npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@achmadya-dev/mcp-core

v0.8.0

Published

Shared MCP server runtime, types, and env helpers for @achmadya-dev MCP packages

Readme

@achmadya-dev/mcp-core

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).