with one click
mcp-server-scaffold
Generate Model Context Protocol (MCP) tool servers from API descriptions, enabling AI assistants to connect to external services
Menu
Generate Model Context Protocol (MCP) tool servers from API descriptions, enabling AI assistants to connect to external services
| name | mcp-server-scaffold |
| description | Generate Model Context Protocol (MCP) tool servers from API descriptions, enabling AI assistants to connect to external services |
| license | MIT |
| metadata | {"version":"1.0.0","author":"Michael Lynn [mlynn.org](https://mlynn.org)","category":"ai-tooling","domain":"mcp-protocol","updated":"2026-03-01T00:00:00.000Z","python-tools":"openapi_parser.py, mcp_generator.py, server_tester.py","tech-stack":"python, typescript, mcp, openapi, json-rpc"} |
Use this skill when building MCP servers to connect AI assistants to APIs, databases, or custom tools.
Trigger phrases:
The Model Context Protocol (MCP) is an open standard for connecting AI assistants to tools and data sources. This skill generates production-ready MCP servers from API descriptions (OpenAPI/Swagger), custom tool specs, or database schemas.
What gets generated:
Not for: Building APIs themselves - this assumes you have an API and want to expose it via MCP.
Parse API description:
python scripts/openapi_parser.py api-spec.yaml --output parsed.json
Generate MCP server:
python scripts/mcp_generator.py parsed.json \
--language typescript \
--output mcp-server/
Test the server:
python scripts/server_tester.py mcp-server/ --test-all
scripts/openapi_parser.py — Parse OpenAPI/Swagger specs and extract tool definitionsscripts/mcp_generator.py — Generate MCP server code (TypeScript or Python)scripts/server_tester.py — Test MCP server tools and validate responsesreferences/mcp-protocol-guide.md — MCP protocol overview and conceptsreferences/tool-design-patterns.md — Best practices for MCP tool designassets/openapi-example.yaml — Sample OpenAPI specassets/tool-definition-template.json — Manual tool definition formatassets/docker-compose.yaml — Deployment configurationOpenAPI specs already describe:
Conversion: OpenAPI endpoint → MCP tool
# OpenAPI
/users/{id}:
get:
summary: Get user by ID
parameters:
- name: id
in: path
required: true
schema:
type: string
→ MCP Tool:
{
name: "get_user",
description: "Get user by ID",
inputSchema: {
type: "object",
properties: {
id: { type: "string" }
},
required: ["id"]
}
}
OpenAPI: GET /api/v1/users/{id}/profile
MCP Tool: get_user_profile
Rules:
MCP servers should return structured errors:
{
error: {
code: -32603, // JSON-RPC error code
message: "API error",
data: {
status: 404,
body: "User not found"
}
}
}
Categories:
-32700 Parse error (malformed request)-32600 Invalid request (missing required params)-32601 Method not found (unknown tool)-32603 Internal error (API failure, network error)Pattern 1: API Key (most common)
const apiKey = process.env.API_KEY;
headers['Authorization'] = `Bearer ${apiKey}`;
Pattern 2: OAuth 2.0
const token = await refreshToken();
headers['Authorization'] = `Bearer ${token}`;
Pattern 3: Custom Auth
headers['X-Custom-Auth'] = computeSignature(request);
Best practice: Store credentials in environment variables, never hardcode.
MCP servers should handle rate limits gracefully:
async function callWithRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (err) {
if (err.status === 429) {
const delay = Math.pow(2, i) * 1000; // Exponential backoff
await sleep(delay);
continue;
}
throw err;
}
}
}
mcp-server/
├── src/
│ ├── index.ts # Server entry point
│ ├── tools/ # Tool implementations
│ │ ├── get_user.ts
│ │ ├── create_user.ts
│ │ └── ...
│ ├── client.ts # API client wrapper
│ ├── types.ts # TypeScript types
│ └── validation.ts # Input validation
├── tests/
│ ├── tools.test.ts # Tool tests
│ └── integration.test.ts
├── package.json
├── tsconfig.json
├── .env.example
└── README.md
mcp-server/
├── src/
│ ├── __init__.py
│ ├── server.py # Server entry point
│ ├── tools/ # Tool implementations
│ │ ├── get_user.py
│ │ ├── create_user.py
│ │ └── ...
│ ├── client.py # API client wrapper
│ └── validation.py # Input validation
├── tests/
│ ├── test_tools.py
│ └── test_integration.py
├── requirements.txt
├── .env.example
└── README.md
Purpose: Parse OpenAPI/Swagger specs and extract MCP tool definitions.
Usage:
python scripts/openapi_parser.py api-spec.yaml --output parsed.json
Output:
{
"info": {
"title": "User API",
"version": "1.0.0",
"baseUrl": "https://api.example.com"
},
"auth": {
"type": "bearer",
"headerName": "Authorization"
},
"tools": [
{
"name": "get_user",
"description": "Get user by ID",
"method": "GET",
"path": "/users/{id}",
"parameters": {
"path": ["id"],
"query": [],
"body": null
},
"inputSchema": { ... },
"responseSchema": { ... }
}
]
}
Purpose: Generate MCP server code from parsed API spec.
Usage:
python scripts/mcp_generator.py parsed.json \
--language typescript \
--output mcp-server/ \
--with-docker
Options:
--language - typescript or python (default: typescript)--output - Output directory--with-docker - Generate Dockerfile and docker-compose.yaml--auth-env-var - Name of env var for auth (default: API_KEY)Generates:
Purpose: Test MCP server tools and validate responses.
Usage:
python scripts/server_tester.py mcp-server/ \
--test-all \
--mock-api \
--output test-report.json
Tests:
Output:
{
"summary": {
"total_tools": 5,
"passed": 4,
"failed": 1
},
"results": [
{
"tool": "get_user",
"tests": {
"schema_valid": true,
"input_validation": true,
"response_validation": true,
"error_handling": false
},
"errors": ["Missing 404 error handling"]
}
]
}
Scenario: Create MCP server for GitHub API
Step 1: Get OpenAPI spec
# GitHub publishes OpenAPI specs
curl -o github-api.yaml https://github.com/github/rest-api-description/releases/latest/download/api.github.com.yaml
Step 2: Parse spec
python scripts/openapi_parser.py github-api.yaml \
--filter "repos,issues" \
--output github-parsed.json
Output: 50 tools extracted (filtered to repos and issues endpoints)
Step 3: Generate MCP server
python scripts/mcp_generator.py github-parsed.json \
--language typescript \
--output github-mcp-server/ \
--auth-env-var GITHUB_TOKEN
Output: TypeScript MCP server with 50 tools
Step 4: Configure authentication
cd github-mcp-server/
cp .env.example .env
echo "GITHUB_TOKEN=ghp_your_token_here" >> .env
Step 5: Test tools
python scripts/server_tester.py github-mcp-server/ \
--test-all \
--output test-report.json
Output: 48/50 tools passing (2 require org access)
Step 6: Deploy
cd github-mcp-server/
npm install
npm run build
npm start
Step 7: Connect to Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"github": {
"command": "node",
"args": ["/path/to/github-mcp-server/dist/index.js"],
"env": {
"GITHUB_TOKEN": "ghp_your_token_here"
}
}
}
}
Now Claude can call GitHub tools: "Create an issue in my repo", "List my repositories", etc.
Many APIs return paginated results. MCP tools should handle this:
async function listUsers(params: { limit?: number, cursor?: string }) {
const response = await api.get('/users', {
params: {
limit: params.limit || 100,
cursor: params.cursor
}
});
return {
users: response.data,
nextCursor: response.nextCursor,
hasMore: !!response.nextCursor
};
}
MCP Tool:
{
"name": "list_users",
"inputSchema": {
"type": "object",
"properties": {
"limit": { "type": "number", "default": 100 },
"cursor": { "type": "string" }
}
}
}
For efficiency, support batch operations:
async function batchGetUsers(params: { ids: string[] }) {
// Split into chunks of 50 (API limit)
const chunks = chunkArray(params.ids, 50);
const results = [];
for (const chunk of chunks) {
const response = await api.post('/users/batch', { ids: chunk });
results.push(...response.data);
}
return results;
}
Some MCP servers can register webhooks:
async function registerWebhook(params: { event: string, url: string }) {
return await api.post('/webhooks', {
event: params.event,
url: params.url,
secret: process.env.WEBHOOK_SECRET
});
}
Cache frequently accessed data:
const cache = new Map();
async function getUser(params: { id: string }) {
const cacheKey = `user:${params.id}`;
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const user = await api.get(`/users/${params.id}`);
cache.set(cacheKey, user, { ttl: 300 }); // 5 min TTL
return user;
}
Bad:
{
"name": "get_data",
"description": "Gets data"
}
Good:
{
"name": "get_user_profile",
"description": "Retrieves detailed user profile including name, email, created date, and account settings. Requires user ID."
}
Always specify required fields and types:
{
"inputSchema": {
"type": "object",
"properties": {
"userId": {
"type": "string",
"description": "Unique user identifier"
},
"includeSettings": {
"type": "boolean",
"description": "Whether to include account settings",
"default": false
}
},
"required": ["userId"]
}
}
Return actionable error messages:
if (!params.userId) {
throw {
code: -32600,
message: "Missing required parameter: userId",
data: {
parameter: "userId",
type: "string",
description: "Unique user identifier"
}
};
}
Normalize API responses to consistent structure:
async function getUser(params) {
try {
const response = await api.get(`/users/${params.id}`);
return {
success: true,
data: response.data,
metadata: {
requestId: response.headers['x-request-id'],
rateLimit: {
remaining: response.headers['x-ratelimit-remaining'],
reset: response.headers['x-ratelimit-reset']
}
}
};
} catch (err) {
return {
success: false,
error: {
message: err.message,
status: err.status,
code: err.code
}
};
}
}
Before deploying MCP server:
| Use MCP Server | Use Direct API |
|---|---|
| AI assistant integration | Simple scripts |
| Multiple tools from one API | One-off requests |
| Reusable across projects | Project-specific code |
| Need structured error handling | Quick prototypes |
| Authentication abstraction needed | Full control required |
npm start
# Runs on localhost, accessible to Claude Desktop
docker-compose up -d
# Containerized, easy to deploy
// Deploy to AWS Lambda, Vercel, etc.
export const handler = mcpServer.handler;
Track MCP server health:
const metrics = {
totalRequests: 0,
successfulRequests: 0,
failedRequests: 0,
averageLatency: 0
};
function recordMetrics(tool: string, success: boolean, latency: number) {
metrics.totalRequests++;
if (success) metrics.successfulRequests++;
else metrics.failedRequests++;
// Update average latency
metrics.averageLatency =
(metrics.averageLatency * (metrics.totalRequests - 1) + latency) / metrics.totalRequests;
console.log(`[${tool}] ${success ? 'OK' : 'FAIL'} ${latency}ms`);
}
references/mcp-protocol-guide.mdreferences/tool-design-patterns.mdMichael Lynn — mlynn.org · @mlynn · LinkedIn · GitHub
Next steps after generating server:
Generate conference talk proposals (CFPs), abstracts, and presentation outlines with slide structure and timing guidance
Generate workshop agendas and hands-on curriculum for customer developer days, technical training sessions, and field engagements
MongoDB schema design advisor focusing on embed vs reference decisions, relationship modeling, and performance optimization patterns
Generate scoring rubrics and constructive feedback for hackathon submissions with fair evaluation frameworks and actionable improvement suggestions
Add AI capabilities to a MongoDB app including LLM summarization, structured generation, RAG pipeline with Atlas Vector Search, Voyage AI embeddings, and usage tracking with cost estimation
Self-service Atlas cluster provisioning with HTTP Digest auth, Admin API v2 client, 9-step orchestration with rollback, status polling, and DevRel attribution tracking