Platform
6 min read
MCP Server guide
StateAnchor generates a fully compliant Model Context Protocol (MCP) server from your stateanchor.yaml spec. This lets AI agents like Claude, Cursor, and ChatGPT interact with your API directly, without writing glue code. The generated server exposes every endpoint in your spec as a discoverable tool with full parameter schemas, error handling, and authentication support.
What is MCP?
Model Context Protocol is an open standard created by Anthropic that lets AI assistants discover and call external tools at runtime. Instead of pasting API documentation into a prompt, you run an MCP server that the AI model connects to. The model sees the available tools, their parameter schemas, and their descriptions, then decides when and how to call them during a conversation.
MCP uses a JSON-RPC transport over stdio (for local servers) or HTTP with Server-Sent Events (for remote servers). StateAnchor generates stdio-based servers by default, which is the format Claude Desktop, Cursor, Windsurf, and the MCP CLI all support.
How StateAnchor generates MCP servers
When your stateanchor.yaml includes mcp: true in the outputs section, every sync generates an MCP server alongside your SDK artifacts. The generation pipeline works as follows:
- StateAnchor reads your spec and builds the canonical Intermediate Representation (IR).
- The IR's endpoints, models, and auth configuration are passed to the MCP generation prompt.
- Claude generates a complete TypeScript MCP server implementing every endpoint as a tool.
- The generated server is validated against Anthropic's Software Directory Policy (tool name length, annotations, error handling).
- The server is content-addressed (SHA-256) and stored as an artifact tied to the sync run.
Enabling MCP in your spec
# stateanchor.yaml
service: my-api
version: "0.1.0"
server:
base_url: https://api.example.com
auth:
type: bearer
endpoints:
- name: list_users
method: GET
path: /users
returns: User[]
- name: create_user
method: POST
path: /users
body: CreateUser
returns: User
outputs:
languages:
- typescript
- python
docs: true
mcp: true # ← enables MCP server generationWhat the MCP server exposes
Every endpoint in your spec becomes a tool. The tool name is derived from the endpoint's namefield. The tool's inputSchemais built from the endpoint's path parameters, query parameters, and request body. The tool's description comes from the endpoint's description field or is inferred from its name and method.
Tool mapping
For the spec above, the generated server exposes these tools:
| Tool name | Method | Parameters | Returns |
|---|---|---|---|
list_users | GET /users | None (or optional query filters) | Array of User objects as JSON text |
create_user | POST /users | name (string, required), email (string, required) | Created User object as JSON text |
Tool annotations
Each tool includes Anthropic Software Directory annotations that help the AI model understand the tool's behavior:
- readOnlyHint --
truefor GET endpoints,falsefor POST/PUT/DELETE. - destructiveHint --
truefor DELETE endpoints,falseotherwise. - title -- human-readable name derived from the endpoint name.
Error handling
Every tool handler is wrapped in a try/catch block. If the API call fails, the tool returns an error response with isError: true and a descriptive message. This lets the AI model understand what went wrong and decide whether to retry, try a different approach, or report the error to the user.
// Generated error handling pattern
server.tool("list_users", "List all users", {
annotations: { readOnlyHint: true, destructiveHint: false, title: "List users" }
}, async () => {
try {
const res = await fetch(`${BASE_URL}/users`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
const data = await res.json();
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
} catch (err) {
return {
content: [{ type: "text", text: `Error: ${err.message}` }],
isError: true,
};
}
});Authentication
Credentials are never hardcoded. The generated server reads authentication from environment variables. For bearer auth, it expects an API_KEY or API_TOKEN environment variable. The variable name is derived from your service name: a service called my-api producesMY_API_API_KEY.
Connecting to Claude Desktop
Claude Desktop reads MCP server configurations from a JSON file. Add your generated server to this file and restart Claude Desktop.
// macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
// Windows: %APPDATA%\Claude\claude_desktop_config.json
// Linux: ~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"my-api": {
"command": "node",
"args": ["./mcp-server.js"],
"env": {
"MY_API_API_KEY": "your-api-key-here"
}
}
}
}After saving and restarting Claude Desktop, your API tools appear in the tools panel (hammer icon). Claude can now call your API tools when answering questions that require data from your service.
Connecting to Cursor
In Cursor, open Settings → MCP and add:
{
"mcpServers": {
"my-api": {
"command": "node",
"args": ["./mcp-server.js"],
"env": { "MY_API_API_KEY": "your-api-key-here" }
}
}
}Connecting to Claude Code (CLI)
Claude Code discovers MCP servers from .mcp.json in your project root or from~/.claude/mcp.json globally:
// .mcp.json in your project root
{
"mcpServers": {
"my-api": {
"command": "node",
"args": ["./mcp-server.js"],
"env": { "MY_API_API_KEY": "your-api-key-here" }
}
}
}Connecting via the MCP CLI
For testing or debugging, you can use the MCP Inspector:
npx @anthropic-ai/mcp-inspector node ./mcp-server.js
This opens a browser-based interface where you can see all registered tools, send test requests, and inspect the JSON-RPC messages.
MCP policy validation
StateAnchor validates every generated MCP server against Anthropic's Software Directory Policy before storing it as an artifact. The validation checks:
- Tool name length -- must not exceed 64 characters (ERR if violated).
- Annotations --
readOnlyHint,destructiveHint, andtitleshould be present (WARN if missing). - Error handling -- every tool handler must have try/catch (ERR if missing).
- Description length -- tool descriptions over 500 characters produce a WARN for token frugality.
- Auth pattern -- hardcoded tokens produce an ERR; auth without OAuth produces a WARN.
Validation findings are stored on the sync run as mcp_policy_findings and are visible in the project detail page.
Versioning and provenance
Every generated MCP server is content-addressed with SHA-256. If your spec does not change between syncs, the MCP server artifact hash remains identical -- the server is not unnecessarily regenerated. When the spec does change, the new server is generated, validated, and stored alongside the previous version. You can download any version from the project detail page.
Tip: The generated server includes a comment header with the StateAnchor sync run ID and IR hash, so you can always trace a deployed MCP server back to the exact spec version that produced it.