| name | mcp-server-builder |
| description | Scaffolds production-ready MCP servers from OpenAPI specs with schema validation, named tool definitions, and versioning strategy. Use when: "build MCP server from API", "create tool server from OpenAPI", "expose REST API to LLM agent", "scaffold MCP from spec", "convert API contract to tools". |
MCP Server Builder
Scaffolds production-ready MCP servers from OpenAPI specs. Treats the API contract as the source of truth and generates tool manifests + starter code with quality gates before publication.
Philosophy
LLM agents choose tools by reading names and descriptions — not by inspecting code. A poorly named or under-described tool will be ignored or misused no matter how correct the implementation is. This skill treats the OpenAPI spec as authoritative input and imposes quality gates on the generated tool manifest so the output works for agents, not just for humans reading the source.
The discipline here is: validate before you ship. A tool manifest with ambiguous names, missing descriptions, or implicit required fields is technically functional but practically unusable in production agent pipelines.
Description
Converts OpenAPI paths/operations into named MCP tool definitions, generates server scaffolds in Python or TypeScript, enforces naming and description quality, validates manifests against common production failures, and applies a versioning strategy for safe evolution. Distinct from mcp-cloudflare-builder (Cloudflare-specific deployment) and mcp-builder (general MCP concepts) — this skill is spec-driven and language-agnostic.
When to Use
- Exposing an internal or external REST API to LLM agents via MCP
- Replacing brittle browser automation with typed, validated tools
- Sharing one MCP server across multiple teams or assistants
- Bootstrapping from an existing OpenAPI spec rather than hand-writing tool definitions
- Running quality checks before publishing MCP tools to a registry
Workflow
Step 1: Parse the OpenAPI Spec
Read the spec and extract:
- Paths and HTTP methods
operationId (use as canonical tool name when available)
- Request parameters and body schemas
- Response schemas
- Authentication requirements
Step 2: Generate Tool Manifest
For each operation, create an MCP tool definition:
{
"name": "get_user",
"description": "Retrieve a user by ID. Returns full profile including email and role.",
"inputSchema": {
"type": "object",
"properties": {
"user_id": { "type": "string", "description": "The user's UUID" }
},
"required": ["user_id"]
}
}
Naming rules:
- Use
operationId when available (preferred)
- Otherwise derive from method + path:
GET /users/{id} → get_user
- Use snake_case; avoid raw path segments (
get__v1__users___id is wrong)
- One intent per tool; avoid mega-tools combining unrelated operations
Description rules:
- Lead with action verb + object
- Include what the tool returns, not just what it does
- Note any side effects for mutating operations
Step 3: Scaffold Server Code
Generate starter implementation in the chosen language:
Python (FastMCP):
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("billing-mcp")
@mcp.tool()
def get_user(user_id: str) -> dict:
"""Retrieve a user by ID. Returns full profile."""
response = requests.get(f"{BASE_URL}/users/{user_id}", headers=AUTH_HEADERS)
response.raise_for_status()
return response.json()
TypeScript (MCP SDK):
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
const server = new McpServer({ name: "billing-mcp", version: "1.0.0" });
server.tool("get_user", { user_id: z.string() }, async ({ user_id }) => {
const res = await fetch(`${BASE_URL}/users/${user_id}`, { headers: AUTH_HEADERS });
return { content: [{ type: "text", text: await res.text() }] };
});
Step 4: Validate the Manifest
Before integration, check for:
- Duplicate tool names
- Missing or empty descriptions
- Parameters with no types
- Ambiguous schemas (no
required fields on non-trivial inputs)
- Tool names that expose secret values
- Destructive operations without explicit confirmation parameters
Fix all validation errors before proceeding.
Step 5: Apply Auth and Safety Controls
- Store secrets in environment variables, never in tool schemas
- Allowlist outbound hosts explicitly; do not proxy arbitrary user-provided URLs
- Return structured errors:
{ "code": "NOT_FOUND", "message": "...", "details": {...} }
- Add explicit confirmation parameters to destructive operations
- Rate-limit high-cost tools and add request timeouts
Step 6: Versioning Strategy
- Additive fields only for non-breaking updates
- Never rename a tool name in-place — introduce a new tool ID for breaking behavior changes
- Maintain a changelog of tool contract changes per release
- Validate backward compatibility in CI using strict mode
Contract Quality Gates
Before publishing the manifest, confirm:
- Every tool has a verb-first, clear name
- Every description explains intent AND expected result
- Every required field is explicitly typed
- Destructive actions include a confirmation parameter
- Error payload format is consistent across all tools
- Validator returns zero errors in strict mode
Runtime Selection
| Constraint | Choose |
|---|
| Fast iteration, data-heavy backend | Python |
| Shared types with JS stack | TypeScript |
| Single server, simple ops | Python FastMCP |
| Split domain servers with strict contracts | TypeScript + Zod |
Common Pitfalls
- Tool names derived from raw paths:
get__v1__users___id — fix with clean operationId
- Missing operation descriptions — agents choose tools poorly without them
- Ambiguous schemas with no
required fields
- Mixing transport errors and domain errors in one opaque message
- Exposing secret values in tool contracts
- Breaking clients by changing schema keys without versioning
Best Practices
- Use
operationId as the tool name when the spec provides one. It encodes intent from the API author; deriving names from paths produces machine-readable noise (get__v1__users___id).
- Lead every description with an action verb and the object. "Retrieve a user by ID" tells an agent more than "User endpoint."
- Mark destructive operations explicitly. Any tool that deletes, overwrites, or sends external communications must include a confirmation parameter and note the side effect in the description.
- Never expose auth tokens in tool schemas. Credentials belong in environment variables referenced from the server implementation, not in the MCP manifest.
- One tool per intent. Avoid parameter-flag multiplexing (
action: "create" | "update" | "delete" in one tool) — agents cannot reliably disambiguate.
- Changelog-first versioning. Add a
CHANGELOG.md entry before renaming or removing a tool; breaking changes require a new tool ID, not an in-place rename.
Related Skills
mcp-cloudflare-builder — deploys an MCP server to Cloudflare Workers with OAuth (use after scaffolding with this skill)
skill-creation — for packaging the built MCP server as an installable agent skill
process-extraction — for capturing the spec-to-tool conversion process as a repeatable workflow
Output
An MCP server scaffold directory containing:
tool_manifest.json — all tool definitions with names, descriptions, and schemas
server.py or server.ts — starter implementation with stubs for each tool
CHANGELOG.md — initial entry for v1.0.0 contract
- Validation report listing any issues found