Comment créer un serveur MCP pour votre produit SaaS en 2025
Si vous avez suivi de près l'espace des outils d'IA en 2025, vous avez probablement remarqué que Model Context Protocol (MCP) est passé de « spec intéressante d'Anthropic » à « chose que tout produit SaaS sérieux doit supporter ». Et c'est justifié. MCP donne aux agents d'IA -- Claude, assistants basés sur GPT, agents personnalisés -- un moyen standardisé de découvrir et d'appeler votre API. Pensez-y comme OpenAPI pour l'ère agentic, mais avec une communication bidirectionnelle en temps réel intégrée.
J'ai passé les derniers mois à construire des serveurs MCP pour plusieurs produits SaaS, et je veux partager ce qui fonctionne vraiment, ce que les docs ne vous disent pas, et les décisions architecturales qui importent. Ce n'est pas un résumé de la spec. C'est le guide que j'aurais aimé avoir quand j'ai commencé.
Table des matières
- Qu'est-ce que le Model Context Protocol (MCP) ?
- Pourquoi votre produit SaaS a besoin d'un serveur MCP
- Architecture MCP : Comment ça marche vraiment
- Configuration de votre projet serveur MCP
- Définir des outils, des ressources et des prompts
- Connexion à votre API SaaS
- Authentification et Multi-locataires
- Gestion des erreurs et validation
- Tester votre serveur MCP
- Déploiement et considérations de production
- Exemple réel : construire un serveur MCP pour un SaaS de gestion de projets
- FAQ
Qu'est-ce que le Model Context Protocol (MCP) ?
MCP est un protocole ouvert créé à l'origine par Anthropic qui définit comment les modèles d'IA communiquent avec des outils et des sources de données externes. Lancé en fin 2024 et atteint la stabilité v1.0 en début 2025, il est maintenant supporté par Claude Desktop, Cursor, Windsurf, le SDK OpenAI Agents, et des dizaines d'autres clients d'IA.
L'idée centrale : au lieu que chaque intégration d'IA soit un plugin personnalisé avec un format propriétaire, MCP fournit un seul protocole qu'n'importe quel client conforme peut utiliser pour découvrir ce que votre service offre et interagir avec lui.
Voici ce que MCP définit :
- Outils -- Des fonctions que l'IA peut appeler (comme
create_ticket,search_users,generate_report) - Ressources -- Des données que l'IA peut lire (comme la documentation, les enregistrements de base de données, les fichiers de configuration)
- Prompts -- Des modèles de prompts réutilisables que votre serveur peut exposer
- Sampling -- La capacité pour votre serveur de demander des complétions LLM au client
La couche de transport utilise JSON-RPC 2.0 sur soit stdio (pour les serveurs locaux), soit HTTP avec Server-Sent Events (SSE) pour les serveurs distants. Le nouveau transport Streamable HTTP, introduit dans la révision de spec 2025-03, est ce que vous voudrez pour n'importe quel déploiement SaaS en production.
Pourquoi votre produit SaaS a besoin d'un serveur MCP
Soyons directs : si votre produit SaaS a une API, vous devriez construire un serveur MCP maintenant. Voici pourquoi.
Les agents d'IA deviennent les principaux consommateurs d'API. Au Q1 2025, Anthropic a rapporté que les utilisateurs de Claude Desktop invoquent des outils MCP plus de 2 millions de fois par jour. Ce nombre grandit rapidement. Vos clients essaient déjà d'utiliser des agents d'IA pour interagir avec votre produit -- la question est de savoir si vous le rendez facile ou frustrant.
C'est un canal de distribution. Quand quelqu'un installe Claude Desktop et tape « help me manage my projects », l'IA peut découvrir et utiliser les serveurs MCP que l'utilisateur a configurés. Votre produit devient accessible via le langage naturel. Ce n'est pas un gimmick -- c'est une véritable nouvelle surface pour votre produit.
Vos concurrents le font. Stripe, Linear, Notion, GitHub, Sentry, et Supabase ont tous lancé des serveurs MCP dans la première moitié de 2025. Si vous êtes en SaaS B2B et que vous n'en avez pas, vous prenez du retard.
| Facteur | API REST seule | API REST + serveur MCP |
|---|---|---|
| Accessibilité pour agent IA | Nécessite une intégration personnalisée par agent | N'importe quel client MCP fonctionne automatiquement |
| Découverte | Les développeurs lisent la doc | L'IA découvre les capacités au runtime |
| Temps jusqu'à première intégration | Heures à jours | Minutes |
| Interaction en langage naturel | Pas possible sans wrapper | Intégrée |
| Charge de maintenance | Une base de code | Deux bases de code (mais MCP enveloppe REST) |
Architecture MCP : Comment ça marche vraiment
Avant d'écrire du code, clarifions l'architecture. Un déploiement MCP a trois parties :
- Client MCP -- L'application d'IA (Claude Desktop, Cursor, votre agent personnalisé). Elle découvre les capacités de votre serveur et les appelle.
- Serveur MCP -- Votre code. Il expose des outils, des ressources et des prompts via le protocole MCP. C'est ce que nous construisons.
- Votre API SaaS -- Le backend réel que votre serveur MCP appelle pour faire les choses.
Le flux ressemble à ceci :
Utilisateur → Client IA → Client MCP → Serveur MCP → Votre API SaaS
↓
La réponse revient
Votre serveur MCP est essentiellement un adaptateur de protocole. Il traduit entre le protocole MCP (que parlent les clients d'IA) et votre API REST/GraphQL existante. Cela signifie que vous n'avez pas besoin de modifier votre API existante du tout. Le serveur MCP s'asoit à côté d'elle.
Options de transport
Pour les produits SaaS, vous avez deux options de transport réalistes :
- Streamable HTTP (recommandé) : Utilise des requêtes HTTP standard avec SSE optionnel pour le streaming. Fonctionne derrière les load balancers, les CDNs et l'infrastructure standard. C'est ce que vous voulez pour les serveurs MCP distants/hébergés.
- SSE (hérité) : Le transport distant original. Fonctionne toujours mais la spec recommande Streamable HTTP pour les nouvelles implémentations.
Le transport stdio est idéal pour les outils locaux mais ne s'applique pas au serveur MCP d'un produit SaaS.
Configuration de votre projet serveur MCP
Construisons cela avec TypeScript. Le package officiel @modelcontextprotocol/sdk est bien maintenu et est le bon choix pour une utilisation en production. Python a mcp (le SDK Python officiel) si c'est votre stack, mais je vais me concentrer sur TypeScript puisque la plupart des backends SaaS avec lesquels je travaille utilisent Node.js.
mkdir my-saas-mcp-server
cd my-saas-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod express
npm install -D typescript @types/node @types/express tsx
Configurez votre tsconfig.json :
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
Maintenant, créons la structure de serveur de base :
// src/server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import express from "express";
const app = express();
app.use(express.json());
const server = new McpServer({
name: "my-saas-mcp",
version: "1.0.0",
description: "MCP server for My SaaS Product",
});
// Nous allons ajouter des outils, des ressources et des prompts ici
app.post("/mcp", async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => crypto.randomUUID(),
});
await server.connect(transport);
await transport.handleRequest(req, res);
});
app.get("/mcp", async (req, res) => {
// Point de terminaison SSE pour les réponses en streaming
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => crypto.randomUUID(),
});
await server.connect(transport);
await transport.handleRequest(req, res);
});
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`MCP server running on port ${PORT}`);
});
C'est votre squelette. Remplissons-le.
Définir des outils, des ressources et des prompts
Outils
Les outils sont la primitive la plus importante. Chaque outil est une fonction que l'IA peut appeler avec des paramètres structurés. Voici comment en définir un :
import { z } from "zod";
server.tool(
"create_project",
"Create a new project in the workspace",
{
name: z.string().describe("The project name"),
description: z.string().optional().describe("Project description"),
team_id: z.string().describe("ID of the team that owns this project"),
},
async ({ name, description, team_id }) => {
// Appelez votre API SaaS ici
const project = await apiClient.createProject({ name, description, team_id });
return {
content: [
{
type: "text",
text: JSON.stringify(project, null, 2),
},
],
};
}
);
Quelques choses que j'ai apprises sur la conception des outils :
Soyez spécifique avec les descriptions. L'IA utilise votre description d'outil et les descriptions de paramètres pour décider quand et comment l'appeler. Des descriptions vagues mènent à une mauvaise sélection d'outil. « Create a new project » c'est okay. « Create a new project in the user's workspace. Requires a team_id which can be obtained from the list_teams tool » c'est bien mieux.
Gardez le nombre d'outils gérable. J'ai vu des gens exposer 50+ outils et se demander pourquoi l'IA se confond. Commencez avec 10-15 opérations principales. Vous pouvez toujours en ajouter plus.
Retournez des données structurées. Retournez toujours du JSON dans votre contenu texte. L'IA le parse mieux que de la prose.
Ressources
Les ressources exposent des données lisibles. Pensez-y comme des endpoints GET pour la consommation par l'IA :
server.resource(
"project-list",
"projects://list",
"List of all projects in the workspace",
async () => {
const projects = await apiClient.listProjects();
return {
contents: [
{
uri: "projects://list",
mimeType: "application/json",
text: JSON.stringify(projects, null, 2),
},
],
};
}
);
Prompts
Les prompts sont des modèles réutilisables. Ils sont sous-utilisés mais puissants :
server.prompt(
"weekly-report",
"Generate a weekly status report for a project",
{
project_id: z.string().describe("The project ID to report on"),
},
async ({ project_id }) => {
const stats = await apiClient.getProjectStats(project_id);
return {
messages: [
{
role: "user",
content: {
type: "text",
text: `Generate a weekly status report based on this data: ${JSON.stringify(stats)}. Include completed tasks, blockers, and upcoming deadlines.`,
},
},
],
};
}
);
Connexion à votre API SaaS
Votre serveur MCP doit parler à votre API existante. Je recommande de créer une classe de client API typée :
// src/api-client.ts
class SaaSApiClient {
private baseUrl: string;
private apiKey: string;
constructor(baseUrl: string, apiKey: string) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
private async request<T>(path: string, options?: RequestInit): Promise<T> {
const response = await fetch(`${this.baseUrl}${path}`, {
...options,
headers: {
"Authorization": `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
...options?.headers,
},
});
if (!response.ok) {
const error = await response.text();
throw new Error(`API error ${response.status}: ${error}`);
}
return response.json() as T;
}
async createProject(data: CreateProjectInput): Promise<Project> {
return this.request<Project>("/api/v1/projects", {
method: "POST",
body: JSON.stringify(data),
});
}
async listProjects(): Promise<Project[]> {
return this.request<Project[]>("/api/v1/projects");
}
// ... plus de méthodes
}
Gardez ce client mince. C'est un pass-through, pas une couche de logique métier.
Authentification et Multi-locataires
C'est là que les choses deviennent intéressantes -- et où la plupart des tutoriels glossent sur les parties difficiles.
Votre serveur MCP doit authentifier les demandes de deux façons :
- Le client MCP s'authentifiant auprès de votre serveur MCP (« est-ce un utilisateur valide ? »)
- Votre serveur MCP s'authentifiant auprès de votre API SaaS (« agissez au nom de cet utilisateur »)
OAuth 2.0 (Recommandé pour la production)
La spec MCP inclut un framework d'autorisation basé sur OAuth 2.1. Pour un produit SaaS, c'est la bonne approche :
// Middleware pour extraire et valider le token OAuth
app.use("/mcp", async (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith("Bearer ")) {
return res.status(401).json({ error: "Missing authorization" });
}
const token = authHeader.slice(7);
try {
const user = await validateOAuthToken(token);
req.user = user;
next();
} catch {
return res.status(401).json({ error: "Invalid token" });
}
});
Puis passez le contexte utilisateur dans vos gestionnaires d'outils. C'est critique pour le multi-locataire -- chaque appel API doit être scoped à l'espace de travail de l'utilisateur authentifié.
Approche par clé API (Plus simple, pour interne/phase précoce)
Si vous êtes en phase précoce ou que c'est interne uniquement, les clés API fonctionnent bien :
const apiKey = req.headers["x-api-key"] as string;
const client = new SaaSApiClient(process.env.API_BASE_URL!, apiKey);
L'utilisateur fournit sa clé API quand il configure le serveur MCP dans son client, et elle est passée à travers à votre API.
Gestion des erreurs et validation
Les agents d'IA ont besoin de messages d'erreur clairs. Quand un outil échoue, l'IA doit comprendre pourquoi afin qu'elle puisse soit fixer l'entrée soit expliquer le problème à l'utilisateur.
server.tool(
"get_project",
"Get project details by ID",
{ project_id: z.string().uuid().describe("The project's UUID") },
async ({ project_id }) => {
try {
const project = await apiClient.getProject(project_id);
return {
content: [{ type: "text", text: JSON.stringify(project, null, 2) }],
};
} catch (error) {
const message = error instanceof Error ? error.message : "Unknown error";
return {
isError: true,
content: [
{
type: "text",
text: `Failed to get project: ${message}. Make sure the project_id is a valid UUID and the project exists in your workspace.`,
},
],
};
}
}
);
Remarquez le flag isError: true. Cela dit au client MCP que l'appel d'outil a échoué, afin qu'il puisse le gérer correctement. Incluez toujours des conseils exploitables dans les messages d'erreur.
Tester votre serveur MCP
Tester les serveurs MCP est devenu beaucoup plus facile en 2025. Voici vos options :
Inspecteur MCP
L'inspecteur MCP officiel est votre meilleur ami pendant le développement :
npx @modelcontextprotocol/inspector
Cela vous donne une interface web où vous pouvez vous connecter à votre serveur, parcourir les outils/ressources, et les invoquer interactivement. Utilisez-le constamment.
Tests automatisés
Pour le CI/CD, testez vos outils comme des fonctions async régulières :
import { describe, it, expect } from "vitest";
describe("create_project tool", () => {
it("should create a project with valid input", async () => {
const result = await createProjectHandler({
name: "Test Project",
team_id: "team-123",
});
expect(result.isError).toBeUndefined();
const data = JSON.parse(result.content[0].text);
expect(data.name).toBe("Test Project");
});
it("should return error for missing team_id", async () => {
// La validation Zod devrait attraper cela avant que le gestionnaire s'exécute
// Testez la couche de validation
});
});
Tests d'intégration avec Claude Desktop
Une fois que votre serveur tourne, ajoutez-le à la config de Claude Desktop :
{
"mcpServers": {
"my-saas": {
"url": "http://localhost:3001/mcp",
"headers": {
"Authorization": "Bearer your-test-token"
}
}
}
}
Puis simplement discutez avec Claude et essayez d'utiliser vos outils naturellement. Vous trouverez rapidement les cas limites que les tests automatisés manquent.
Déploiement et considérations de production
Où déployer
Votre serveur MCP est juste une app Express. Déployez-le où vous déployez les services Node.js. Quelques bonnes options :
| Plateforme | Cold Start | Coût (est.) | Meilleur pour |
|---|---|---|---|
| Railway | Aucun | ~$5-20/mo | SaaS petite-moyenne |
| Fly.io | <500ms | ~$5-15/mo | Distribution globale |
| AWS ECS/Fargate | Aucun | ~$15-50/mo | Enterprise, AWS existant |
| Vercel (Edge) | <100ms | $0-20/mo | Si vous êtes déjà sur Vercel |
| Cloudflare Workers | <5ms | $0-5/mo | Critique pour la performance |
Limitation de débit
Les agents d'IA peuvent être bavards. Une seule conversation utilisateur pourrait déclencher 20-30 appels d'outil. Implémentez une limitation de débit qui soit assez généreuse pour l'utilisation normale par l'IA mais prévient les abus :
import rateLimit from "express-rate-limit";
const limiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 60, // 60 requêtes par minute par utilisateur
keyGenerator: (req) => req.user?.id || req.ip,
});
app.use("/mcp", limiter);
Monitoring
Enregistrez chaque invocation d'outil avec l'ID utilisateur, le nom de l'outil, et la latence. Vous voulez de la visibilité sur quels outils sont utilisés le plus, lesquels échouent le plus, et où sont les goulots d'étranglement de latence. Datadog, Axiom, ou même des logs JSON structurés à CloudWatch fonctionnent bien.
Versioning
Votre serveur MCP évoluera. Utilisez le field version dans les métadonnées de votre serveur et envisagez de lancer plusieurs versions derrière un préfixe de chemin (/mcp/v1, /mcp/v2) durant les transitions.
Exemple réel : construire un serveur MCP pour un SaaS de gestion de projets
Parcourons un exemple réel. Disons que vous construisez un serveur MCP pour un outil de gestion de projets (pensez à un Linear ou Asana simplifié).
Voici l'ensemble d'outils que j'exposerais :
// Outils CRUD principaux
server.tool("list_projects", ...);
server.tool("get_project", ...);
server.tool("create_project", ...);
server.tool("update_project", ...);
// Gestion des tâches
server.tool("list_tasks", ...); // avec filtres pour statut, assigné, projet
server.tool("create_task", ...);
server.tool("update_task", ...); // mettre à jour statut, assigné, priorité
server.tool("add_comment", ...);
// Recherche et reporting
server.tool("search", ...); // recherche fulltext sur projets et tâches
server.tool("get_project_stats", ...); // stats récapitulatives pour un projet
// Ressources
server.resource("workspace-info", ...); // config d'espace de travail, membres de l'équipe
// Prompts
server.prompt("standup-report", ...); // générer un standup d'une activité récente
server.prompt("sprint-planning", ...); // aider à planifier un sprint
C'est 12 outils, 1 ressource, et 2 prompts. Assez pour être genuinely utile sans surcharger la sélection d'outils de l'IA.
L'expérience utilisateur ressemble à ceci : quelqu'un ouvre Claude Desktop et dit « What tasks are overdue in the Backend Rewrite project? » Claude appelle list_tasks avec un filtre de statut et un nom de projet, obtient les résultats, et les présente en langage naturel. L'utilisateur dit « Assign the auth migration task to Sarah and bump it to high priority. » Claude appelle update_task. Ça paraît magique, et c'est vraiment juste de la plomberie de protocole.
Si vous construisez quelque chose comme ça et voulez de l'aide avec le frontend Next.js ou la couche CMS headless qui accompagne souvent ces projets, c'est quelque chose que nous faisons beaucoup chez Social Animal. Mais le serveur MCP lui-même ? C'est quelque chose que vous pouvez absolument construire en interne avec ce guide.
FAQ
Quelle est la différence entre MCP et function calling?
Function calling (comme le function calling d'OpenAI ou le tool use de Claude) est la façon dont un modèle d'IA décide d'invoquer une fonction dans un seul appel API. MCP est le protocole qui permet à un client IA de découvrir quelles fonctions sont disponibles à partir de serveurs externes. Ils travaillent ensemble -- le client IA utilise function calling en interne pour décider quand invoquer un outil MCP. Pensez à MCP comme la plomberie entre systèmes, et function calling comme le processus de prise de décision du modèle.
Combien coûte-t-il de construire et faire tourner un serveur MCP?
Le serveur lui-même est léger. Pour un produit SaaS typique avec 10-20 outils, vous regardez quelques centaines de lignes de TypeScript. Les coûts d'hébergement $5-50/mois dépendant de votre trafic et de votre plateforme. Le coût réel est le temps des développeurs -- budgétisez 2-4 semaines pour un serveur MCP de qualité production avec auth, gestion des erreurs, monitoring et tests. Si cela semble beaucoup, nous avons aidé des équipes à les livrer plus vite. Vérifiez notre page de pricing pour les détails.
Puis-je utiliser Python à la place de TypeScript?
Absolument. Le SDK Python officiel (pip install mcp) est excellent et a probablement une meilleure ergonomie pour les définitions d'outils. Utilisez ce que votre équipe connait. Le protocole est agnostique quant à la langue. Si votre backend SaaS est Python (Django, FastAPI), construire le serveur MCP en Python a encore plus de sens puisque vous pouvez partager des modèles et de la logique de validation.
Dois-je modifier mon API existante?
Non. Votre serveur MCP est un service séparé qui appelle votre API existante. C'est une couche adaptateur. Cela dit, vous pourriez vous retrouver voulant ajouter quelques endpoints API spécifiquement pour la consommation par l'IA -- comme un endpoint de recherche qui retourne plus de contexte que votre interface utilisateur n'en a besoin. C'est bon. Mais c'est additif, pas une modification.
Comment je gère les opérations de longue durée?
Certains outils pourraient déclencher des opérations qui prennent des minutes (comme générer un rapport ou traiter un gros import). Utilisez la feature de notification de progrès de MCP pour garder le client informé. Votre gestionnaire d'outil peut émettre des mises à jour de progrès mientras attend que l'opération se complète. Pour les très longues opérations (>30 secondes), envisagez de retourner immédiatement avec un job ID et fournir un outil check_job_status séparé.
MCP est-il assez stable pour la production?
Oui, depuis le milieu 2025. La spec a atteint v1.0 en mars 2025, et la révision 2025-03-26 (qui a ajouté Streamable HTTP) est ce que les clients majeurs ont adoptés. Anthropic, Microsoft, Google, et OpenAI investissent tous dans le protocole. Il ne disparaîtra pas. Cela dit, gardez un œil sur les mises à jour de spec -- il y a des propositions actives autour de meilleurs flows d'auth et de communication serveur-à-serveur.
Quelle est la meilleure façon de gérer la pagination dans les outils MCP?
Retournez une page raisonnable de résultats (20-50 items) par défaut, et acceptez des paramètres cursor ou page. Incluez les métadonnées de pagination dans votre réponse afin que l'IA sache qu'il y a plus de résultats. Quelque chose comme : { results: [...], next_cursor: "abc123", total_count: 342 }. L'IA demandera naturellement la page suivante si l'utilisateur a besoin de plus de données.
Un seul serveur MCP peut-il supporter plusieurs clients d'IA simultanément?
Oui, et il le devrait. Votre serveur MCP est juste un serveur HTTP gérant des requêtes concurrentes. Chaque requête inclut le token d'auth de l'utilisateur, donc vous scoped tout au bon tenant. Il n'y a pas d'état spécifique au client à vous faire du souci si vous utilisez le transport Streamable HTTP correctement. Traitez-le comme n'importe quel autre serveur API sans état.