mcp-gen

by zodromon

Not rated
GitHub

About

Turn your typed TypeScript functions into an MCP server; tool, resource, and prompt schemas are inferred from your types and JSDoc. No schema library, no decorators, no boilerplate.

Details

Author
zodromon
Categories
Developer Tools, Automation

Setup

Install mcp-gen in your MCP client (Claude Desktop, Cursor, Windsurf, and others).

Repository: https://github.com/zodromon/mcp-gen

Follow the installation instructions in the repository README, then restart your MCP client.

Turn your typed TypeScript functions into an MCP server. No schema library, no decorators, no boilerplate — the schema is inferred from your types.

mcp-genreads exported TypeScript functions and generatesModel Context Protocoltool, resource, and promptdefinitions from them — using the TypeScript type checker (viats-morph) to turn each parameter type into a JSON Schema and each JSDoc comment into a description. It can emit the schemas as JSON, serve a live MCP server, or open alive playgroundwhere you call your tools in a browser while you edit.

If your functions are typed, they're already MCP tools.

Write plain, typed functions with ordinary JSDoc:

// tools.ts /  Greets a person by name and age.  @param name - The person's name  @param age - The person's age in years / export function greet(name: string, age: number): string { return Hello ${name}, age ${age}; } /  Echoes a message back after a tick.  @param msg - The message to echo back / export async function slowEcho(msg: string): Promise<string> { return echo: ${msg}; }
{ "tools": [ { "name": "greet", "description": "Greets a person by name and age.", "inputSchema": { "type": "object", "properties": { "name": { "type": "string", "description": "The person's name" }, "age": { "type": "number", "description": "The person's age in years" } }, "required": ["name", "age"] } } // ... slowEcho ] }

The fastest way to actually try them — thelive playground. It watches your file, renders an input form for each tool from the inferred schema, and runs it right in your browser while you edit:

mcp-gen dev tools.ts # then open the printed http://127.0.0.1:4000/ URL

servebinds127.0.0.1(loopback) by default— it executes your local code when its tools are called, so it isn't reachable off-machine unless you ask. To expose it on the network, opt in explicitly with--host(e.g.mcp-gen serve tools.ts --host 0.0.0.0); doing so prints a warning, since the endpoint is then reachable by other machines.

Before exposing it, turn onbearer-token authentication. Configure one or more keys with--api-key <key>(repeatable) or the comma-separatedMCP_GEN_API_KEYSenvironment variable — if any key is set, auth isonand every request to/mcpmust carryAuthorization: Bearer <key>(a missing, malformed, or wrong key is rejected with401before any tool runs). With no keys set, auth is off and behavior is unchanged. When auth is on, the off-machine "no authentication" warning is replaced by a one-line confirmation that a token is required. SeeDeploying to production.

That's it. The parameter names, types, required-ness, and descriptions all come from the code you already wrote.

- Types become schemas.Each exported function's parameters are converted to a JSON SchemainputSchemaby the TypeScript type checker. Optional params (age?: number) are omitted fromrequired; return types are captured (andPromise<T>is unwrapped).
- JSDoc becomes documentation.The function's summary becomes the tooldescription; each@parambecomes that property'sdescription.
- Fail-loud, never fail-silent.A function that can't be converted to a valid schema — for example an unbound generic in input position (identity<T>(value: T)) — isexcluded and reported, never emitted as something broken. Clean functions are still generated; the exit code tells you if any failed (see below).

It's built to work on real codebases, not just single-file toys:

- generics (constrained and unbound), and a clear error on the ones that can't be represented
- types imported from other modules and re-exported under aliases
- multiple export styles (named, default, aliased)
- tsconfig.jsondiscovery and path-alias resolution
- detection of non-serializable types (instead of emitting invalid schema)
- asyncfunctions (awaited return types); a throwing tool returnsisErrorrather than taking the server down

MCP servers can expose three kinds of things —tools(actions),resources(data), andprompts(reusable templates).mcp-geninfers all three from the same typed exports; a single JSDoc tag picks which one.An untagged function is a tool, exactly as before — nothing changes for existing code.

Tag a function with@resourceand a URI. If the URI has{placeholders}that match parameter names, it becomes aresource template(the params validate the URL); with no placeholders it's astatic resource. The return value is the content — astringis served astext/plain, anything else asapplication/json. Add@mime <type>to override.

/  Read a user record by id.  @resource users://{id} — templated: {id} matches the id param  @param id - The user id / export function getUser(id: string): { id: string; name: string } { return { id, name: User ${id} }; } /  The current app configuration.  @resource config://app — static: no placeholders / export function appConfig() { return { theme: "dark", version: "1.1.0" }; } /  @resource info://build  @mime text/plain — override the content type / export function buildInfo(): string { return "mcp-gen build 1.1.0"; }

Tag a function with@prompt. Its parameters become the prompt's arguments (names and descriptions from@param). Return astringfor a single user message, or an array of{ role, content }messages to pass them through as-is.

/  A code-review prompt.  @prompt  @param language - The programming language  @param code - The code to review / export function reviewPrompt(language: string, code: string): string { return Please review this ${language} code:\n\n${code}; }

Runmcp-gen serveand a connected MCP client can list and read your resources and get your prompts, alongside calling tools. Resources and prompts go through theexact same pathas tools — same type inference, same validation, same execution — and the same fail-loud rule applies: e.g. a@resourcetemplate whose{var}doesn't match a parameter is excluded and reported, never half-registered.

mcp-gen <file.ts> [--debug] [--tsconfig <path>] Generate tool schemas as JSON (stdout) mcp-gen serve <file.ts> [--port N] [--host <addr>] [--api-key <key>]... [--tsconfig <path>] Start a live MCP server (default port 3000; binds 127.0.0.1; PORT/HOST env honored) mcp-gen dev <file.ts> [--port N] [--tsconfig <path>] Live playground UI, watches + reloads (default port 4000) mcp-gen check <file.ts> [--update] [--snapshot <path>] [--tsconfig <path>] Guard the tool surface against breaking changes (CI)

Generation writes machine-readable JSON tostdout(always includestools; includeserrors/warningswhen present); human-readable messages go tostderr. Exit codes:

To runserveas a network-reachable MCP server, bind a public interfaceandrequire a bearer token. Pass the keys through the environment (not--api-key) so the secret never lands in shell history or a process listing:

# one or more comma-separated keys; every caller must send Authorization: Bearer <key> MCP_GEN_API_KEYS="$(openssl rand -hex 32)" mcp-gen serve tools.ts --host 0.0.0.0

- Keys via env in prod, not the CLI.--api-keyis convenient for local testing, but a flag is visible to anyone who can list processes (ps);MCP_GEN_API_KEYSkeeps the secret out ofargv. Both sources are unioned, comma-split, and trimmed, and any non-empty key turns auth on.
- PORT/HOSTare honored.When you omit--port/--host,servefalls back to thePORTandHOSTenvironment variables before its defaults (3000/127.0.0.1) — so it drops straight into a platform that injectsPORT. An explicit--port/--hostflag always overrides the env var.
- Auth is fail-closed.With keys configured, a request to/mcpcarrying a missing, malformed, or non-matching token is rejected with401 {"error":"unauthorized"}beforethe MCP transport — so no tool, resource, or prompt code runs. Keys are compared in constant time.
- No keys = exposed and unauthenticated.Bind a non-loopback--host(orHOST) without any key configured andservekeeps printing the loud exposure warning. Configure a key to gate the endpoint (and silence the warning).

On a platform that injectsPORT(and optionallyHOST), you supply only the keys:

MCP_GEN_API_KEYS="key-a,key-b" mcp-gen serve tools.ts --host 0.0.0.0 # PORT taken from the environment

checkistscfor your tool surface: it snapshots the generated tools to a committed file and, on later runs, fails the build onbreakingchanges — so an agent-facing tool can't silently change shape under its callers.

mcp-gen check tools.ts --update # write the baseline (the jest -u of tool contracts) — commit it mcp-gen check tools.ts # in CI: fail if the surface broke

Breaking changes (exit1) are judged from the perspective of an existing caller: a tool removed or renamed, a property removed, anew requiredparameter, an optional param made required, a type change, an enum value removed, or any other narrowing of a nested sub-schema. Purely additive or loosening changes — a new tool, a newoptionalparam, a relaxed requirement, a new enum value — aresafeand never fail. Description and return-type changes are reported asnotices.

The snapshot is deterministically normalized (tools and keys sorted, stable formatting), so it stays byte-stable and reviews cleanly in a PR. Defaults to<file>.mcp-snapshot.json; override with--snapshot.

devis a type-aware playground for the server a file defines. It watches the file, regenerates the tool surface on every save, and serves a tinylocalhostweb UI:

mcp-gen dev tools.ts # then open the printed URL, e.g. http://127.0.0.1:4000/

Open the printed URL in a browser. For each tool it renders an input form from the inferred schema (string → text, number → number, boolean → checkbox, enum → dropdown, arrays/objects → a raw-JSON box), runs the tool on demand, and shows theresult, thegeneratedinputSchema, and theraw JSON-RPCrequest/response — the inspector view. Fail-loud excluded functions are listed greyed out with their reasons. Save the file and the page reloads itself, preserving what you'd typed.

Crucially, the playground runs each tool through theexact same pathasmcp-gen serve— the same module loader and the same named→positional dispatch — so what you see in the browser is what the served server does. It binds127.0.0.1only (it executes your local code on request, so it is never exposed off-machine), defaults to port4000, and follows the same exit-code discipline asserve.

Commit the*.mcp-snapshot.jsonfile— it's the baseline every latercheckcompares against, not build output. Don't add it to.gitignore; check it in alongside your code so a PR's diff shows exactly how the tool surface changed.

The package is scoped (@zodromon/mcp-gen), but the command you run is justmcp-gen:

git clone https://github.com/zodromon/mcp-gen && cd mcp-gen npm install npm run build # → dist/ node dist/generate-mcp-schemas.js tools.ts

During development you can run it directly without building:

Requires Node.js. Dependencies:@modelcontextprotocol/sdk,ts-morph,typescript,jiti.

Good for:quickly exposing existing typed functions as MCP tools — internal tools, prototypes, anything where you'd rather not hand-write tool schemas.

Not trying to bethe biggest MCP framework. It does one thing. If you want decorators, a plugin system, or a managed platform, other good tools fit better:

- FastMCP— mature and popular; you declare params via a schema library (Zod/ArkType/Valibot).
-
simply-mcp-ts— decorator / functional / programmatic APIs.
-
The official MCP SDK— maximal control, more boilerplate.

mcp-gen's only real difference is taste:nothing is added to your functions— no schema library, no annotations beyond the JSDoc you'd write anyway. If that appeals, use it. If not, the others are great.

This is a web browser that enables your coding agent, such as Claude Code, to visit websites on your behalf and assist you in identifying bugs or creating UI test cases.

Execute any LLM-generated code in the YepCode secure and scalable sandbox environment and create your own MCP tools using JavaScript or Python, with full support for NPM and PyPI packages

Agent-native developer Q&A API with MCP + A2A endpoints for citations, job pickup, and answer submission.

MCP bridge that lets Claude Code delegate heavy tasks to the Antigravity CLI (agy) — purpose-built tools, model routing with fallback, session continuity, and output truncation to save Claude's context and tokens.

Structured .aide spec files that give AI agents progressive disclosure into your codebase architecture. 6 MCP tools, 8 slash commands, TUI wizard, multi-IDE support.

An MCP server for real-time user intervention in AI-assisted development workflows.

Embeds intelligent guidance into AI workflows to organize development and ensure quality.

Hosted MCP server and coordination layer for AI coding agents — live API contracts, database schema, frontend/backend mismatch detection, and shared handoff tickets for Claude Code, Cursor, Codex, and Lovable.

Open-source AI coding stack — bundled MCP servers, agent runtime, and developer tooling for shipping AI-native dev tools.

Local agent workbench bundling OpenHands, Goose, Aider, and ashlrcode against one local LLM, with ashlr-plugin MCP servers pre-wired.

Async Parallel Antigravity for Codex & Claude Code

Run parallel, resumable, human-operable Antigravity CLI sessions from Codex, Claude Code, or any MCP-capable agent harness.

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.