Le Model Context Protocol (MCP) permet aux agents IA d'appeler des outils externes de manière native. Au lieu de coder chaque outil à la main, vous pouvez auto-générer un serveur MCP à partir de n'importe quelle spécification Swagger ou OpenAPI. Ce tutoriel prend la spécification OpenAPI de l'API Scavio et produit un serveur MCP fonctionnel que Claude Code peut utiliser directement. La même approche fonctionne pour toute API disposant d'une spécification Swagger -- Stripe, Twilio, GitHub, ou vos services internes.
Prérequis
- Node.js 18+ ou Python 3.9+
- Une spécification Swagger/OpenAPI en JSON ou YAML
- Connaissance de base de MCP
- Claude Code installé (pour les tests)
Parcours
Étape 1: Analyser la spécification OpenAPI et extraire les points de terminaison
Charger la spécification Swagger et extraire les définitions de points de terminaison qui deviendront des outils MCP. Chaque point de terminaison devient un outil avec des paramètres typés.
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Étape 2: Générer le code du serveur MCP
Transformer les points de terminaison analysés en un serveur MCP fonctionnel. Chaque point de terminaison devient un outil appelable avec validation des entrées.
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)}')Étape 3: Enregistrer le serveur MCP dans Claude Code
Ajoutez votre serveur MCP généré à .mcp.json pour que Claude Code le découvre automatiquement au démarrage.
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')Exemple 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')Exemple 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`));Sortie attendue
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.