openapi-to-mcp

Par mcp-use · mcp-use

Construire et déployer un serveur MCP à partir d'une spec OpenAPI / Swagger en utilisant le SDK TypeScript mcp-use. Utilise cette skill chaque fois que l'utilisateur souhaite « transformer cette spec OpenAPI en serveur MCP », « rendre cette API utilisable depuis Claude/ChatGPT », « encapsuler cette doc Swagger comme outils MCP », « exposer cette API REST à un LLM », « générer des outils MCP depuis une spec », ou colle/joint un fichier `openapi.yaml`, `openapi.json` ou `swagger.json` en demandant une version compatible Claude. Se déclenche même si l'utilisateur ne mentionne pas « MCP » — s'il décrit une API HTTP existante (endpoints REST, un service interne, une API tierce pour laquelle il possède une clé) et souhaite qu'un LLM puisse l'appeler, c'est la bonne skill. Couvre l'ingestion de spec (chemin de fichier, URL ou collé), le mapping opération-vers-outil, le câblage de l'authentification (apiKey, bearer, basic, OAuth bearer), le scaffolding avec `create-mcp-use-app`, la génération d'outils avec des schémas zod appropriés, les tests en direct dans l'inspecteur mcp-use, et le déploiement sur Manufact / mcp-use cloud.

npx skills add https://github.com/mcp-use/mcp-use --skill openapi-to-mcp

Construire un serveur MCP à partir d'une spécification OpenAPI

Transformez une API REST existante — décrite par un document OpenAPI 3.x ou Swagger 2.0 — en serveur MCP. Chaque opération de la spec devient un outil MCP que le LLM peut appeler. Le serveur s'exécute localement pour les tests et se déploie sur Manufact / mcp-use cloud en une commande.

Cette compétence est la recette complète : scope → ingestion de spec → mapping des opérations → scaffolding → génération des outils → câblage auth → tests → déploiement.

Philosophie centrale : la spec est le contrat

Le document OpenAPI est la source de vérité. Les noms des outils, les descriptions, les formes des paramètres et les exigences d'authentification proviennent tous de la spec — ils ne doivent pas être inventés. C'est important parce que :

  • Le LLM fait confiance aux descriptions. Si la spec dit summary: "Get current weather for a city", c'est exactement ce que le LLM lira pour décider s'il faut appeler l'outil. Les résumés écrits à la main divergent ; les résumés dérivés de la spec restent synchronisés si l'API change.
  • Les schémas Zod reflètent les schémas OpenAPI. Chaque paramètre — path, query, body — devient un champ dans un objet zod. Les champs requis/optionnels, les énums, min/max et les descriptions se transposent tous. Le LLM utilise le schéma pour déterminer ce qu'il doit demander à l'utilisateur.
  • L'auth existe en dehors de la spec. OpenAPI déclare le schéma d'auth mais jamais le secret. Les secrets viennent des variables d'env ; la spec vous dit quelles variables d'env sont obligatoires.

En cas de doute, préférez la fidélité mécanique à la spec plutôt que la créativité. Le LLM fait la partie créative — dialoguer avec l'utilisateur — et a seulement besoin d'une poignée fidèle et bien typée sur l'API.

Processus

1. Définir la portée (utiliser AskUserQuestion)

Avant d'écrire du code, verrouillez cinq choses via l'outil AskUserQuestion. Les cinq concernent l'API et ce qu'il faut construire — le déploiement est une question séparée que nous posons plus tard à l'étape 10, quand l'utilisateur peut effectivement l'évaluer face à un serveur fonctionnel.

  • Source de la spec : un chemin de fichier dans l'espace de travail, une URL (ex. https://api.example.com/openapi.json), ou collée dans le chat. Si collée, sauvegardez-la d'abord dans openapi.yaml ou openapi.json.
  • URL de base du serveur : prenez-la depuis servers[0].url dans la spec si elle existe ; sinon demandez. Plusieurs entrées servers sont courantes (prod / staging) — confirmez laquelle.
  • Schéma d'authentification : lisez components.securitySchemes. S'il y en a plusieurs, demandez lequel utiliser. Si l'API a besoin d'une clé API ou d'un token, demandez quelle variable d'env doit le contenir (API_KEY, OPENAI_API_KEY, etc.). Ne demandez jamais le secret lui-même — ne le mettez jamais dans la conversation ou ne le committez.
  • Filtre d'opérations : les grandes specs (Stripe, GitHub) ont des centaines d'endpoints. Demandez si toutes les opérations doivent être exposées, une étiquette (pets, users), ou une liste présélectionnée. La valeur par défaut est « toutes » pour les specs ayant ~30 opérations ou moins ; demandez au-dessus. Voir references/mapping-rules.md pour les patterns de filtrage.
  • Widgets : demandez si des opérations doivent afficher un widget dans le chat (un composant React affiché en ligne à côté de la réponse du LLM), ou si c'est un serveur pur tools-only. La valeur par défaut pour un wrapper OpenAPI est tools-only — le LLM lit du JSON et discute. Choisissez les widgets seulement si l'utilisateur veut une UI plus riche pour des réponses spécifiques (une carte pour un endpoint de géocodage, un graphique pour un endpoint de métriques, une liste de cartes pour un résultat de recherche). Cette réponse pilote le template du scaffold à l'étape 3 : tools-only → --template blank, n'importe quels widgets → --template mcp-apps (qui livre l'infrastructure de widgets resources/ pré-câblée). Si l'utilisateur veut des widgets sur la plupart des opérations, la compétence build-mcp-app est généralement un meilleur choix que celle-ci — signalez-le et confirmez avant de continuer.

Ne sautez pas cette étape. Générer 200 outils que l'utilisateur n'a pas besoin pollue la liste d'outils du LLM et le ralentit.

2. Acquérir et dérférencer la spec

Obtenez la spec dans un objet JSON unique et dérférencé sur le disque. Le déréférencement intègre les $ref pour que le code suivant n'ait jamais besoin de poursuivre les pointeurs.

# Dans la racine du projet scaffoldé
npm install @apidevtools/swagger-parser
// scripts/load-spec.ts (exécuter une fois, manuellement ou comme étape de build)
import SwaggerParser from "@apidevtools/swagger-parser";
import { writeFileSync } from "node:fs";

const spec = await SwaggerParser.dereference("./openapi.yaml");
writeFileSync("./openapi.dereferenced.json", JSON.stringify(spec, null, 2));

Pour les specs d'URL, swagger-parser accepte l'URL directement. Pour du YAML collé, écrivez-le d'abord dans openapi.yaml, puis déréférencez. Si la spec est Swagger 2.0, exécutez-la d'abord via swagger2openapi (npx swagger2openapi --outfile openapi.yaml swagger.yaml).

Vérifiez le fichier dérférencé : ouvrez-le, cherchez "$ref" — il ne devrait y en avoir aucun. S'il y en a, la spec a des références circulaires et swagger-parser les conserve telles quelles ; traitez ces refs comme des types d'objet opaques dans zod.

3. Scaffolder avec create-mcp-use-app

Choisissez le template selon la réponse sur les widgets de l'étape 1 :

# Tools-only (la valeur par défaut pour un wrapper OpenAPI)
npx create-mcp-use-app@latest <nom-projet> --template blank

# N'importe quels widgets
npx create-mcp-use-app@latest <nom-projet> --template mcp-apps

Laissez le scaffold installer les dépendances et faire git init — les deux sont utiles (npm install exécute mcp-use generate-types postinstall, et un repo git est obligatoire pour npm run deploy plus tard). Le skill installe aussi par défaut les compétences de coding-agent complémentaires ; c'est normal.

Vérifiez le catalogue de templates avec npx create-mcp-use-app@latest --list-templates si c'était il y a longtemps — l'ensemble disponible est blank, starter, mcp-apps à ce jour. starter inclut des outils exemples que vous déchireriez, donc nous ne le recommandons pas ici.

Après le scaffolding, ajoutez les deux dépendances supplémentaires que le flux OpenAPI nécessite :

cd <nom-projet>
npm install @apidevtools/swagger-parser dotenv

Ce que vous obtenez de blank (mcp-apps est un sur-ensemble avec resources/ + infrastructure de widgets) :

  • index.ts à la racine avec une instance MCPServer configurée — name, title, version, description, baseUrl, favicon, et un tableau icons[]. Exemples commentés pour les outils, ressources et prompts. Écoute sur process.env.PORT (défaut 3000).
  • package.json avec des scripts câblés au CLI mcp-use : dev (hot reload + inspector), build, start, deploy. tsx, zod, et typescript sont déjà dans les dépendances dev/régulières ; ne les réinstallez pas.
  • tsconfig.json pré-configuré pour ESM ("type": "module").
  • public/ avec un favicon et une icône SVG — servis en tant qu'assets statiques.
  • Un répertoire .git et un commit initial.

Le scaffold réserve la variable d'env MCP_URL pour l'URL de base publique du serveur MCP lui-même (utilisée pour les URLs d'assets de widgets et similaires). Ce n'est pas l'URL de base de l'API en amont — nommez votre variable en amont BASE_URL (ou API_BASE_URL si vous voulez être explicite) pour éviter de marcher dessus.

4. Planifier la structure du projet

Gardez l'arborescence superficielle et prévisible. L'idée est que quelqu'un lisant le projet pour la première fois puisse trouver le client OpenAPI, le câblage des outils et l'auth dans trois fichiers évidents.

<projet>/
├── index.ts                         # MCPServer + boucle d'enregistrement server.tool()
├── openapi.yaml                     # La spec originale (committée)
├── openapi.dereferenced.json        # Spec dérférencée (dans .gitignore ; régénérée)
├── src/
│   ├── client.ts                    # Client HTTP basé sur fetch (URL de base + auth + gestion erreurs)
│   ├── auth.ts                      # Lit les variables d'env, construit l'en-tête d'auth
│   ├── operations.ts                # Charge la spec dérférencée, expose les métadonnées d'opération
│   └── schema.ts                    # Convertisseur schéma OpenAPI → zod
├── scripts/
│   └── load-spec.ts                 # Assistant déréférencement (étape 2)
├── .env.example                     # Documenter les variables d'env obligatoires (API_KEY, BASE_URL, etc.)
└── package.json

Pour les très petites specs (<10 opérations) vous pouvez intégrer client.ts, auth.ts, et operations.ts dans index.ts. Pour tout ce qui est plus grand, divisez — le LLM fonctionne mieux quand chaque fichier a un seul rôle.

5. Mapper les opérations aux outils

Pour chaque opération dans la spec, vous produisez un appel server.tool({...}, handler). Le mapping est mécanique :

Champ OpenAPI Champ outil MCP
operationId (préféré) ou ${method}_${path} slugifié name de l'outil (snake_case)
summary + description description de l'outil
parameters (path + query) + requestBody.content."application/json".schema objet zod fusionné → schema de l'outil
responses.200.content."application/json".schema objet zod optionnel → outputSchema de l'outil
security (ou fallback au niveau racine) quels en-têtes d'auth le gestionnaire attache

Lisez references/mapping-rules.md pour les règles complètes — incluant comment nommer les outils quand operationId manque, comment gérer oneOf / anyOf / types nullable en zod, comment aplatir les corps de requête multi-contenus, et comment traiter les gotchas de forme de réponse (téléchargements binaires, streaming, listes paginées).

6. Générer les schémas zod

Les types OpenAPI se mappent à zod comme suit. Le convertisseur complet vit dans src/schema.ts — voir references/code-templates.md pour l'implémentation. Choix clés :

  • type: string avec enumz.enum([...]). Utilisez la description OpenAPI comme argument .describe() pour que le LLM la voie.
  • type: integer / type: numberz.number().int() / z.number(). Transportez minimum, maximum, multipleOf.
  • type: arrayz.array(<itemType>). Si minItems / maxItems existent, chaînez .min() / .max().
  • type: objectz.object({...}). Les props obligatoires sont non-optionnels ; les autres enveloppez dans .optional().
  • oneOf / anyOfz.union([...]). allOf avec seulement des membres objets → fusionnez dans un z.object.
  • nullable: true (OpenAPI 3.0) ou type: ["string", "null"] (3.1) → .nullable().

Toujours appeler .describe(openapi.description ?? openapi.summary ?? "") sur chaque champ pour que le LLM obtienne des indices lisibles par l'humain quand il remplit les args.

7. Construire le client HTTP et la couche d'authentification

src/client.ts expose une fonction : callOperation(operationId, args) → Promise<unknown>. En interne elle :

  1. Cherche l'opération dans la spec dérférencée.
  2. Substitue les params de path dans le template d'URL (/users/{id} + {id: 42}/users/42).
  3. Ajoute les params de query en tant que ?key=value.
  4. Ajoute les en-têtes d'auth depuis src/auth.ts.
  5. Sérialise le corps de la requête en JSON (ou application/x-www-form-urlencoded si la spec le dit).
  6. Envoie la requête ; lève une exception sur non-2xx avec le corps d'erreur du serveur inclus.
  7. Retourne du JSON analysé (ou du texte, pour les réponses non-JSON).

src/auth.ts lit depuis process.env basé sur les securitySchemes de la spec. Voir references/auth.md pour les quatre schémas courants et comment chacun devient un en-tête. Les variables d'env obligatoires vont dans .env.example — c'est le contrat pour quiconque exécute le serveur plus tard.

8. Câbler les outils dans index.ts

La boucle d'enregistrement est petite. Pseudocode :

import "dotenv/config";
import { MCPServer, text } from "mcp-use/server";
import { operations } from "./src/operations";
import { callOperation } from "./src/client";
import { operationToZod } from "./src/schema";

// Conservez les champs MCPServer que le scaffold vous a donnés (title, baseUrl, favicon, icons).
// Ajustez juste `name`, `title`, et `description` pour correspondre à l'API que vous wrappez.
const server = new MCPServer({
  name: "<api-name>",
  title: "<API name>",
  version: "1.0.0",
  description: "MCP server wrapping the <API name> REST API",
  baseUrl: process.env.MCP_URL || "http://localhost:3000",
  favicon: "favicon.ico",
  icons: [{ src: "icon.svg", mimeType: "image/svg+xml", sizes: ["512x512"] }],
});

for (const op of operations) {
  server.tool(
    {
      name: op.toolName,
      description: op.description,
      schema: operationToZod(op),
    },
    async (args) => {
      const result = await callOperation(op.operationId, args);
      return text(typeof result === "string" ? result : JSON.stringify(result, null, 2));
    },
  );
}

// Transport HTTP streamable — le seul transport supporté pour cette compétence.
// Endpoint MCP : POST http://localhost:<port>/mcp
// Inspector :    http://localhost:<port>/inspector
const PORT = process.env.PORT ? Number(process.env.PORT) : 3000;
server.listen(PORT);

Le transport doit être HTTP streamable, pas stdio. Le server.listen(port) de mcp-use configure le transport HTTP streamable à /mcp — c'est le bon choix pour chaque serveur que cette compétence génère. Ne substituez pas stdio. Les serveurs stdio ne peuvent pas être déployés sur Manufact / mcp-use cloud (le cloud a besoin d'un endpoint HTTP pour router le trafic), ne peuvent pas être testés avec l'inspector en ligne, ne peuvent pas être installés comme connecteur personnalisé dans ChatGPT ou Claude (les deux se connectent via HTTPS), et ne peuvent pas être frappés avec les tests curl dans references/testing.md. Stdio est pour les serveurs MCP CLI-tools locaux, ce n'est pas ce qu'on construit ici.

Les templates complets pour chaque fichier dans references/code-templates.md.

9. Tester le serveur

Commencez le serveur dev en premier — chaque test dans cette étape suppose qu'il s'exécute :

npm run dev

Le log affiche le port (défaut 3000, fallback 3001 s'il est pris), l'URL MCP (http://localhost:<port>/mcp), et l'URL de l'inspector. Laissez-le s'exécuter dans un terminal ; utilisez un deuxième terminal pour les commandes de test ci-dessous.

Ensuite testez en deux couches. Ne prétendez pas être « fait » jusqu'à ce que les deux passent. Les recettes complètes dans references/testing.md.

Couche 1 — CLI mcp-use client. C'est la première chose à laquelle recourir. Le package mcp-use livre un CLI qui parle HTTP streamable, gère la session/bookkeeping d'auth, et donne une boucle tools list / tools call / interactive directement depuis le terminal. Pas de code, pas d'arithmétique curl.

# Sauvegardez le serveur dev sous un nom court
npx mcp-use client connect dev http://localhost:3000/mcp

# Listez et décrivez les outils
npx mcp-use client dev tools list
npx mcp-use client dev tools describe <nom_outil>

# Appelez un outil — les args sont key=value, ou passez JSON pour des formes complexes
npx mcp-use client dev tools call <nom_outil> limit=5
npx mcp-use client dev tools call <nom_outil> '{"limit": 5, "filter": "active"}'

# Mode REPL pour itération rapide
npx mcp-use client dev interactive

Pour les tests CI / scriptés, ajoutez --json et pipeliez vers jq. Si tools list retourne rien, votre filtre d'opérations dans index.ts a tué tout ou openapi.dereferenced.json manque. Si connect lui-même échoue, le serveur dev n'est pas en cours d'exécution ou il est sur un port différent — passez à curl (voir la section references/testing.md « Raw protocol debugging ») pour confirmer que l'endpoint est vivant du tout.

Couche 2 — Inspector chat (la vraie boucle LLM). La couche 1 prouve que le serveur fonctionne. L'inspector prouve que le LLM peut l'utiliser — que la description de l'outil est assez descriptive pour que le modèle choisisse le bon outil, que le schéma zod a assez d'indices pour remplir correctement les args, que la forme de réponse n'est pas si bizarre que le modèle ne puisse pas la résumer.

http://localhost:<PORT>/inspector?server=http%3A%2F%2Flocalhost%3A<PORT>%2Fmcp&tab=chat

Testez à la fois l'invocation forcée (« Use list_pets with limit 5. ») et la découverte libre (« Show me the first 5 pets in the store. ») — la seconde est plus difficile et celle qui attrape la qualité de la description.

Testez les chemins d'échec dans les deux couches : arg obligatoire manquant (rejet zod), auth incorrecte (upstream 401), upstream 5xx (pointez BASE_URL sur un port mort). Les deux couches doivent dégrader avec une erreur propre, pas un crash du serveur.

Si vous voyez « Failed to resolve import » ou des définitions d'outils périmées dans l'une ou l'autre couche : rm -rf .mcp-use && npm run dev.

10. Déployer (demander à l'utilisateur)

Le serveur fonctionne localement. Maintenant demandez, via AskUserQuestion, s'il faut le déployer sur mcp-use cloud ou le garder local. Deux options seulement — pas besoin d'énumérer les alternatives :

  • Déployer sur mcp-use cloud — publie le serveur à une URL https://<nom>.run.mcp-use.com/mcp utilisable depuis ChatGPT, Claude, et n'importe quel client MCP. Préférable quand le serveur sera utilisé par quiconque autre que la propre machine dev du développeur.
  • Rester local — retournez l'URL du serveur dev et arrêtez. Préférable pour prototyper contre une API interne, ou quand l'utilisateur veut évaluer le résultat avant de s'engager sur une URL publique.

Si l'utilisateur choisit rester local, vous êtes terminé — donnez-lui les URLs de l'inspector et /mcp de l'étape 9 et sautez le reste de cette étape. Ne poussez pas le déploiement ; les déploiements prématurés fuient les credentials et créent des endpoints publics périmés.

Si l'utilisateur choisit déployer, le scaffold blank câble déjà npm run deploy sur mcp-use deploy. Deux commandes une fois qu'ils sont loggés :

npx mcp-use login
npm run deploy

Ceci nécessite actuellement un repo GitHub. Si le projet n'est pas sur GitHub encore :

gh repo create <org>/<nom> --private --source=. --push

Après déploiement vous obtenez une URL comme https://<nom>.run.mcp-use.com/mcp. Réglez les mêmes variables d'env dans le dashboard Manufact (le déploiement CLI affiche le lien vers la page du projet) pour que le serveur de production ait la même auth que votre local.

La walkthrough complète du déploiement — incluant comment câbler les variables d'env dans le dashboard, comment voir les logs, et comment configurer les branch deploys — est dans references/deploy.md.

11. Checklist d'expédition

Avant de déclarer terminé :

  • .env.example liste chaque variable d'env obligatoire avec un court commentaire.
  • openapi.dereferenced.json est dans .gitignore (régénérez depuis openapi.yaml).
  • npm run build passe ; npx tsc --noEmit est propre.
  • L'inspector a exécuté chaque outil auquel vous tenez contre l'API live au moins une fois.
  • L'URL déployée répond à curl https://<nom>.run.mcp-use.com/mcp avec une réponse MCP valide.
  • Committez et poussez.

Matériel de référence critique

Lisez ces éléments quand l'étape pertinente arrive. Chaque fichier est une plongée profonde ciblée — ne les chargez pas tous d'avance.

  • references/mapping-rules.md — Règles de mapping opération-vers-outil : nommage, fusion de paramètres, gestion des réponses, edge cases de schéma (oneOf/anyOf/allOf, nullable, refs récursives), filtrage de grandes specs.
  • references/code-templates.md — Squelettes prêts à copier pour index.ts, src/client.ts, src/auth.ts, src/operations.ts, src/schema.ts, scripts/load-spec.ts, .env.example, et tsconfig.json. Chacun est annoté.
  • references/auth.md — Les quatre schémas d'auth courants (apiKey, http bearer, http basic, OAuth2 bearer) et comment chacun devient un en-tête. Inclut le pattern OAuth-avec-refresh-token.
  • references/testing.md — Recette inspector, le piège du cache périmé .mcp-use, comment forcer-invoquer un outil, ce qu'il faut vérifier dans les réponses d'outil, débogage courant 4xx/5xx.
  • references/deploy.mdmcp-use deploy, configuration GitHub, variables d'env dans le dashboard Manufact, branch deploys, onglets observabilité, et comment installer l'URL déployée comme connecteur MCP personnalisé dans ChatGPT ou Claude.

Mots-clés de déclenchement et alias

Utilisez cette compétence chaque fois que l'utilisateur dit quelque chose dans le cluster :

  • « construire un serveur MCP à partir de cette spec OpenAPI / doc Swagger »
  • « transformer cette API / swagger.json / openapi.yaml en outils MCP »
  • « wrapper [nom API] en tant que serveur MCP »
  • « rendre [Stripe / GitHub / notre API interne] appelable depuis Claude »
  • « exposer ces endpoints à un LLM »
  • « j'ai une spec OpenAPI, générer le serveur MCP »
  • Colle ou attache une spec .yaml / .json et demande une version MCP
  • Décrit une API REST existante (avec une clé, URL de base, ou lien de docs) et veut l'accès LLM

Quand NE PAS utiliser cette compétence

  • L'utilisateur veut une MCP App pilotée par widgets où la plupart/tous les outils affichent une UI personnalisée dans le chat → utiliser build-mcp-app à la place. Cette compétence peut ajouter un widget sur un ou deux outils spécifiques, mais si les widgets sont tout l'intérêt, l'autre compétence est le bon cadre de départ. Confirmez avec l'utilisateur lors de l'étape 1.
  • L'utilisateur n'a pas encore d'API et veut en concevoir une de zéro → c'est une tâche de conception d'API, pas une tâche de wrapping d'MCP.
  • L'utilisateur veut consommer un serveur MCP en tant que client (pas en construire un) → compétence différente / pas celle-ci.
  • La spec est pour un service GraphQL ou gRPC, pas OpenAPI → cette compétence est spécifique à OpenAPI ; les patterns se transfèrent mais le convertisseur de schéma ne le fait pas.

Skills similaires