Command Palette

Search for a command to run...

UnylyUnyly
Browse all

Achmadya Dev/ Core

FreeNot checked

Provides a shared MCP SDK wrapper for building MCP servers with stdio transport, tool registration, JSON-safe responses, and environment helpers.

GitHubEmbed

About

Provides a shared MCP SDK wrapper for building MCP servers with stdio transport, tool registration, JSON-safe responses, and environment helpers.

README

Shared MCP SDK wrapper for @achmadya-dev servers: stdio and Streamable HTTP transport, tool registration, JSON-safe responses, and env helpers.

Built on MCP TypeScript SDK v2 (@modelcontextprotocol/server). Tool schemas use Standard Schema — pick any compatible library in your package.

Install

pnpm add @achmadya-dev/mcp-core
# plus a Standard Schema library in your MCP package, e.g. zod, valibot, arktype, …

Installed automatically as a dependency of @achmadya-dev/mcp-*-query servers.

Quick start (stdio)

Pass a Standard Schema object to inputSchema / outputSchema. Examples below use Zod; other libraries work the same way.

pnpm add zod
import * as z from "zod";
import { defineTool, runMcp } from "@achmadya-dev/mcp-core";

const myTool = defineTool({
  name: "my_tool",
  description: "Does something",
  inputSchema: z.object({
    name: z.string().describe("Item name"),
  }),
  outputSchema: z.object({ ok: z.boolean() }),
  handler: async ({ name }) => ({ ok: true }),
});

await runMcp({ name: "My MCP", version: "1.0.0", tools: [myTool] });

Configure in Cursor (stdio — client spawns the process):

{
  "mcpServers": {
    "my-mcp": {
      "command": "npx",
      "args": ["-y", "@achmadya-dev/mcp-my-service"]
    }
  }
}

Architecture

One runMcp() call = one MCP instance (one process, one transport).

flowchart TB
  subgraph runMcp["runMcp(options)"]
    tools["tools: defineTool[]"]
    setup["setup(server)"]
    http["http.setupRoutes(app)"]
  end

  tools --> bindTools["bindTools → server.registerTool + handler wrap"]
  setup --> sdk["SDK native: MCP Apps, resources, custom _meta"]
  http --> express["Express routes: /health, /api/…"]

  bindTools --> mcp["MCP protocol /mcp"]
  sdk --> mcp

  transport{{"transport?"}}
  transport -->|stdio| pipe["stdin/stdout — Cursor spawns process"]
  transport -->|http| port["HTTP :PORT/mcp — remote + MCP Apps"]
Hook Use for
tools Standard tools from defineTool (auto-wrap JSON, ToolErrorfail)
setup(server) SDK-native MCP features (registerAppTool, resources, custom _meta)
http.setupRoutes(app) Extra HTTP endpoints on the same port (health, webhooks)

Need both stdio and HTTP? Run two separate instances (two processes) — not one runMcp with dual transport.

HTTP transport

Same tools and config — set transport: "http" or env TRANSPORT=http:

await runMcp({
  name: "My MCP",
  version: "1.0.0",
  tools: [myTool],
  transport: "http",
  http: {
    port: 3001,
    path: "/mcp",
    setupRoutes: (app) => {
      app.get("/health", (_req, res) => res.json({ ok: true }));
    },
  },
});

Or rely on env (default port 3001 via PORT):

TRANSPORT=http PORT=3001 node dist/index.js

Configure in Cursor (remote HTTP):

{
  "mcpServers": {
    "my-mcp": {
      "url": "http://127.0.0.1:3001/mcp"
    }
  }
}

Environment variables

Variable Default Description
TRANSPORT stdio stdio or http when runMcp has no explicit transport
PORT 3001 HTTP listen port (when transport=http)

runMcp reads TRANSPORT and PORT internally when not set in options. For tool-specific config in your package, use envStr, envInt, envBool, envTrans.

Schema libraries

Valibot

pnpm add valibot @valibot/to-json-schema
import * as v from "valibot";
import { toStandardJsonSchema } from "@valibot/to-json-schema";
import { defineTool, runMcp } from "@achmadya-dev/mcp-core";

const myTool = defineTool({
  name: "my_tool",
  description: "Does something",
  inputSchema: toStandardJsonSchema(
    v.object({ name: v.pipe(v.string(), v.description("Item name")) })
  ),
  outputSchema: toStandardJsonSchema(v.object({ ok: v.boolean() })),
  handler: async ({ name }) => ({ ok: true }),
});

await runMcp({ name: "My MCP", version: "1.0.0", tools: [myTool] });

ArkType

pnpm add arktype
import { type } from "arktype";
import { defineTool } from "@achmadya-dev/mcp-core";

defineTool({
  name: "my_tool",
  description: "Does something",
  inputSchema: type({ name: "string" }),
  outputSchema: type({ ok: "boolean" }),
  handler: async ({ name }) => ({ ok: true }),
});

Raw JSON Schema

import { fromJsonSchema } from "@modelcontextprotocol/server";
import { defineTool } from "@achmadya-dev/mcp-core";

defineTool({
  name: "my_tool",
  description: "Does something",
  inputSchema: fromJsonSchema({
    type: "object",
    properties: { name: { type: "string", description: "Item name" } },
    required: ["name"],
  }),
  handler: async ({ name }) => ({ ok: true }),
});

Tools with no parameters: use an empty object schema from your library (e.g. z.object({}), toStandardJsonSchema(v.object({})), type({})).

Advanced: resources, prompts, MCP Apps

Use setup to register SDK features beyond tools — parameter server is already typed via McpSetupHook, no SDK import needed for typical cases.

Resources & prompts

import * as z from "zod";
import { defineTool, runMcp } from "@achmadya-dev/mcp-core";

await runMcp({
  name: "My MCP",
  version: "1.0.0",
  tools: [myTool],
  setup(server) {
    server.registerResource(
      "config",
      "config://app",
      { title: "App Config", mimeType: "application/json" },
      async (uri) => ({
        contents: [{ uri: uri.href, text: JSON.stringify({ ok: true }) }],
      }),
    );

    server.registerPrompt(
      "review",
      {
        title: "Code Review",
        argsSchema: z.object({ code: z.string() }),
      },
      ({ code }) => ({
        messages: [{ role: "user", content: { type: "text", text: `Review:\n${code}` } }],
      }),
    );
  },
});

createMcpApp + await app.createServer() works the same way for manual SDK wiring.

Tool results

MCP tools respond with a CallToolResult (content array + optional structuredContent / isError). With defineTool, return plain JSON (auto-wrapped), the helpers below, or throw ToolErrorfail().

ok(data) — success

Object values become formatted JSON text and structuredContent. Strings become a single text block.

import { defineTool, ok } from "@achmadya-dev/mcp-core";

defineTool({
  name: "query",
  description: "Run SQL",
  inputSchema: z.object({ sql: z.string() }),
  handler: async ({ sql }) => {
    const rows = await db.query(sql);
    return ok({ rows, count: rows.length });
  },
});

return ok("Migration applied.");

Equivalent to returning { rows, count } directly from the handler (auto-wrapped).

fail(msg) — expected error

Sets isError: true. Same outcome as throw new ToolError(msg).

import { defineTool, fail } from "@achmadya-dev/mcp-core";

if (!sql.trim()) return fail("SQL is required");
return fail(new Error("Connection refused"));

content(blocks) — custom blocks

Multiple content blocks (text, image, …). Optional structured metadata or isError: true.

import { defineTool, content } from "@achmadya-dev/mcp-core";

defineTool({
  name: "screenshot",
  description: "Capture screen",
  inputSchema: z.object({}),
  handler: async () => {
    const base64 = await captureScreen();
    return content(
      [
        { type: "text", text: "Screenshot:" },
        { type: "image", data: base64, mimeType: "image/png" },
      ],
      { structured: { capturedAt: new Date().toISOString() } },
    );
  },
});

return content([{ type: "text", text: "Invalid input" }], { isError: true });
Helper When to use
ok(data) Success — object → JSON + structuredContent, string → text
fail(msg) Expected error; sets isError: true
content(blocks) Multiple blocks (text, image, …)

Exports

  • runMcp, createMcpApp, McpApp
  • defineTool, ToolError
  • ok, fail, content, isCalled — tool result helpers
  • envStr, envInt, envBool, envTrans
  • @achmadya-dev/mcp-core/ext-appsregisterAppResource, registerAppTool, RESOURCE_MIME_TYPE (requires peer @modelcontextprotocol/ext-apps)
  • Types: RunMcpOptions, McpSetupHook, McpTransport, HttpTransportOptions, McpAppConfig, …

Schema builders and runtime validation stay in each @achmadya-dev/mcp-* package.

Development

pnpm install
pnpm run build
pnpm test        # 22 unit tests (also runs on pre-commit)
pnpm changeset   # before shipping user-facing changes

Release

Uses Changesets — same flow as achmadya-dev/mcp.

  1. Add a changeset when you ship user-facing changes:

    pnpm changeset
    
  2. Push to main. GitHub Actions opens a Version packages PR (version bump + CHANGELOG.md).

  3. Merge that PR. Next push to main publishes to npm (@achmadya-dev/mcp-core).

Remote prerequisites: GitHub secret NPM_TOKEN (npm automation token with bypass 2FA for @achmadya-dev).

from github.com/achmadya-dev/mcp-core

Installing Achmadya Dev/ Core

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/achmadya-dev/mcp-core

FAQ

Is Achmadya Dev/ Core MCP free?

Yes, Achmadya Dev/ Core MCP is free — one-click install via Unyly at no cost.

Does Achmadya Dev/ Core need an API key?

No, Achmadya Dev/ Core runs without API keys or environment variables.

Is Achmadya Dev/ Core hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install Achmadya Dev/ Core in Claude Desktop, Claude Code or Cursor?

Open Achmadya Dev/ Core on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare Achmadya Dev/ Core with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All development MCPs