模型上下文协议 (MCP) 允许 AI 代理本地调用外部工具。您可以根据任何 Swagger 或 OpenAPI 规范自动生成 MCP 服务器,而不是手动编码每个工具。本教程采用 Scavio API OpenAPI 规范并生成 Claude Code 可以直接使用的工作 MCP 服务器。同样的方法适用于任何具有 Swagger 规范的 API——Stripe、Twilio、GitHub 或您的内部服务。
前置条件
- Node.js 18+ 或 Python 3.9+
- Swagger/OpenAPI JSON 或 YAML 规范
- MCP的基本了解
- 安装了克劳德代码(用于测试)
操作指南
步骤 1: 解析 OpenAPI 规范并提取端点
加载 Swagger 规范并提取将成为 MCP 工具的端点定义。每个端点都成为一个具有类型化参数的工具。
import json, yaml
from pathlib import Path
def parse_openapi(spec_path: str) -> list:
"""Parse OpenAPI spec into MCP tool definitions."""
raw = Path(spec_path).read_text()
spec = yaml.safe_load(raw) if spec_path.endswith('.yaml') else json.loads(raw)
tools = []
base_url = spec.get('servers', [{}])[0].get('url', '')
for path, methods in spec.get('paths', {}).items():
for method, details in methods.items():
if method not in ('get', 'post', 'put', 'delete'):
continue
params = []
for p in details.get('parameters', []):
params.append({
'name': p['name'],
'type': p.get('schema', {}).get('type', 'string'),
'required': p.get('required', False),
'description': p.get('description', '')
})
# Extract request body schema
body = details.get('requestBody', {}).get('content', {}).get('application/json', {})
body_schema = body.get('schema', {}).get('properties', {})
for name, prop in body_schema.items():
params.append({
'name': name, 'type': prop.get('type', 'string'),
'required': name in body.get('schema', {}).get('required', []),
'description': prop.get('description', '')
})
tool_name = details.get('operationId', f'{method}_{path}'.replace('/', '_'))
tools.append({
'name': tool_name, 'description': details.get('summary', ''),
'method': method.upper(), 'path': path,
'base_url': base_url, 'parameters': params
})
print(f'Parsed {len(tools)} tools from spec')
for t in tools[:5]:
print(f' {t["name"]}: {t["method"]} {t["path"]}')
return tools步骤 2: 生成 MCP 服务器代码
将解析的端点转换为工作 MCP 服务器。每个端点都成为具有输入验证的可调用工具。
def generate_mcp_server(tools: list, output_path: str = 'mcp_server.py'):
"""Generate a Python MCP server from parsed tool definitions."""
lines = [
'import json, os, sys, requests',
'from typing import Any',
'',
'def handle_tool_call(name: str, arguments: dict) -> dict:',
' """Route tool calls to the correct API endpoint."""',
' tools_map = {',
]
for tool in tools:
lines.append(f' "{tool["name"]}": {{"method": "{tool["method"]}", "path": "{tool["path"]}", "base_url": "{tool["base_url"]}"}},')
lines.extend([
' }',
' if name not in tools_map:',
' return {"error": f"Unknown tool: {name}"}',
' spec = tools_map[name]',
' url = spec["base_url"] + spec["path"]',
' headers = {"Content-Type": "application/json",',
' "x-api-key": os.environ.get("API_KEY", "")}',
' if spec["method"] == "GET":',
' resp = requests.get(url, headers=headers, params=arguments)',
' else:',
' resp = requests.post(url, headers=headers, json=arguments)',
' return resp.json()',
'',
])
# Write MCP stdio loop
lines.extend([
'def main():',
' for line in sys.stdin:',
' msg = json.loads(line.strip())',
' if msg.get("method") == "tools/call":',
' result = handle_tool_call(msg["params"]["name"], msg["params"].get("arguments", {}))',
' print(json.dumps({"jsonrpc": "2.0", "id": msg["id"], "result": {"content": [{"type": "text", "text": json.dumps(result)}]}}))',
' sys.stdout.flush()',
'',
'if __name__ == "__main__":',
' main()',
])
with open(output_path, 'w') as f:
f.write('\n'.join(lines))
print(f'Generated MCP server: {output_path}')
print(f'Tools: {len(tools)}')步骤 3: 在Claude代码中注册MCP服务器
将生成的 MCP 服务器添加到 .mcp.json,以便 Claude Code 在启动时自动发现它。
import json
from pathlib import Path
def register_mcp(server_name: str, server_path: str):
mcp_config_path = Path('.mcp.json')
config = json.loads(mcp_config_path.read_text()) if mcp_config_path.exists() else {'mcpServers': {}}
config['mcpServers'][server_name] = {
'command': 'python3',
'args': [server_path],
'env': {
'API_KEY': 'your-api-key-here'
}
}
mcp_config_path.write_text(json.dumps(config, indent=2))
print(f'Registered MCP server: {server_name}')
print(f'Config: {mcp_config_path}')
print(f'Restart Claude Code to load the new tools.')
# Register the generated server
register_mcp('my-api', 'mcp_server.py')Python 示例
import json, yaml, requests, os, sys
def parse_spec(spec_url):
resp = requests.get(spec_url)
spec = resp.json()
tools = []
base = spec.get('servers', [{}])[0].get('url', '')
for path, methods in spec.get('paths', {}).items():
for method, details in methods.items():
if method in ('get', 'post'):
tools.append({'name': details.get('operationId', path),
'method': method.upper(), 'path': path, 'base_url': base})
return tools
def call_tool(tool, args):
url = tool['base_url'] + tool['path']
headers = {'x-api-key': os.environ.get('API_KEY', ''), 'Content-Type': 'application/json'}
if tool['method'] == 'GET':
return requests.get(url, headers=headers, params=args).json()
return requests.post(url, headers=headers, json=args).json()
tools = parse_spec('https://api.scavio.dev/api/v1/openapi.json')
print(f'Parsed {len(tools)} tools')JavaScript 示例
async function parseSpec(specUrl) {
const resp = await fetch(specUrl);
const spec = await resp.json();
const tools = [];
const base = spec.servers?.[0]?.url || '';
for (const [path, methods] of Object.entries(spec.paths || {})) {
for (const [method, details] of Object.entries(methods)) {
if (['get', 'post'].includes(method)) {
tools.push({ name: details.operationId || path, method: method.toUpperCase(), path, base });
}
}
}
return tools;
}
async function callTool(tool, args) {
const url = tool.base + tool.path;
const opts = { headers: { 'x-api-key': process.env.API_KEY, 'Content-Type': 'application/json' } };
if (tool.method === 'GET') {
return (await fetch(`${url}?${new URLSearchParams(args)}`, opts)).json();
}
return (await fetch(url, { ...opts, method: 'POST', body: JSON.stringify(args) })).json();
}
parseSpec('https://api.scavio.dev/api/v1/openapi.json').then(t => console.log(`${t.length} tools`));预期输出
Parsed 6 tools from spec
search: POST /api/v1/search
tiktok_search_videos: POST /api/v1/tiktok/search/videos
tiktok_user_info: POST /api/v1/tiktok/user/info
Generated MCP server: mcp_server.py
Registered MCP server: my-api
Restart Claude Code to load the new tools.