mcp-forge — turn any API into a Claude-callable tool surface
When to use this skill
Trigger when the user wants Claude (or any MCP client) to be able to invoke a remote API as native tools. Strong signals:
- Mentions of "MCP server", "MCP for X", "wrap API as MCP", "Model Context Protocol"
- A pasted OpenAPI/Swagger spec, Postman collection, or list of REST endpoints
- A documentation URL where you can read the API surface (Stripe, GitHub, internal services)
Do not trigger for: pure client-side API calls, simple fetch wrappers, or when an existing first-party MCP already covers the surface (check the MCP registry first — see step 1 below).
The output contract
A working MCP server, in a single directory, that:
- Builds clean —
npm install && npm run build succeeds with zero warnings.
- Connects — registers without error against
claude mcp add and answers tools/list.
- Is safe — secrets read from env, never logged; rate limits and 5xx retried with exponential backoff and jitter; PII redacted from error messages.
- Is typed — every tool's
inputSchema is a real Zod schema, not a hand-written JSON blob. Response types are inferred.
- Ships with an install path — README has copy-paste install for Claude Code, plus the bare
claude mcp add command.
Workflow
1 — Reconnaissance (do not skip)
Before writing code:
- Ask the user for the API source. Accept: OpenAPI URL/file, docs URL, raw endpoint list, or a Postman export.
- Search the MCP registry for an existing server (
mcp__mcp-registry__search_mcp_registry if available, otherwise WebSearch "<service> MCP server github"). If a maintained one exists, say so and ask before forking. Don't reinvent.
- Identify the auth model: API key in header, OAuth 2.0, basic auth, signed requests. Each shapes the code differently.
2 — Surface design
Pick the useful subset of endpoints, not all of them. Heuristic:
- Include: any GET that returns data the user would want Claude to read, any POST/PATCH that performs the headline action of the API.
- Exclude: admin endpoints, billing endpoints, bulk-delete endpoints — unless the user explicitly asked.
- Name tools in the form
<verb>_<noun> (list_customers, create_invoice). Never expose the HTTP verb or URL — those are implementation details.
For each tool, decide:
- Required vs optional inputs
- Whether the response needs pagination handling baked in
- Whether the response needs trimming (e.g. strip 80% of fields the model doesn't need to reason over)
3 — Scaffold
Generate this layout:
<service>-mcp/
package.json
tsconfig.json
src/
index.ts # entry — wires StdioServerTransport
server.ts # registers tools, holds the client
client.ts # the typed HTTP client (auth, retries, error mapping)
tools/
<one file per tool>.ts
schemas/
<zod schemas grouped by resource>.ts
errors.ts # MCPError subclasses
README.md
.env.example
Use @modelcontextprotocol/sdk for transport and tool registration. Use zod for schemas. Use undici (Node 20+) or native fetch for HTTP — never axios unless the API requires its interceptor model.
4 — Auth, retries, redaction
- Auth: read from
process.env.<SERVICE>_API_KEY (or OAuth equivalent). If env is missing, fail at startup with a clear message naming the variable.
- Retries: 3 attempts on 5xx and 429. Exponential backoff: 500ms, 1500ms, 4500ms, plus 0–500ms jitter. Respect
Retry-After if present.
- Redaction: build an
errorMap that strips tokens, emails, and any header values from error responses before they bubble up as MCPError messages.
- Timeouts: 30s default per request, configurable via
<SERVICE>_TIMEOUT_MS.
5 — Test the loop
Before declaring done:
npm run build clean.
node dist/index.js starts without crash.
- Add the server locally:
claude mcp add <name> -- node /full/path/dist/index.js
- In a Claude session, ask: "list tools from " — verify the tool list matches the design.
- Invoke one read tool and one write tool end-to-end against a test account.
6 — README
Must contain:
- One-paragraph summary of what the server exposes
- Required env vars (with where to get the API key)
- Install block (copy-paste) for Claude Code
- The full tool list with one-line descriptions
- Known limitations (rate limits, unsupported endpoints, sandbox vs prod)
Patterns and anti-patterns
✅ Do:
- Pin the SDK version in
package.json — MCP is evolving fast.
- Stream paginated results; expose a
cursor input rather than auto-following all pages (the model decides when to stop).
- Return concise tool responses. Trim arrays to the first N items by default and add a
limit input.
- Catch and re-throw with
MCPError so the client gets actionable messages.
❌ Don't:
- Don't expose every endpoint. The model gets confused when a tool list has 80 entries.
- Don't put API keys in
inputSchema. They live in env, not in tool arguments.
- Don't
console.log the full response — it pollutes the JSON-RPC stream over stdio and breaks the transport.
- Don't catch errors silently. A swallowed 401 looks like a successful empty response to the model.
Example invocation
User: "Wrap the Linear API as an MCP server so I can use it from Claude Code."
- Confirm the auth model (Linear uses an API key) and the priority surface (issues, projects, comments).
- Scaffold
linear-mcp/ with tools: list_issues, get_issue, create_issue, update_issue, list_projects, comment_on_issue.
- Wire the client with
LINEAR_API_KEY env, GraphQL POST against api.linear.app/graphql, error mapping for the standard Linear error envelope.
- Generate Zod schemas from the issue, project, comment types.
- Verify locally, write the README, hand the user the
claude mcp add linear -- node ... command.
See also
api-architect skill — for when you need to design the API before wrapping it
security-sentinel — sweep the generated server for leaked secrets and unsafe defaults
doc-craft — polish the README before sharing
1---2name: mcp-forge3description: Scaffold a production-ready Model Context Protocol (MCP) server from an OpenAPI spec, API reference URL, or pasted endpoint list. Use when the user says "build an MCP server", "wrap this API as MCP", "expose this service to Claude", "create MCP for <service>", or pastes a swagger/OpenAPI JSON and asks Claude to make it callable as tools. Produces a typed TypeScript server, auth handling, retry/backoff, and a one-command install path.4---56# mcp-forge — turn any API into a Claude-callable tool surface78## When to use this skill910Trigger when the user wants Claude (or any MCP client) to be able to *invoke* a remote API as native tools. Strong signals:1112- Mentions of "MCP server", "MCP for X", "wrap API as MCP", "Model Context Protocol"13- A pasted OpenAPI/Swagger spec, Postman collection, or list of REST endpoints14- A documentation URL where you can read the API surface (Stripe, GitHub, internal services)1516Do *not* trigger for: pure client-side API calls, simple `fetch` wrappers, or when an existing first-party MCP already covers the surface (check the MCP registry first — see step 1 below).1718## The output contract1920A working MCP server, in a single directory, that:21221. **Builds clean** — `npm install && npm run build` succeeds with zero warnings.232. **Connects** — registers without error against `claude mcp add` and answers `tools/list`.243. **Is safe** — secrets read from env, never logged; rate limits and 5xx retried with exponential backoff and jitter; PII redacted from error messages.254. **Is typed** — every tool's `inputSchema` is a real Zod schema, not a hand-written JSON blob. Response types are inferred.265. **Ships with an install path** — README has copy-paste install for Claude Code, plus the bare `claude mcp add` command.2728## Workflow2930### 1 — Reconnaissance (do not skip)3132Before writing code:3334- Ask the user for the API source. Accept: OpenAPI URL/file, docs URL, raw endpoint list, or a Postman export.35- Search the MCP registry for an existing server (`mcp__mcp-registry__search_mcp_registry` if available, otherwise WebSearch `"<service> MCP server github"`). If a maintained one exists, *say so* and ask before forking. Don't reinvent.36- Identify the auth model: API key in header, OAuth 2.0, basic auth, signed requests. Each shapes the code differently.3738### 2 — Surface design3940Pick the *useful* subset of endpoints, not all of them. Heuristic:4142- **Include**: any GET that returns data the user would want Claude to read, any POST/PATCH that performs the headline action of the API.43- **Exclude**: admin endpoints, billing endpoints, bulk-delete endpoints — unless the user explicitly asked.44- Name tools in the form `<verb>_<noun>` (`list_customers`, `create_invoice`). Never expose the HTTP verb or URL — those are implementation details.4546For each tool, decide:47- Required vs optional inputs48- Whether the response needs pagination handling baked in49- Whether the response needs trimming (e.g. strip 80% of fields the model doesn't need to reason over)5051### 3 — Scaffold5253Generate this layout:5455```56<service>-mcp/57 package.json58 tsconfig.json59 src/60 index.ts # entry — wires StdioServerTransport61 server.ts # registers tools, holds the client62 client.ts # the typed HTTP client (auth, retries, error mapping)63 tools/64 <one file per tool>.ts65 schemas/66 <zod schemas grouped by resource>.ts67 errors.ts # MCPError subclasses68 README.md69 .env.example70```7172Use `@modelcontextprotocol/sdk` for transport and tool registration. Use `zod` for schemas. Use `undici` (Node 20+) or native `fetch` for HTTP — never `axios` unless the API requires its interceptor model.7374### 4 — Auth, retries, redaction7576- **Auth**: read from `process.env.<SERVICE>_API_KEY` (or OAuth equivalent). If env is missing, fail at startup with a clear message naming the variable.77- **Retries**: 3 attempts on 5xx and 429. Exponential backoff: 500ms, 1500ms, 4500ms, plus 0–500ms jitter. Respect `Retry-After` if present.78- **Redaction**: build an `errorMap` that strips tokens, emails, and any header values from error responses before they bubble up as MCPError messages.79- **Timeouts**: 30s default per request, configurable via `<SERVICE>_TIMEOUT_MS`.8081### 5 — Test the loop8283Before declaring done:84851. `npm run build` clean.862. `node dist/index.js` starts without crash.873. Add the server locally: `claude mcp add <name> -- node /full/path/dist/index.js`884. In a Claude session, ask: "list tools from <name>" — verify the tool list matches the design.895. Invoke one read tool and one write tool end-to-end against a test account.9091### 6 — README9293Must contain:94- One-paragraph summary of what the server exposes95- Required env vars (with where to get the API key)96- Install block (copy-paste) for Claude Code97- The full tool list with one-line descriptions98- Known limitations (rate limits, unsupported endpoints, sandbox vs prod)99100## Patterns and anti-patterns101102✅ **Do**:103- Pin the SDK version in `package.json` — MCP is evolving fast.104- Stream paginated results; expose a `cursor` input rather than auto-following all pages (the model decides when to stop).105- Return concise tool responses. Trim arrays to the first N items by default and add a `limit` input.106- Catch and re-throw with `MCPError` so the client gets actionable messages.107108❌ **Don't**:109- Don't expose every endpoint. The model gets confused when a tool list has 80 entries.110- Don't put API keys in `inputSchema`. They live in env, not in tool arguments.111- Don't `console.log` the full response — it pollutes the JSON-RPC stream over stdio and breaks the transport.112- Don't catch errors silently. A swallowed 401 looks like a successful empty response to the model.113114## Example invocation115116> User: "Wrap the Linear API as an MCP server so I can use it from Claude Code."1171181. Confirm the auth model (Linear uses an API key) and the priority surface (issues, projects, comments).1192. Scaffold `linear-mcp/` with tools: `list_issues`, `get_issue`, `create_issue`, `update_issue`, `list_projects`, `comment_on_issue`.1203. Wire the client with `LINEAR_API_KEY` env, GraphQL POST against `api.linear.app/graphql`, error mapping for the standard Linear error envelope.1214. Generate Zod schemas from the issue, project, comment types.1225. Verify locally, write the README, hand the user the `claude mcp add linear -- node ...` command.123124## See also125126- `api-architect` skill — for when you need to *design* the API before wrapping it127- `security-sentinel` — sweep the generated server for leaked secrets and unsafe defaults128- `doc-craft` — polish the README before sharing