# MCP Forge

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

- Skill: `ak-ship/mcp-forge` (Agent Skill)
- Install (CLI): `npx skillmds@latest add ak-ship/mcp-forge`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ak-ship/mcp-forge/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: ak-ship (https://skillmd.com/u/ak-ship)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ak-ship/mcp-forge

---


# 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:

1. **Builds clean** — `npm install && npm run build` succeeds with zero warnings.
2. **Connects** — registers without error against `claude mcp add` and answers `tools/list`.
3. **Is safe** — secrets read from env, never logged; rate limits and 5xx retried with exponential backoff and jitter; PII redacted from error messages.
4. **Is typed** — every tool's `inputSchema` is a real Zod schema, not a hand-written JSON blob. Response types are inferred.
5. **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:

1. `npm run build` clean.
2. `node dist/index.js` starts without crash.
3. Add the server locally: `claude mcp add <name> -- node /full/path/dist/index.js`
4. In a Claude session, ask: "list tools from <name>" — verify the tool list matches the design.
5. 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."

1. Confirm the auth model (Linear uses an API key) and the priority surface (issues, projects, comments).
2. Scaffold `linear-mcp/` with tools: `list_issues`, `get_issue`, `create_issue`, `update_issue`, `list_projects`, `comment_on_issue`.
3. Wire the client with `LINEAR_API_KEY` env, GraphQL POST against `api.linear.app/graphql`, error mapping for the standard Linear error envelope.
4. Generate Zod schemas from the issue, project, comment types.
5. 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

