ShipKit docs
The stack

MCP server

Every API route as a tool an assistant can call, generated from the OpenAPI spec.

apps/mcp exposes the API to Model Context Protocol clients — Claude Desktop, Cursor, and anything else speaking MCP. It is generated, not hand-written: the server reads apps/api/openapi.json and turns each operation into a tool with a Zod input schema derived from that operation's parameters and body.

A route added to the API with proper validators becomes an MCP tool on the next build. Nothing to register, nothing to keep in sync.

Two transports

The transport desktop clients use. The server is launched as a subprocess and speaks over stdin and stdout, so nothing listens on a port.

{
  "mcpServers": {
    "shipkit": {
      "command": "bun",
      "args": ["run", "/path/to/shipkit/apps/mcp/src/index.ts"],
      "env": {
        "MCP_API_URL": "http://localhost:3001",
        "MCP_AUTH_HEADER": "Authorization: Bearer sk_..."
      }
    }
  }
}

Streamable HTTP, for clients that connect to a running server rather than spawning one. Useful when the assistant and the API are on different machines.

MCP_API_URL falls back to NEXT_PUBLIC_API_URL, then to http://localhost:3001.

Authentication

The server holds no session of its own. It forwards headers you give it:

MCP_AUTH_HEADER='Authorization: Bearer sk_...'
MCP_AUTH_HEADER='Cookie: better-auth.session_token=...'

# or several
MCP_AUTH_HEADER_1='Authorization: Bearer sk_...'
MCP_AUTH_HEADER_2='X-Org-Id: org_...'

That indirection is what keeps the same binary working against whatever auth the project ends up using. It also means the assistant acts as whoever owns that credential.

An API key handed to an MCP server is a key an assistant can spend. Every tool the spec exposes is callable, including the destructive ones, and the API's own RBAC is the only thing standing between a prompt and a delete.

Issue a dedicated key with the narrowest scope that makes the assistant useful, rather than reusing your own session. Hide routes from the spec with detail: { hide: true } if they should not be reachable this way at all — see OpenAPI.

Keeping the spec current

The tools are only as accurate as apps/api/openapi.json, which is regenerated by the API's prebuild step and committed. A route whose validators are missing produces a tool with no useful input schema, so the assistant guesses at the arguments. Declaring body, query and params on a route is what makes it callable rather than merely present.

On this page