Générer un serveur MCP à partir d'une spécification OpenAPI transforme toute API REST documentée en un ensemble d'outils typés que les agents IA peuvent appeler directement, éliminant ainsi la définition manuelle des outils. Au lieu d'écrire des gestionnaires d'outils MCP à la main pour chaque point de terminaison, vous analysez le JSON/YAML OpenAPI, extrayez les opérations, paramètres et schémas de réponse, et générez un serveur MCP conforme aux normes que Claude Code, Cursor ou tout client MCP peut consommer. Ce tutoriel construit un générateur en Python qui prend une spécification OpenAPI 3.x et produit un serveur MCP fonctionnel.
Prérequis
- Python 3.10+
- Bibliothèque fastmcp installée (pip install fastmcp)
- Une spécification OpenAPI 3.x (JSON ou YAML)
- Clé API Scavio pour tester avec une spécification réelle
Parcours
Étape 1: Analyser la spécification OpenAPI
Charger et analyser un fichier de spécification OpenAPI 3.x, en extrayant toutes les opérations avec leurs chemins, méthodes, paramètres et corps de requête.
import json, yaml
from pathlib import Path
def parse_openapi(spec_path):
content = Path(spec_path).read_text()
spec = yaml.safe_load(content) if spec_path.endswith(('.yml', '.yaml')) else json.loads(content)
operations = []
for path, methods in spec.get('paths', {}).items():
for method, details in methods.items():
if method in ('get', 'post', 'put', 'patch', 'delete'):
op = {
'operation_id': details.get('operationId', f'{method}_{path}'.replace('/', '_')),
'summary': details.get('summary', ''),
'method': method.upper(),
'path': path,
'parameters': details.get('parameters', []),
'request_body': details.get('requestBody', {}),
}
operations.append(op)
print(f'Parsed {len(operations)} operations from {spec_path}')
return spec, operations
spec, ops = parse_openapi('openapi.json')Étape 2: Générer les définitions d'outils MCP
Convertir chaque opération OpenAPI en un outil MCP avec des paramètres typés et des descriptions.
def openapi_type_to_python(schema):
type_map = {'string': 'str', 'integer': 'int', 'number': 'float',
'boolean': 'bool', 'array': 'list', 'object': 'dict'}
return type_map.get(schema.get('type', 'string'), 'str')
def generate_tool_code(op, base_url):
params = []
for p in op['parameters']:
ptype = openapi_type_to_python(p.get('schema', {}))
default = f' = None' if not p.get('required') else ''
params.append(f"{p['name']}: {ptype}{default}")
body_params = []
if op['request_body']:
content = op['request_body'].get('content', {})
json_schema = content.get('application/json', {}).get('schema', {})
for prop, schema in json_schema.get('properties', {}).items():
ptype = openapi_type_to_python(schema)
required = prop in json_schema.get('required', [])
default = '' if required else ' = None'
body_params.append(f"{prop}: {ptype}{default}")
all_params = ', '.join(params + body_params)
name = op['operation_id'].replace('-', '_').replace('.', '_')
return name, all_params, op['summary']Étape 3: Construire le fichier du serveur MCP
Générer un fichier Python complet de serveur MCP qui enregistre tous les outils et gère les requêtes HTTP vers l'API sous-jacente.
def generate_mcp_server(spec, operations, base_url, output_path='generated_mcp.py'):
lines = [
'import httpx',
'from fastmcp import FastMCP',
'',
f'mcp = FastMCP("Generated API")',
f'BASE_URL = "{base_url}"',
'',
]
for op in operations:
name, params, summary = generate_tool_code(op, base_url)
lines.append(f'@mcp.tool(description="{summary}")')
lines.append(f'async def {name}({params}) -> dict:')
lines.append(f' async with httpx.AsyncClient() as client:')
if op['method'] == 'GET':
lines.append(f' resp = await client.get(f"{{BASE_URL}}{op["path"]}")')
else:
lines.append(f' resp = await client.{op["method"].lower()}(f"{{BASE_URL}}{op["path"]}", json=locals())')
lines.append(f' return resp.json()')
lines.append('')
Path(output_path).write_text('\n'.join(lines))
print(f'Generated MCP server with {len(operations)} tools at {output_path}')
generate_mcp_server(spec, ops, 'https://api.example.com')Étape 4: Tester avec la spécification OpenAPI de Scavio
Récupérer la spécification de l'API Scavio via une recherche et générer un serveur MCP à partir de celle-ci comme exemple fonctionnel.
import requests, os
# Use Scavio search to find OpenAPI specs for testing
H = {'x-api-key': os.environ['SCAVIO_API_KEY'], 'Content-Type': 'application/json'}
data = requests.post('https://api.scavio.dev/api/v1/search',
headers=H, json={'query': 'petstore openapi 3.0 json spec', 'country_code': 'us'}).json()
print('Found specs to test with:')
for r in data.get('organic_results', [])[:3]:
print(f" {r.get('title', '')}: {r.get('link', '')}")
# Example: generate from the classic Petstore spec
# spec, ops = parse_openapi('petstore.json')
# generate_mcp_server(spec, ops, 'https://petstore3.swagger.io/api/v3')Exemple Python
import json, yaml, httpx, os, requests
from pathlib import Path
def parse_openapi(spec_path):
content = Path(spec_path).read_text()
spec = yaml.safe_load(content) if spec_path.endswith(('.yml', '.yaml')) else json.loads(content)
ops = []
for path, methods in spec.get('paths', {}).items():
for method, details in methods.items():
if method in ('get', 'post', 'put', 'patch', 'delete'):
ops.append({
'id': details.get('operationId', f'{method}_{path}'.replace('/', '_')),
'summary': details.get('summary', ''),
'method': method.upper(), 'path': path,
'parameters': details.get('parameters', []),
'body': details.get('requestBody', {}),
})
return spec, ops
def generate_server(spec, ops, base_url, out='generated_mcp.py'):
type_map = {'string': 'str', 'integer': 'int', 'number': 'float', 'boolean': 'bool'}
lines = ['import httpx', 'from fastmcp import FastMCP', '',
'mcp = FastMCP("Generated API")', f'BASE = "{base_url}"', '']
for op in ops:
name = op['id'].replace('-', '_').replace('.', '_')
params = []
for p in op['parameters']:
pt = type_map.get(p.get('schema', {}).get('type', 'string'), 'str')
params.append(f"{p['name']}: {pt}")
sig = ', '.join(params)
lines.extend([
f'@mcp.tool(description="{op["summary"]}")',
f'async def {name}({sig}) -> dict:',
f' async with httpx.AsyncClient() as c:',
f' r = await c.request("{op["method"]}", f"{{BASE}}{op["path"]}")',
f' return r.json()', ''
])
Path(out).write_text('\n'.join(lines))
print(f'Generated {len(ops)} MCP tools to {out}')
# Example usage
# spec, ops = parse_openapi('openapi.json')
# generate_server(spec, ops, 'https://api.example.com')
print('MCP generator ready. Pass any OpenAPI 3.x spec.')Exemple JavaScript
const fs = require('fs');
function parseOpenAPI(specPath) {
const spec = JSON.parse(fs.readFileSync(specPath, 'utf-8'));
const ops = [];
for (const [path, methods] of Object.entries(spec.paths || {})) {
for (const [method, details] of Object.entries(methods)) {
if (['get', 'post', 'put', 'patch', 'delete'].includes(method)) {
ops.push({
id: details.operationId || \`\${method}_\${path.replace(/\//g, '_')}\`,
summary: details.summary || '',
method: method.toUpperCase(), path,
parameters: details.parameters || [],
});
}
}
}
return {spec, ops};
}
function generateServer(spec, ops, baseUrl, out = 'generated_mcp.mjs') {
let code = \`import {FastMCP} from 'fastmcp';\nconst mcp = new FastMCP('Generated API');\nconst BASE = '\${baseUrl}';\n\n\`;
for (const op of ops) {
const name = op.id.replace(/[-. ]/g, '_');
const params = op.parameters.map(p => \`\${p.name}: {type: 'string'}\`).join(', ');
code += \`mcp.addTool({name: '\${name}', description: '\${op.summary}',\n parameters: {\${params}},\n execute: async (args) => {\n const r = await fetch(\\\`\\\${BASE}\${op.path}\\\`, {method: '\${op.method}'});\n return JSON.stringify(await r.json());\n }\n});\n\n\`;
}
fs.writeFileSync(out, code);
console.log(\`Generated \${ops.length} MCP tools to \${out}\`);
}
// const {spec, ops} = parseOpenAPI('openapi.json');
// generateServer(spec, ops, 'https://api.example.com');
console.log('MCP generator ready');Sortie attendue
Parsed 12 operations from openapi.json
Generated MCP server with 12 tools at generated_mcp.py
Tools: get_users, post_users, get_users_by_id, put_users_by_id, ...