@vantageos/backend-doctor 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +94 -0
- package/dist/contract.d.ts +18 -0
- package/dist/contract.js +29 -0
- package/dist/coverage-cli.d.ts +2 -0
- package/dist/coverage-cli.js +17 -0
- package/dist/coverage.d.ts +32 -0
- package/dist/coverage.js +77 -0
- package/dist/detectors/oracle-axis1.d.ts +86 -0
- package/dist/detectors/oracle-axis1.js +221 -0
- package/dist/detectors/oracle-axis2.d.ts +22 -0
- package/dist/detectors/oracle-axis2.js +149 -0
- package/dist/detectors/predicates.d.ts +19 -0
- package/dist/detectors/predicates.js +334 -0
- package/dist/detectors/shared.d.ts +56 -0
- package/dist/detectors/shared.js +103 -0
- package/dist/detectors/source/_scan.d.ts +43 -0
- package/dist/detectors/source/_scan.js +82 -0
- package/dist/detectors/source/backfill.d.ts +27 -0
- package/dist/detectors/source/backfill.js +211 -0
- package/dist/detectors/source/contract.d.ts +33 -0
- package/dist/detectors/source/contract.js +385 -0
- package/dist/detectors/source/identity.d.ts +47 -0
- package/dist/detectors/source/identity.js +410 -0
- package/dist/detectors/source/jobs.d.ts +10 -0
- package/dist/detectors/source/jobs.js +209 -0
- package/dist/detectors/source/lifecycle.d.ts +6 -0
- package/dist/detectors/source/lifecycle.js +147 -0
- package/dist/detectors/source/readbudget.d.ts +6 -0
- package/dist/detectors/source/readbudget.js +133 -0
- package/dist/detectors/source/refusal.d.ts +6 -0
- package/dist/detectors/source/refusal.js +125 -0
- package/dist/detectors/source/security.d.ts +37 -0
- package/dist/detectors/source/security.js +392 -0
- package/dist/detectors/source/writesafety.d.ts +14 -0
- package/dist/detectors/source/writesafety.js +345 -0
- package/dist/detectors/standard/_doc.d.ts +66 -0
- package/dist/detectors/standard/_doc.js +152 -0
- package/dist/detectors/standard/r41-frontier.d.ts +58 -0
- package/dist/detectors/standard/r41-frontier.js +176 -0
- package/dist/detectors/standard/r46-returns-validator.d.ts +61 -0
- package/dist/detectors/standard/r46-returns-validator.js +474 -0
- package/dist/detectors/standard/standard-doc.d.ts +12 -0
- package/dist/detectors/standard/standard-doc.js +271 -0
- package/dist/detectors/structural.d.ts +14 -0
- package/dist/detectors/structural.js +83 -0
- package/dist/engine-types.d.ts +10 -0
- package/dist/engine-types.js +1 -0
- package/dist/not-checkable.d.ts +22 -0
- package/dist/not-checkable.js +37 -0
- package/dist/oracle.d.ts +33 -0
- package/dist/oracle.js +133 -0
- package/dist/registry.d.ts +11 -0
- package/dist/registry.js +140 -0
- package/dist/report.d.ts +35 -0
- package/dist/report.js +138 -0
- package/dist/severity.d.ts +30 -0
- package/dist/severity.js +121 -0
- package/dist/tally.d.ts +57 -0
- package/dist/tally.js +152 -0
- package/dist/trigger.d.ts +47 -0
- package/dist/trigger.js +143 -0
- package/fixtures/backend-standard-51ec7af1.md +648 -0
- package/fixtures/backend-standard-a9d7877b.md +629 -0
- package/fixtures/mcp-app-standard-a74a042b.md +279 -0
- package/fixtures/mcp-extension-standard-a74a042b.md +259 -0
- package/fixtures/mcp-server-standard-a74a042b.md +434 -0
- package/fixtures/mcp-standard-a74a042b.md +354 -0
- package/fixtures/vp-1200-green/.pin +1 -0
- package/fixtures/vp-1200-green/convex/migrations.ts +350 -0
- package/fixtures/vp-1200-green/convex/schema.ts +1243 -0
- package/fixtures/vp-1200-red/.pin +1 -0
- package/fixtures/vp-1200-red/convex/migrations.ts +239 -0
- package/fixtures/vp-1200-red/convex/schema.ts +1243 -0
- package/fixtures/vp-by-tool-day158.csv +190 -0
- package/fixtures/vp-convex/.github/workflows/npm-publish.yml +163 -0
- package/fixtures/vp-convex/.pin +1 -0
- package/fixtures/vp-convex/bun.lock +783 -0
- package/fixtures/vp-convex/convex/auth.config.ts +8 -0
- package/fixtures/vp-convex/convex/briefingNotes.ts +321 -0
- package/fixtures/vp-convex/convex/convex.config.ts +14 -0
- package/fixtures/vp-convex/convex/credentials.ts +591 -0
- package/fixtures/vp-convex/convex/crons.ts +72 -0
- package/fixtures/vp-convex/convex/dashboard.ts +199 -0
- package/fixtures/vp-convex/convex/gumroadWebhook.ts +815 -0
- package/fixtures/vp-convex/convex/issues.ts +544 -0
- package/fixtures/vp-convex/convex/kb.ts +305 -0
- package/fixtures/vp-convex/convex/kbMutations.ts +192 -0
- package/fixtures/vp-convex/convex/kbShared.ts +56 -0
- package/fixtures/vp-convex/convex/licenses.ts +443 -0
- package/fixtures/vp-convex/convex/memories.ts +406 -0
- package/fixtures/vp-convex/convex/memoriesScoped.ts +220 -0
- package/fixtures/vp-convex/convex/messages.ts +971 -0
- package/fixtures/vp-convex/convex/migrations.ts +239 -0
- package/fixtures/vp-convex/convex/missionTemplates.ts +369 -0
- package/fixtures/vp-convex/convex/oauth.ts +1608 -0
- package/fixtures/vp-convex/convex/oauthMigrations.ts +96 -0
- package/fixtures/vp-convex/convex/okfBundleNode.ts +1031 -0
- package/fixtures/vp-convex/convex/recurringTasks.ts +386 -0
- package/fixtures/vp-convex/convex/schema.ts +1229 -0
- package/fixtures/vp-convex/convex/tasks.ts +2782 -0
- package/fixtures/vp-r31-green/.pin +1 -0
- package/fixtures/vp-r31-green/convex/migrations.ts +350 -0
- package/fixtures/vp-r31-red/.pin +1 -0
- package/fixtures/vp-r31-red/convex/migrations.ts +286 -0
- package/fixtures/vr-by-tool-day159.csv +89 -0
- package/package.json +39 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
# MCP Server Standard — Complément serveur
|
|
2
|
+
|
|
3
|
+
Version : v2.2 — 2026-08-16. Protocole visé `2026-07-28`, plancher `2025-06-18`. Table de détecteurs complète S-1..S-27.
|
|
4
|
+
Owner : gamma
|
|
5
|
+
Status : en vigueur — non déclaré fini tant que l'opérateur ne l'a pas dit.
|
|
6
|
+
Référence parent : `resources/references/mcp-standard.md` (le tronc).
|
|
7
|
+
|
|
8
|
+
Source canonique : `resources/references/mcp-server-standard.md`. **Ce document est un complément.** Il ne porte QUE ce qui est propre à un serveur MCP — transport, forme protocolaire des résultats, découverte de capacités, autorisation, cohérence de cache, version observable. Tout le reste — bilinguisme, nommage, SemVer, schémas, évaluations, autorité/verbe/mécanisme/pagination déclarés, doublons, plafond de contexte, inscription au catalogue — vit dans le tronc (règles `C-*`) et ne se réécrit jamais ici.
|
|
9
|
+
|
|
10
|
+
**Ce que gouverne ce document** : un serveur MCP, c'est-à-dire un produit qui expose un sous-domaine fonctionnel via le Model Context Protocol, fonctionne hors de notre pile, et s'installe en une commande sur tout client conforme. **Où passe la frontière** : ce qui se lit sur la surface servie (déclaration d'autorité, de verbe, de mécanisme, de pagination) est au tronc ; ce qui se lit sur la couche données (application de ces mêmes noms) est au standard backend ; ce qui se lit sur la réponse du transport et l'échange d'autorisation est ici.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Règles propres au serveur (`S-1` à `S-7`)
|
|
15
|
+
|
|
16
|
+
1. **`S-1` — stdio toujours supporté ; HTTP en complément, jamais en remplacement.** Tout serveur publié sur le marketplace DOIT supporter stdio (cible : Claude Code, Cursor, VS Code, Goose). HTTP est additionnel, pour les intégrations remote et multi-tenant. Un transport adapter Streamable HTTP ne s'appuie plus sur `Mcp-Session-Id` ni sur un état par connexion (délta `2026-07-28`, SEP-2567/2575) — c'est la propriété que garde le détecteur `D2`, sans être une règle distincte.
|
|
17
|
+
2. **`S-2` — `resultType` obligatoire sur toute réponse renvoyée** (protocole `2026-07-28`). Toute réponse sur le transport (`CallToolResult`, `ReadResourceResult`) porte explicitement `resultType: "complete"` (ou `"input_required"` pour un round MRTR). La propriété se vérifie **sur la réponse**, jamais sur l'existence d'un builder écrit à la main : peu importe qui pose le champ — l'auteur, un helper, le SDK. Le comportement client « champ absent = complete » est une grâce de migration pour serveurs pré-`2026-07-28`, jamais un bouclier permanent.
|
|
18
|
+
3. **`S-3` — `CacheableResult` (`ttlMs` + `cacheScope`) obligatoire sur les 5 réponses list/read** : `tools/list`, `prompts/list`, `resources/list`, `resources/read`, `resources/templates/list`. Comme `S-2`, la propriété se vérifie **sur la réponse renvoyée**, pas sur la forme du handler. Mirroré dans `convex/schema.ts` pour tout serveur à backend Convex-style (doctrine schema-mirror) — le jeu de champs ne dérive jamais entre le contrat de fil et le schéma persisté.
|
|
19
|
+
4. **`S-4` — `server/discover` implémenté et non optionnel** (protocole `2026-07-28`). Tout serveur ciblant `minProtocolVersion >= 2026-07-28` DOIT répondre `DiscoverResult extends CacheableResult` avec `supportedVersions`, `capabilities`, `instructions?`. Absence = non-conformité protocolaire, pas une feature en attente. `DiscoverResult` étend `CacheableResult` : le handler pose aussi `ttlMs`/`cacheScope` et `resultType` (voir `S-2`/`S-3`).
|
|
20
|
+
5. **`S-5` — OAuth 2.1 resource server obligatoire pour tout serveur multi-tenant / multi-client.** Dans l'ordre : (a) challenge `401` + `WWW-Authenticate: Bearer realm="mcp", resource_metadata="..."` ; (b) endpoint Protected Resource Metadata (RFC 9728, `/.well-known/oauth-protected-resource`) ; (c) discovery de l'Authorization Server (OIDC / RFC 8414) pointant vers **Clerk** (Clerk-as-AS, jamais un IdP bespoke) ; (d) vérification cryptographique de la signature du JWT (RS256/JWKS — une comparaison de shared secret ne vérifie rien) ; (e) liaison `aud` / issuer pour bloquer le replay cross-tenant. Le SEUL code neuf autorisé est cette façade resource-server ; tout ce qui est en aval de la vérification réutilise `@vantageos/cloud-identity@0.3.0` SANS MODIFICATION (doctrine `one-identity-layer`). Absence d'un des cinq éléments = non-conformité.
|
|
21
|
+
6. **`S-6` — Cohérence de cache sur `tools/list`.** `S-3` exige la PRÉSENCE de `ttlMs`/`cacheScope` ; celle-ci exige leur COHÉRENCE. Tout chemin de code qui modifie l'ensemble des tools enregistrés (ajout, retrait, changement de schéma) émet `notifications/tools/list_changed`, et le `ttlMs` servi borne réellement la fraîcheur. **Anti-règle** : ne PAS gonfler `ttlMs` pour éviter d'émettre `list_changed` ; ne PAS servir en `cacheScope: "public"` un `tools/list` dont la visibilité dépend du tenant ou de l'identité. Tracé `MCP-R1` (`j57d4abk26wm0yh5n7mtzjjw318cb2jv`).
|
|
22
|
+
7. **`S-7` — Version observable sur la surface de découverte / santé.** La réponse `server/discover` (identité) — et, en mode HTTP, un endpoint `/health` non authentifié — expose un champ `version` ÉGAL à celui de `package.json`, atteignable **avant** toute authentification. Ainsi l'activation par version se prouve depuis ce que le serveur RETOURNE, jamais depuis la sortie de la commande de déploiement — la doctrine *publié ≠ livré* rendue vérifiable de l'extérieur. **Anti-règle** : « observable » signifie la version et RIEN d'autre — pas un secret de build, pas un SHA d'infra privée ; et ne PAS placer la version derrière l'auth. Tracé `MCP-R2` (`j570vhqjmjtwd5m5eze9758v6d8cffk4`).
|
|
23
|
+
|
|
24
|
+
### Seconde source — pratique établie MCP au-delà de nos produits
|
|
25
|
+
|
|
26
|
+
Les règles `S-1` à `S-7` dérivaient du délta de spec et de nos produits. Les dix suivantes reposent sur la **spec MCP officielle et sa page Security Best Practices, citées** — pour des sujets d'exécution, de protocole et d'autorisation propres au serveur (une App EST un serveur : elle en hérite ; les outils d'une tool-extension s'exécutent dans le serveur hôte qui suit ce complément). Chaque règle porte sa source. Cadre : `2026-07-28` est une quasi-réécriture, pas un délta additif — plusieurs sujets diffèrent entre le plancher `2025-06-18` et la version de record, et la règle nomme la version qu'elle décrit.
|
|
27
|
+
|
|
28
|
+
8. **`S-8` — Aucun passthrough de jeton ; audience validée à l'entrée.** Le serveur n'accepte JAMAIS un jeton qui ne lui a pas été explicitement délivré (validation `aud` en garde de pré-requête, pas un middleware optionnel), et ne transmet JAMAIS tel quel le jeton du client vers une API amont — il utilise son propre jeton amont. La moitié couche données — le backend valide l'audience DANS l'accès et n'emploie que son propre credential scopé au callee — est **`R-34`** du standard backend ; `S-8` garde la façade servie, `R-34` l'applique dans la requête, chaque côté nomme l'autre (tronc §7). *(Source : spec MCP 2025-06-18 `basic/security_best_practices` §Token Passthrough, `basic/authorization` §Access Token Privilege Restriction — MUST.)*
|
|
29
|
+
9. **`S-9` — Député confus sur un serveur proxy — page de consentement complète.** Un serveur qui relaie une API tierce protégée par OAuth obtient un **consentement par client** avant de relayer vers l'AS amont. La page de consentement (servie par nous) **identifie le client par son nom**, montre les **scopes tiers** demandés et l'**URI de redirection enregistrée**, porte une protection CSRF et `frame-ancestors 'none'` / `X-Frame-Options: DENY` contre le clickjacking. Les cookies de consentement portent le préfixe **`__Host-`** et sont `Secure`, `HttpOnly`, `SameSite=Lax`, signés ou côté serveur, **liés au `client_id`**. Le registre de consentement par client est consulté ET persisté **AVANT tout relais vers l'AS amont** — jamais un contrôle situé quelque part dans le flux après la redirection. Les endpoints de l'AS et toute `redirect_uri` sont en **`https`** (le loopback `localhost`/`127.0.0.1`/`::1` excepté en dev) ; `redirect_uri` est comparé par **chaîne exacte** — jamais un motif ni un joker. `state` est **cryptographiquement aléatoire**, posé juste avant la redirection, validé au callback, à **usage unique** et à **courte expiration**. *(Source : spec MCP 2025-06-18 `basic/security_best_practices` §Confused Deputy Problem, `basic/authorization` §Security Considerations + §Communication Security — MUST. Serveurs proxy uniquement ; toute clause qui relèverait d'un client hôte serait nommée telle.)*
|
|
30
|
+
10. **`S-10` — Garde SSRF — la règle entière, union des deux sources.** Tout composant qui déréférence une URL vers une destination influencée par un tiers (métadonnées OAuth `resource_metadata` / `authorization_servers` / `token_endpoint`, et tout fetch sortant) applique l'ensemble suivant :
|
|
31
|
+
- **Deux cas.** Destinations connues → **allowlist, obligatoire**. Destinations arbitraires → blocklist, plus faible par nature. La découverte de métadonnées OAuth est un cas allowlist : l'ensemble des AS acceptés est connu. Le cas blocklist est une **exception portant une justification écrite** ; l'allowlist est forcée partout où l'ensemble des destinations est connaissable.
|
|
32
|
+
- **Aucune URL complète reçue de l'appelant.** On accepte une **IP ou un domaine validés via un paramètre dédié**, et on reconstruit l'URL soi-même — une URL complète est difficile à valider et son parseur s'abuse. (La ligne la plus porteuse de la page.)
|
|
33
|
+
- **Aucune validation d'adresse écrite à la main.** Une bibliothèque éprouvée (Apache Commons Validator `InetAddressValidator`/`DomainValidator` ; .NET `IPAddress.TryParse`/`Uri.CheckHostName` ; JS `ip-address`/`is-valid-domain` ; Ruby `IPAddr` ; Python `validators.domain`), en sachant qu'une bibliothèque nommée se contourne quand même (le `TryParse` .NET est exposé aux encodages hex, octal, Dword, mixte). Mitigation : **on compare la SORTIE de la bibliothèque à l'allowlist, jamais l'entrée brute.** C'est l'allowlist qui porte la sûreté, pas le parseur.
|
|
34
|
+
- **Encodages qui trompent un parseur naïf**, nommés pour que personne ne réinvente une regex : hexadécimal, octal, Dword, mixte (nommés par OWASP) ; notation décimale (`2130706433` = `127.0.0.1`) et IPv6 mappé-IPv4 (nommés dans la source spec/RFC).
|
|
35
|
+
- **Blocklist minimale, union des deux sources.** Métadonnées cloud `169.254.169.254`, `metadata.amazonaws.com`, `metadata.google.internal` ; réservées `127.0.0.0/8`, `0.0.0.0/8`, `::1/128`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16` ; link-local `169.254.0.0/16` et `fe80::/10` ; unique-local IPv6 `fc00::/7` ; multicast `224.0.0.0/4` et `ff00::/8`. **Répartition entre sources :** la page spec (RFC 9728 §7.7) porte `169.254.0.0/16`, `fc00::/7`, `fe80::/10` ; OWASP porte `0.0.0.0/8` et les plages multicast. Aucune source n'est interchangeable — la règle prend leur **union**.
|
|
36
|
+
- **Résoudre, puis vérifier les adresses résolues.** TOUS les enregistrements A et AAAA vérifiés publics — pas le premier, tous — via un résolveur interne qui renvoie not-found pour un domaine externe légitime.
|
|
37
|
+
- **Redirections désactivées.** Une adresse validée qui redirige est un contournement.
|
|
38
|
+
- **Allowlist de protocole** : `http` ou `https` seulement, via le paramètre dédié validé.
|
|
39
|
+
- **DNS rebinding.** Épinglage DNS entre la vérification et l'usage, plus surveillance de l'allowlist pour un domaine qui se met à résoudre vers une adresse interne — ce changement de résolution est lui-même le signal d'attaque, et quelque chose doit le guetter.
|
|
40
|
+
- **Couche réseau (non optionnelle).** Politique d'egress / segmentation réseau pour qu'un appel illégitime meure au réseau et non dans le code applicatif. Détenteur nommé : la plateforme de déploiement (Railway / VPS / CDN) ; pour un serveur publié dont nous ne tenons pas le réseau, cette couche est écrite hors périmètre en nommant son détenteur.
|
|
41
|
+
- **Jeton de légitimité** (aléatoire, 20 caractères alphanumériques) entre nos propres services : recommandé quand l'appel est interne à notre flotte ; hors périmètre quand la cible est tierce.
|
|
42
|
+
- **Moitié couche données.** Le fetch sortant appliqué DANS le code d'accès (webhook stocké en ligne, corpus tiré d'un lien client, métadonnées OAuth déréférencées) est **`R-33`** du standard backend — `S-10` déclare la garde sur la surface serveur, `R-33` l'exécute dans la requête ; chaque côté nomme l'autre (tronc §7).
|
|
43
|
+
|
|
44
|
+
*(Sources : spec MCP 2025-06-18 `basic/security_best_practices` §Server-Side Request Forgery + RFC 9728 §7.7 — **force SHOULD côté spec** ; **OWASP SSRF Prevention Cheat Sheet, lu en entier** — opérationnel. **Ce standard élève l'ensemble à obligatoire pour nos produits** : allowlist forcée, aucune URL complète de l'appelant, résolution vérifiée sur tous les enregistrements, redirections désactivées. C'est un dépassement de marché assumé, la force de la source est nommée telle.)*
|
|
45
|
+
11. **`S-11` — Contrat de résultat : contenu structuré et erreurs distinguées.** Un outil renvoyant une donnée machine déclare son `outputSchema` et y conforme sa sortie structurée, avec un bloc texte de repli pour les clients non structurés. Une erreur d'**exécution** de l'outil se rapporte via `isError: true` dans un résultat normal ; une erreur de **protocole** (outil inconnu, args invalides) via une erreur JSON-RPC à code entier. Un code neuf reste dans la bande implémentation `-32000..-32019`, jamais la plage MCP réservée `-32020..-32099`. Distinct de `C-4`, qui impose au tronc le `outputSchema` typé lui-même ; `S-11` y ajoute le contrat d'erreurs (exécution `isError` vs protocole JSON-RPC) et l'allocation des codes. *(Source : spec MCP 2025-06-18 `server/tools` §Structured Content / §Error Handling ; changelog 2026-07-28 (allocation des codes) — MUST.)*
|
|
46
|
+
12. **`S-12` — Pagination des méthodes de liste du protocole.** Toute surface `tools/list` · `resources/list` · `resources/templates/list` · `prompts/list` qui peut dépasser une page implémente `nextCursor` ; le curseur est **opaque** — jamais décodé, jamais persisté d'une session à l'autre — et le client ne suppose aucune taille de page fixe. *(Source : spec MCP 2025-06-18 `server/utilities/pagination` — MUST. Distinct de `C-11`, qui borne la pagination de NOS résultats d'outils ; celle-ci vise les méthodes de liste du protocole lui-même.)*
|
|
47
|
+
13. **`S-13` — Progression et annulation sur un appel long.** Un appel long honore le `progressToken` présent dans `_meta` (progression monotone, notifications stoppées après complétion) et traite une `notifications/cancelled` tardive en fire-and-forget — aucune réponse renvoyée pour la requête annulée. La requête `initialize` n'est jamais annulée. Toute requête que LE SERVEUR émet (vers un amont, vers un serveur-request initié par le client) porte un **timeout configuré, appliqué quel que soit le flux de notifications de progression** — une progression qui avance n'annule pas le plafond. Une `notifications/cancelled` ou une notification de progression que le serveur émet ne référence QUE une requête/un token qu'il a lui-même émis et qu'il croit encore actif — jamais un identifiant périmé, étranger ou déjà complété (vérifié contre un registre de requêtes actives avant l'envoi). *(Source : spec MCP 2025-06-18 `basic/utilities/progress` + `basic/utilities/cancellation` §Behavior Requirements + `basic/lifecycle` §Timeouts — MUST (référence d'un id actif) / SHOULD (timeouts sortants).)*
|
|
48
|
+
14. **`S-14` — Négociation de version et capabilities gardées.** Le serveur renvoie la version négociée (l'écho si supportée, sinon une version qu'il supporte) et ne sert ensuite QUE les capabilities de cette version. Le build est **gardé par version** (`protocolVersion` négocié), car `2026-07-28` retire `initialize` (versioning par `_meta`, sans état) : un serveur qui ne connaît que la poignée de main du plancher se comporte mal face à un client de record. Support double-mode, ou déclaration écrite « plancher seul ». *(Source : spec MCP 2025-06-18 `basic/lifecycle` §Version Negotiation ; changelog 2026-07-28 — MUST.)*
|
|
49
|
+
15. **`S-15` — Sécurité des complétions.** Un serveur qui déclare la capability `completions` valide toutes les entrées, applique une limitation de débit, et empêche la divulgation d'information par complétion — une suggestion ne fuite jamais une valeur que l'appelant n'est pas autorisé à voir. *(Source : spec MCP 2025-06-18 `server/utilities/completion` §Security — MUST. Conditionnel : uniquement si la capability est déclarée.)*
|
|
50
|
+
16. **`S-16` — Empreinte locale d'un serveur stdio déclarée et minimale ; portée du transport limitée.** Un serveur local/stdio ne lit RIEN hors de son objet déclaré — jamais `~/.ssh`, un fichier d'identifiants cloud, ou l'historique du shell, sauf si l'objet de l'outil l'exige et le documente ; l'empreinte fichier demandée est documentée dans son manifeste. **Clause transport (nouvelle) :** un serveur destiné à tourner localement utilise `stdio` pour limiter sa portée au seul client ; s'il expose un transport HTTP, il exige un jeton d'autorisation OU une socket IPC / Unix à accès restreint — un HTTP lié localement est joignable par tout processus de la machine, ce que `S-1` (stdio toujours supporté) ne borne pas. *(Source : spec MCP 2025-06-18 `basic/security_best_practices` §Local MCP Server Compromise — SHOULD sur le transport. La consent-UI d'une config one-click est la responsabilité du client hôte, hors de notre surface.)*
|
|
51
|
+
17. **`S-17` — Autorisation compatible en avant.** Au-delà du plancher `S-5` (resource server, PRM RFC 9728, audience), un serveur authentifié valide `iss` (RFC 9207) avant de rédimer un code, **clé ses identifiants de client stockés par issuer** (jamais réutilisés d'un AS à l'autre), et préfère les Client ID Metadata Documents à l'enregistrement dynamique (RFC 7591, déprécié `2026-07-28`). *(Source : spec MCP 2025-06-18 `basic/authorization` ; changelog 2026-07-28 minor #7-9, deprecated #4 — MUST/SHOULD.)*
|
|
52
|
+
|
|
53
|
+
### Seconde source (suite) — la page sécurité lue en entier, huit attaques
|
|
54
|
+
|
|
55
|
+
Le premier passage a couvert cinq des huit attaques de `basic/security_best_practices` (S-8, S-9, S-10, S-16, plus A-7 côté App). Une couverture partielle d'une page sécurité est le défaut même que la page existe pour prévenir. Ce passage lit la page **en entier** et route les trois attaques restantes en règles neuves. Révision lue : **spec `2025-06-18`** (la page renvoie en avant à l'autorisation `2025-11-25`).
|
|
56
|
+
|
|
57
|
+
18. **`S-18` — Intégrité de session.** Les sessions ne servent JAMAIS à l'authentification, et tout serveur qui autorise vérifie chaque requête entrante. Un identifiant de session est **non déterministe**, issu d'un générateur aléatoire sûr (jamais séquentiel ni prévisible). Il DEVRAIT être **lié à une information propre à l'utilisateur**, de forme `<user_id>:<session_id>`, de sorte qu'un id deviné n'usurpe personne. Interaction `S-1`/`D2` : le Streamable HTTP sans session (`2026-07-28`) retire l'essentiel de cette surface ; le plancher `2025-06-18`, stateful, la réintroduit — la règle nomme ce qui subsiste selon la version négociée. *(Source : spec MCP 2025-06-18 `basic/security_best_practices` §Session Hijacking — MUST (non-auth, non-déterministe, vérifier l'entrant) / SHOULD (binding, rotation).)*
|
|
58
|
+
19. **`S-19` — Validation d'URL d'autorisation OAuth.** Une URL d'autorisation n'est ouverte que si son schéma est `http`/`https` (`http` réservé au loopback `localhost`/`127.0.0.1`/`::1` en dev ; un AS de production est en `https`) ; les schémas `javascript:`, `data:`, `file:`, `vbscript:` sont **rejetés**. Une URL n'est **JAMAIS ouverte via un shell** (`cmd.exe`, `sh`, PowerShell) — on emploie l'ouverture native non-shell de la plateforme. La validation est par **allowlist, pas blocklist** ; un client web pose `script-src 'self'` et `default-src 'self'`. *(Source : spec MCP 2025-06-18 `basic/security_best_practices` §OAuth Authorization URL Validation — MUST (schémas, pas de shell) / SHOULD (allowlist, CSP).)*
|
|
59
|
+
20. **`S-20` — Minimisation des scopes ; un scope revendiqué ne suffit jamais.** Le serveur demande le scope initial minimal (p. ex. `mcp:tools-basic`), élève par des challenges `WWW-Authenticate` `scope="..."` **ciblés** (jamais le catalogue entier), tolère le down-scoping, journalise les élévations avec des correlation ids, et n'emploie aucun scope joker/omnibus (`*`, `all`, `full-access`) ni ne publie tout le catalogue dans `scopes_supported`. **Un scope revendiqué dans un jeton n'est jamais suffisant sans une logique d'autorisation côté serveur** — clause qui renforce `C-8` : les cinq tiers DÉCLARENT l'autorité, ils ne l'APPLIQUENT pas ; l'application vit dans le serveur et son backend (frontière tronc §7). *(Source : spec MCP 2025-06-18 `basic/security_best_practices` §Scope Minimization — **aucune obligation MUST dans cette section**, une seule MAY (down-scoping) : la force de la source est un guidage, et la « faute commune » citée quasi-verbatim est l'insuffisance des scopes revendiqués. Ce standard l'adopte en SHOULD, honnêteté de la force incluse.)*
|
|
60
|
+
21. **`S-21` — Bac à sable d'un serveur proxy stdio.** Un serveur **que nous publions** qui relaie via stdio et lance des processus enfants les met en bac à sable / conteneur, restreint leur accès fichier, **journalise chaque processus lancé**, et exige une autorisation additionnelle pour toute commande dangereuse. La défense première reste `S-19` (validation d'URL). Le bac à sable du serveur QUE LE CLIENT HÔTE lance est la responsabilité du client — hors périmètre, détenteur nommé. *(Source : spec MCP 2025-06-18 `basic/security_best_practices` §stdio Transport Security in Proxy Scenarios — SHOULD. S'applique aux seules architectures proxy, pas au stdio direct.)*
|
|
61
|
+
|
|
62
|
+
### Seconde source (troisième passage) — le protocole lu en entier, pas son changelog
|
|
63
|
+
|
|
64
|
+
Les deux premiers passages lisaient le délta de spec `2026-07-28` et la page sécurité. Un changelog dit ce qui a BOUGÉ ; il ne dit jamais ce que le protocole oblige depuis toujours et que nous n'avions pas écrit. Ce passage lit la spec elle-même — les quatorze sections `2025-06-18` (base, lifecycle, transports, autorisation, tools, resources, prompts, completion, pagination, logging, ping, cancellation, progress, sampling/roots/elicitation) et son plancher de compatibilité. Chaque MUST/SHOULD propre au serveur finit ici en règle neuve, en complétion d'une règle existante, ou en hors-périmètre motivé (§ dédié). Les obligations client-only et celles que le SDK garantit sont tracées à la frontière plateforme (tronc §7). Révision lue : **spec `2025-06-18`**.
|
|
65
|
+
|
|
66
|
+
22. **`S-22` — Validation de l'en-tête `Origin` (Streamable HTTP, anti-DNS-rebinding).** Un serveur qui expose un transport Streamable HTTP valide l'en-tête `Origin` de **toute connexion entrante** contre une allowlist d'origines attendues, et rejette une origine non listée en `403` — un `Origin` non validé ouvre le DNS rebinding par lequel une page web locale attaque le serveur. En exécution locale, il se lie à `127.0.0.1` seulement, jamais `0.0.0.0`. Distinct de `S-10`, qui garde le fetch SORTANT : `S-22` est la garde ENTRANTE sur le transport lui-même. *(Source : spec MCP 2025-06-18 `basic/transports` §Streamable HTTP Security — MUST (valider `Origin`) / SHOULD (lier à localhost). Notre serveur tient ce transport ; l'obligation est nôtre, pas héritée du SDK par défaut.)*
|
|
67
|
+
23. **`S-23` — En-tête `MCP-Protocol-Version` validé côté serveur.** Un serveur sur transport HTTP lit l'en-tête `MCP-Protocol-Version` de chaque requête post-initialisation et répond `400 Bad Request` si la version est absente-alors-que-requise, invalide, ou non supportée — jamais un traitement silencieux sous une version qu'il ne sert pas. C'est la réciproque serveur de l'obligation client d'envoyer l'en-tête. *(Source : spec MCP 2025-06-18 `basic/transports` §Protocol Version Header — MUST.)*
|
|
68
|
+
24. **`S-24` — Limitation de débit sur toute invocation d'outil.** Tout outil publié applique une **limitation de débit** à son invocation. La spec l'exige comme MUST au même rang que la validation d'entrée (`C-4`) et l'assainissement de sortie (`C-16`) ; `S-15` n'en couvre que le cas complétions — `S-24` est l'obligation générale sur tout outil. *(Source : spec MCP 2025-06-18 `server/tools` §Security Considerations — MUST (« rate limit tool invocations »). `S-15` en est le cas particulier `completions`.)*
|
|
69
|
+
25. **`S-25` — Discipline des ressources et encodage du contenu.** Un serveur qui déclare la capability `resources` (ou `prompts` avec contenu image/audio) : (a) **valide toute URI de ressource** avant de la déréférencer — bonne forme, allowlist de schéma, aucune traversée de chemin ; (b) toute URI de **schéma personnalisé** se conforme à **RFC 3986** ; (c) n'emploie le schéma `https://` que si le client peut récupérer la ressource **directement**, jamais pour un contenu que le serveur lui-même relaie ; (d) tout **contenu binaire** (ressource, image, audio) est encodé en **base64** et porte un **type MIME valide**. Conditionnel : uniquement si la capability est déclarée. *(Source : spec MCP 2025-06-18 `server/resources` §Security Considerations + §Custom URI Schemes ; `server/prompts` §Image/Audio Content — MUST (validation, RFC 3986, base64+MIME) / SHOULD (discipline `https`).)*
|
|
70
|
+
26. **`S-26` — Une capability déclarée est réellement adossée : parité des notifications.** Un serveur qui déclare `listChanged: true` pour `resources` ou `prompts` émet la `notifications/{resources,prompts}/list_changed` correspondante à chaque changement d'ensemble (ajout, retrait, changement de schéma) — au même titre que `S-6` l'exige pour `tools`. Le SDK dérive la capability des handlers enregistrés ; il n'émet PAS la notification à notre place, c'est notre moitié. Une capability déclarée mais non émettrice est un contrat menteur. *(Source : spec MCP 2025-06-18 `server/resources` + `server/prompts` §List Changed — SHOULD. Généralise le principe de `S-6` hors des seuls `tools`.)*
|
|
71
|
+
27. **`S-27` — Élicitation au plancher : aucune donnée sensible, repli ou déclaration.** L'élicitation (`elicitation/create`) est supersédée au record `2026-07-28` par le round MRTR `resultType: "input_required"` (`S-2`), mais reste vivante au plancher `2025-06-18`. Un serveur qui l'emploie (a) ne demande **JAMAIS** une donnée sensible par élicitation — identifiants, secrets, données financières ou de santé ; (b) dont l'entrée mi-appel repose sur MRTR fournit un **repli `elicitation/create`** quand il est négocié au plancher, OU **déclare explicitement** (dans `server/discover` / `mcp.json`) qu'il ne supporte pas l'entrée mi-appel au plancher. Une entrée MRTR-seule, négociée au plancher, sans repli ni déclaration, est un plancher revendiqué que le serveur ne tient pas. *(Source : spec MCP 2025-06-18 `client/elicitation` §Security Considerations — MUST NOT (donnée sensible) ; changelog 2026-07-28 (MRTR supersède les requêtes serveur-initiées) — comportement plancher.)*
|
|
72
|
+
|
|
73
|
+
### Traçabilité — règles serveur → objectifs
|
|
74
|
+
|
|
75
|
+
| Règle | production | entreprise | sécurité | jetons | cloisonnement |
|
|
76
|
+
|---|:---:|:---:|:---:|:---:|:---:|
|
|
77
|
+
| S-1 stdio toujours supporté | ✓ | | | | |
|
|
78
|
+
| S-2 `resultType` sur toute réponse | ✓ | | | | |
|
|
79
|
+
| S-3 `CacheableResult` list/read | ✓ | | | ✓ | |
|
|
80
|
+
| S-4 `server/discover` obligatoire | ✓ | ✓ | | | |
|
|
81
|
+
| S-5 OAuth 2.1 resource server | | ✓ | ✓ | | ✓ |
|
|
82
|
+
| S-6 cohérence de cache `tools/list` | ✓ | | | ✓ | ✓* |
|
|
83
|
+
| S-7 version observable pré-auth | ✓ | ✓ | | | |
|
|
84
|
+
| S-8 aucun passthrough, audience validée | | ✓ | ✓ | | ✓ |
|
|
85
|
+
| S-9 député confus (serveur proxy) | | ✓ | ✓ | | ✓ |
|
|
86
|
+
| S-10 garde SSRF sur fetch OAuth | | ✓ | ✓ | | |
|
|
87
|
+
| S-11 contrat de résultat (structuré + erreurs) | ✓ | ✓ | | ✓ | |
|
|
88
|
+
| S-12 pagination des méthodes de liste | ✓ | | | ✓ | |
|
|
89
|
+
| S-13 progression et annulation | ✓ | | | ✓ | |
|
|
90
|
+
| S-14 négociation de version, capabilities gardées | ✓ | ✓ | | | |
|
|
91
|
+
| S-15 sécurité des complétions | | ✓ | ✓ | | ✓ |
|
|
92
|
+
| S-16 empreinte locale déclarée et minimale | | ✓ | ✓ | | |
|
|
93
|
+
| S-17 autorisation compatible en avant | | ✓ | ✓ | | ✓ |
|
|
94
|
+
| S-18 intégrité de session | | ✓ | ✓ | | ✓ |
|
|
95
|
+
| S-19 validation d'URL d'autorisation | | ✓ | ✓ | | |
|
|
96
|
+
| S-20 minimisation des scopes | | ✓ | ✓ | | ✓ |
|
|
97
|
+
| S-21 bac à sable proxy stdio | | ✓ | ✓ | | |
|
|
98
|
+
| S-22 validation `Origin` (anti-DNS-rebinding) | | ✓ | ✓ | | |
|
|
99
|
+
| S-23 en-tête `MCP-Protocol-Version` validé | ✓ | ✓ | | | |
|
|
100
|
+
| S-24 limitation de débit par outil | | ✓ | ✓ | ✓ | |
|
|
101
|
+
| S-25 discipline ressources + encodage contenu | ✓ | ✓ | ✓ | | |
|
|
102
|
+
| S-26 parité des notifications de capability | ✓ | | | ✓ | |
|
|
103
|
+
| S-27 élicitation au plancher, sans donnée sensible | | ✓ | ✓ | | |
|
|
104
|
+
|
|
105
|
+
\* `S-6` ne touche le cloisonnement que par l'anti-règle `cacheScope: "public"` sur un `tools/list` dépendant du tenant — un garde-fou, pas une garantie d'isolation.
|
|
106
|
+
|
|
107
|
+
**Les deux objectifs jadis faiblement servis sont renforcés par la seconde source.** Le cloisonnement, qui ne tenait au tronc qu'à `C-8`, gagne `S-8` (audience validée, aucun passthrough), `S-9` (député confus), `S-15` et `S-17` (autorisation stricte) — l'isolation ne repose plus sur une déclaration seule. L'économie de jetons gagne `S-11` (repli structuré, pas de sur-verbosité) et `S-12` (pagination des listes du protocole), qui bornent le poids servi au-delà des règles `C-10`..`C-13`.
|
|
108
|
+
|
|
109
|
+
### Les huit attaques de la page sécurité — table de routage (aucune ligne non routée)
|
|
110
|
+
|
|
111
|
+
La page `basic/security_best_practices` (spec `2025-06-18`) porte huit attaques. Chacune finit ici en règle nommée, complétée, routée vers le backend (Sigma, `k173z34hpwzzhesbq1w4tep8p18cjqhs`, moitié couche données), ou écrite hors périmètre avec son détenteur. « Déjà couvert » nomme la règle et cite sa clause.
|
|
112
|
+
|
|
113
|
+
| # | Attaque | Force spec | Destination |
|
|
114
|
+
|---|---|---|---|
|
|
115
|
+
| 1 | Confused Deputy | MUST | **`S-9`, complété ce passage** — la clause « consent page names client, shows scopes/redirect_uri, CSRF + `X-Frame-Options: DENY`, cookies `__Host-`/`Secure`/`HttpOnly`/`SameSite=Lax` liés au `client_id`, `redirect_uri` exact-string, `state` single-use » est désormais entière dans S-9. |
|
|
116
|
+
| 2 | Token Passthrough | MUST NOT (une seule) | **Déjà couvert par `S-8`** — clause : « le serveur n'accepte JAMAIS un jeton qui ne lui a pas été explicitement délivré … ne transmet JAMAIS tel quel le jeton du client vers une API amont ». Frontière avec `S-5` (façade resource-server). L'**application** de l'audience dans la couche données est la moitié backend (Sigma). |
|
|
117
|
+
| 3 | SSRF | SHOULD (spec) / opérationnel (OWASP) | **`S-10`, reconstruit ce passage** contre l'intégralité d'OWASP. Le **fetch sortant appliqué dans le code d'accès** est la moitié backend (Sigma) — frontière nommée : S-10 déclare la garde, le backend l'exécute. |
|
|
118
|
+
| 4 | Session Hijacking | MUST / SHOULD | **`S-18`, neuf.** Le **stockage** d'une session côté données (rotation, expiration) touche le backend — frontière nommée. |
|
|
119
|
+
| 5 | Local Server Compromise | SHOULD | **`S-16`, complété** (clause transport). La consent-UI d'une config one-click est **hors périmètre — détenteur : le client hôte** (Claude Code, etc.). |
|
|
120
|
+
| 6 | OAuth Authorization URL Validation | MUST / SHOULD | **`S-19`, neuf.** |
|
|
121
|
+
| 7 | stdio Transport in Proxy | SHOULD | **`S-21`, neuf** (serveur proxy que nous publions). Le bac à sable du serveur lancé par le client est **hors périmètre — détenteur : le client hôte**. |
|
|
122
|
+
| 8 | Scope Minimization | aucune MUST, une MAY | **`S-20`, neuf** (force SHOULD, honnêteté incluse). Renforce `C-8` (tronc) ; l'**application** de l'autorisation côté données est backend (Sigma) — frontière nommée. |
|
|
123
|
+
|
|
124
|
+
### Spécifications de détecteur (toutes les règles serveur `S-1`..`S-27`)
|
|
125
|
+
|
|
126
|
+
L'instrument reste gelé ; on spécifie ici ce qu'un détecteur lira, sans le construire — une règle dont le check n'est pas spécifié est une règle que personne ne bâtira. La colonne Genre porte le jeu fermé du tronc §5.2 — **`statique`** · **`dynamique`** · **`gate`**, seuls ou combinés par `+` — et rien d'autre. La part non mécanique (jugement d'un lecteur, preuve d'un acte) se lit dans les colonnes Rouge/Vert, jamais dans Genre.
|
|
127
|
+
|
|
128
|
+
| Règle | Le check lit | Rouge | Vert | Type |
|
|
129
|
+
|---|---|---|---|---|
|
|
130
|
+
| S-1 | le transport déclaré dans `mcp.json` | aucun transport `stdio` déclaré | `stdio` présent (HTTP additionnel permis) | statique |
|
|
131
|
+
| S-2 | une réponse effectivement servie (appel de l'outil) | réponse sans `resultType` | `resultType` présent (`complete`/`input_required`) | dynamique |
|
|
132
|
+
| S-3 | les 5 réponses list/read servies | `ttlMs`/`cacheScope` absents d'une réponse list/read | les deux présents sur les cinq | dynamique |
|
|
133
|
+
| S-4 | un appel `server/discover` | absent alors que `minProtocolVersion >= 2026-07-28` | `DiscoverResult` retourné (versions, capabilities) | dynamique |
|
|
134
|
+
| S-5 | le challenge 401 + l'endpoint PRM + la vérification JWT | un des cinq éléments (401/PRM/discovery/JWT/`aud`) absent | les cinq présents et vérifiés | statique + dynamique |
|
|
135
|
+
| S-6 | les chemins de mutation des tools + le `tools/list` servi | mutation sans `list_changed`, ou `cacheScope: public` sur une liste dépendante du tenant | `list_changed` émis + `ttlMs` bornant + scope cohérent | dynamique |
|
|
136
|
+
| S-7 | la version pré-auth (`/health` ou `server/discover`) vs `package.json` | version derrière l'auth, absente, ou ≠ `package.json` | version égale + atteignable pré-auth | dynamique |
|
|
137
|
+
| S-8 | le traitement du jeton (audience, upstream) | accepte un jeton non délivré pour lui ; transmet tel quel le jeton du client amont | `aud` validée + jeton amont propre au serveur | statique |
|
|
138
|
+
| S-11 | `outputSchema` + `isError` + les codes d'erreur | outil structuré sans `outputSchema` ; erreur protocole hors code JSON-RPC ; code dans la plage MCP réservée | sortie conforme au schéma + codes dans la bande implémentation | statique + dynamique |
|
|
139
|
+
| S-12 | les méthodes de liste du protocole | une méthode listable sans `nextCursor` ; curseur décodé/persisté | `nextCursor` présent, curseur opaque | statique + dynamique |
|
|
140
|
+
| S-13 | le handler d'appel long | `progressToken` ignoré ; annulation non fire-and-forget ; `initialize` annulable ; appel sortant sans timeout ; notification référençant un id périmé | progression honorée + annulation fire-and-forget + timeouts sortants + id actif seul référencé | statique + dynamique |
|
|
141
|
+
| S-14 | la négociation de version + le build gardé | n'écho pas la version négociée ; sert des capabilities hors version ; build non gardé | version négociée échoée + capabilities gardées par version | dynamique |
|
|
142
|
+
| S-15 | le handler `completions` (si déclaré) | pas de validation d'entrée / pas de limite de débit / divulgation d'info | entrées validées + débit limité + aucune divulgation | dynamique |
|
|
143
|
+
| S-17 | le flux d'autorisation | `iss` non validé (RFC 9207) ; identifiants client non clés par issuer ; enregistrement dynamique préféré aux CIMD | `iss` validé + identifiants clés par issuer + CIMD préférés | statique |
|
|
144
|
+
| S-9 | le handler de consentement proxy | pas de registre de consentement par `client_id` ; `redirect_uri` comparé par motif ; `state` posé avant consentement | consentement par client + `redirect_uri` exact-string + `state` single-use posé après consentement + cookies `__Host-` liés au `client_id` | statique |
|
|
145
|
+
| S-10 | le site de fetch sortant | URL complète reçue de l'appelant ; validation d'IP écrite à la main ; redirections suivies ; blocklist incomplète vs l'union | paramètre IP/domaine dédié + bibliothèque nommée (sortie comparée) + résolution vérifiée sur tous A/AAAA + redirections désactivées + blocklist = union | statique |
|
|
146
|
+
| S-16 empreinte + transport | (a) l'objet déclaré au manifeste vs les chemins réellement lus ; (b) la config de transport local | (a) une lecture hors de l'objet déclaré (`~/.ssh`, identifiants cloud, historique shell), ou une empreinte non documentée au manifeste ; (b) HTTP lié localement sans jeton ni socket restreinte | (a) rien lu hors de l'objet déclaré, empreinte documentée ; (b) stdio, ou HTTP + jeton d'autorisation / socket IPC restreinte | statique + dynamique |
|
|
147
|
+
| S-18 | la génération d'id de session | id séquentiel/prévisible ; session servant d'auth ; pas de binding | RNG sûr + non-auth + binding `<user_id>:<session_id>` | statique |
|
|
148
|
+
| S-19 | l'ouverture d'URL d'autorisation | schéma hors `http`/`https` accepté ; ouverture via shell ; validation par blocklist | allowlist de schémas + ouverture native non-shell + CSP `'self'` (client web) | statique |
|
|
149
|
+
| S-20 | les scopes déclarés et le challenge | scope joker/omnibus ; catalogue entier dans `scopes_supported` ; autorisation reposant sur le scope revendiqué seul | scope minimal + challenge ciblé + logique d'autorisation côté serveur + élévations journalisées | statique |
|
|
150
|
+
| S-21 | le serveur proxy stdio que nous publions | processus enfant sans bac à sable ni log ; commande dangereuse sans autorisation additionnelle | bac à sable + log par processus + autorisation additionnelle | statique |
|
|
151
|
+
| S-22 | le middleware du transport Streamable HTTP | aucun contrôle d'`Origin` avant le dispatch ; liaison `0.0.0.0` en local | allowlist d'`Origin` rejetant en `403` + liaison `127.0.0.1` en local | statique |
|
|
152
|
+
| S-23 | le parsing de l'en-tête `MCP-Protocol-Version` | en-tête ignoré ou accepté inconditionnellement | version non supportée → `400` explicite | statique |
|
|
153
|
+
| S-24 | le câblage du handler de chaque outil | aucun garde de débit sur le chemin d'invocation d'un outil | un garde de débit (token bucket, fenêtre glissante) présent sur chaque outil | statique |
|
|
154
|
+
| S-25 | le handler `resources/read` et la construction des blocs de contenu | URI déréférencée sans validation ; schéma custom non conforme RFC 3986 ; binaire sans base64/MIME | validation avant déréférencement + schéma RFC 3986 + base64 + MIME valide | statique |
|
|
155
|
+
| S-26 | les chemins de mutation resources/prompts | une mutation d'ensemble sans `list_changed` émise alors que `listChanged` est déclaré | notification émise à chaque add/remove/changement de schéma | statique |
|
|
156
|
+
| S-27 | les sites de construction d'`elicitation/create` + la branche gardée par version | champ demandé correspondant à une donnée sensible ; MRTR-seul au plancher sans repli ni déclaration | aucun champ sensible + repli élicitation OU restriction de plancher déclarée | statique + dynamique |
|
|
157
|
+
|
|
158
|
+
### Hors périmètre, avec raison écrite (jamais un silence)
|
|
159
|
+
|
|
160
|
+
Un sujet qu'un lecteur attentif soulève et que ce standard ne tranche PAS délibérément est nommé ici avec sa raison — son absence est une décision, pas un oubli.
|
|
161
|
+
|
|
162
|
+
- **Sampling (`sampling/createMessage`).** Déprécié au record `2026-07-28` (SEP-2577) ; la migration recommandée est l'appel direct à un fournisseur LLM. Aucune nouvelle feature serveur ne dépend de Sampling comme chemin durable ; legacy-compat seulement. *(Source : changelog 2026-07-28, Deprecated ; spec 2025-06-18 `client/sampling`.)*
|
|
163
|
+
- **Roots (capability `roots`).** Dépréciée au record `2026-07-28` ; la migration passe les répertoires/fichiers par paramètre d'outil ou URI de ressource. Un serveur qui s'appuie encore sur Roots valide les chemins côté serveur (jamais le root déclaré par le client seul) ; les deux SHOULD résiduels côté serveur — gérer un root devenu indisponible sans planter, respecter les frontières de root à chaque opération — sont **dette tracée** sur une capability en voie d'extinction, non un chantier neuf : on ne bâtit pas d'instrument pour un mécanisme qui disparaît. *(Source : spec 2025-06-18 `client/roots` §Security Considerations (SHOULD côté serveur) ; changelog 2026-07-28, Deprecated.)*
|
|
164
|
+
- **Qualité des suggestions de complétion (tri par pertinence, correspondance floue).** `server/utilities/completion` §Implementation Considerations porte des SHOULD de **qualité d'implémentation** — trier par pertinence, offrir une correspondance floue — sans portée sécurité ni correction. Le cas sécurité de la complétion est en `S-15` (validation, débit, anti-divulgation) ; ces SHOULD de confort ne sont pas promus en règle. *(Source : spec 2025-06-18 `server/utilities/completion` §Implementation Considerations — SHOULD qualité.)*
|
|
165
|
+
- **Capability Logging (`logging/setLevel`).** Retirée / dépréciée au record `2026-07-28` au profit de stderr (stdio) ou OpenTelemetry. L'obligation « aucun secret ni PII dans un log » demeure, mais elle vit dans `C-6` (tronc), pas ici. *(Source : changelog 2026-07-28, Major #5 + Deprecated ; spec 2025-06-18 `server/utilities/logging`.)*
|
|
166
|
+
- **Abonnement aux ressources.** Capability optionnelle dont le mécanisme dépend de la version négociée — `resources/subscribe` au plancher `2025-06-18`, `subscriptions/listen` au record `2026-07-28`. Aucun mécanisme n'est codé en dur : s'il est utilisé, `S-14` (garde par version) gouverne lequel. *(Source : spec 2025-06-18 `server/resources` ; changelog 2026-07-28 Major #4.)*
|
|
167
|
+
|
|
168
|
+
### Traçabilité — règles serveur → source
|
|
169
|
+
|
|
170
|
+
| Règle | Source | Citation |
|
|
171
|
+
|---|---|---|
|
|
172
|
+
| S-1..S-7 | délta de spec 2026-07-28 + nos produits | source une (voir mcp-standard.md §8) |
|
|
173
|
+
| S-8 | pratique — spec | 2025-06-18 `basic/security_best_practices` §Token Passthrough ; `basic/authorization` |
|
|
174
|
+
| S-9 | pratique — spec | 2025-06-18 `basic/security_best_practices` §Confused Deputy |
|
|
175
|
+
| S-10 | pratique — spec + OWASP | 2025-06-18 §SSRF ; RFC 9728 §7.7 ; OWASP SSRF Cheat Sheet |
|
|
176
|
+
| S-11 | pratique — spec | 2025-06-18 `server/tools` §Structured Content / §Error Handling ; changelog 2026-07-28 |
|
|
177
|
+
| S-12 | pratique — spec | 2025-06-18 `server/utilities/pagination` |
|
|
178
|
+
| S-13 | pratique — spec | 2025-06-18 `basic/utilities/progress` + `cancellation` |
|
|
179
|
+
| S-14 | pratique — spec | 2025-06-18 `basic/lifecycle` §Version Negotiation ; changelog 2026-07-28 |
|
|
180
|
+
| S-15 | pratique — spec | 2025-06-18 `server/utilities/completion` §Security |
|
|
181
|
+
| S-16 | pratique — spec | 2025-06-18 `basic/security_best_practices` §Local MCP Server Compromise |
|
|
182
|
+
| S-17 | pratique — spec + RFC | 2025-06-18 `basic/authorization` ; RFC 9207 ; changelog 2026-07-28 |
|
|
183
|
+
| S-18 | pratique — spec | 2025-06-18 `basic/security_best_practices` §Session Hijacking |
|
|
184
|
+
| S-19 | pratique — spec | 2025-06-18 `basic/security_best_practices` §OAuth Authorization URL Validation |
|
|
185
|
+
| S-20 | pratique — spec | 2025-06-18 `basic/security_best_practices` §Scope Minimization (guidage, sans MUST) |
|
|
186
|
+
| S-21 | pratique — spec | 2025-06-18 `basic/security_best_practices` §stdio Transport Security in Proxy Scenarios |
|
|
187
|
+
| S-10 (reconstruit) | pratique — spec + OWASP | 2025-06-18 §SSRF + RFC 9728 §7.7 ; OWASP SSRF Prevention Cheat Sheet (lu en entier) |
|
|
188
|
+
| S-22 | protocole — spec lue en entier | 2025-06-18 `basic/transports` §Streamable HTTP Security |
|
|
189
|
+
| S-23 | protocole — spec lue en entier | 2025-06-18 `basic/transports` §Protocol Version Header |
|
|
190
|
+
| S-24 | protocole — spec lue en entier | 2025-06-18 `server/tools` §Security Considerations |
|
|
191
|
+
| S-25 | protocole — spec lue en entier | 2025-06-18 `server/resources` §Security + §Custom URI Schemes ; `server/prompts` §Image/Audio Content |
|
|
192
|
+
| S-26 | protocole — spec lue en entier | 2025-06-18 `server/resources` + `server/prompts` §List Changed |
|
|
193
|
+
| S-27 | protocole — spec lue en entier | 2025-06-18 `client/elicitation` §Security ; changelog 2026-07-28 (MRTR) |
|
|
194
|
+
|
|
195
|
+
### Comportement au plancher de compatibilité `2025-06-18`
|
|
196
|
+
|
|
197
|
+
La famille déclare `2025-06-18` comme plancher (`S-14`, `minProtocolVersion`). Un plancher déclaré sans comportement écrit à ce plancher est une promesse qu'on ne peut tenir. Voici, par sujet, ce qui dégrade, ce qui doit encore tenir, et ce que le client perd quand un serveur est négocié EN BAS vers le plancher.
|
|
198
|
+
|
|
199
|
+
| Sujet | Au record `2026-07-28` | Au plancher `2025-06-18` | Ce que le serveur doit encore |
|
|
200
|
+
|---|---|---|---|
|
|
201
|
+
| `resultType` / MRTR | présent ; round `input_required` supporté | absent — le client lit « champ absent = complete » | retomber sur une sémantique single-shot ; pour l'entrée mi-appel, repli `elicitation/create` ou déclaration explicite (`S-27`) |
|
|
202
|
+
| `CacheableResult` (`ttlMs`/`cacheScope`) | requis sur 5 handlers list/read (`S-3`) | non observable au niveau protocole | le client n'a aucun signal de fraîcheur protocolaire ; il sonde ou se fie à `list_changed` — le serveur émet `list_changed` fidèlement (`S-6`, `S-26`) |
|
|
203
|
+
| `server/discover` | requis (`S-4`) | indisponible — capabilities via la seule poignée de main `initialize` | exposer ses capabilities par `initialize` ; la version reste observable pré-auth (`S-7`) |
|
|
204
|
+
| Session `Mcp-Session-Id` | Streamable HTTP sans état (SEP-2567/2575) | stateful, porteur de session | un serveur bâti sans session pour le record, négocié au plancher, **réintroduit** la gestion de session ou il est non conforme au plancher (`S-18`) |
|
|
205
|
+
| Sampling / Roots / capability Logging | dépréciés au record | vivants / attendus au plancher | maintenir les chemins legacy-compat tant qu'il sert des clients au plancher (hors-périmètre nommé pour les nouvelles constructions) |
|
|
206
|
+
| Contenu structuré / `outputSchema` / `structuredContent` / liens de ressource / `isError` | présents | **déjà présents au plancher** — non introduits par `2026-07-28` | rien ne dégrade ; `S-11` tient identiquement aux deux versions |
|
|
207
|
+
| `_meta.progressToken` | mécanisme de progression vivant | identique au plancher | rien ne dégrade ; `S-13` inchangé |
|
|
208
|
+
| Complétions | feature complète | identique — aucun delta de version | rien ne dégrade ; `S-15` inchangé |
|
|
209
|
+
|
|
210
|
+
La règle du plancher : tout ce qui est propre au record est **gardé par version négociée** (`S-14`) ; tout ce qui existait déjà au plancher tient aux deux versions sans branche. Une feature qu'on croit nouvelle du record alors qu'elle est du plancher (le contenu structuré) n'a pas à être gardée — la garder par erreur casse l'interop avec un client de record qui l'attend.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## File structure
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
mcp-server-name/
|
|
218
|
+
├── .claude-plugin/
|
|
219
|
+
│ └── plugin.json # Manifest plugin (obligatoire si publié)
|
|
220
|
+
├── src/
|
|
221
|
+
│ ├── index.ts # Entry point — register tools + transport
|
|
222
|
+
│ ├── server.ts # MCP server factory
|
|
223
|
+
│ ├── tools/ # Un fichier par tool
|
|
224
|
+
│ ├── schemas/ # Zod / JSON Schema input + output
|
|
225
|
+
│ ├── i18n/ # Table de locales (tronc §5.1)
|
|
226
|
+
│ └── lib/
|
|
227
|
+
├── tests/ # unit / integration / fixtures (tronc §5.2)
|
|
228
|
+
├── evals/evals.json # Eval suite (tronc §5.5)
|
|
229
|
+
├── docs/ # README.md, README.fr.md, tools.md, architecture.md
|
|
230
|
+
├── examples/ # claude-desktop.json, cursor.json
|
|
231
|
+
├── package.json
|
|
232
|
+
├── mcp.json # Manifest MCP (transport + capabilities)
|
|
233
|
+
├── tsconfig.json
|
|
234
|
+
├── LICENSE
|
|
235
|
+
└── CHANGELOG.md
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Python : remplacer `src/*.ts` par `src/*.py`, `package.json` par `pyproject.toml`. Le reste est identique. (L'arborescence commune — `tests/`, `evals/`, `docs/`, `i18n/` — obéit au tronc ; seuls `src/tools/`, `src/server.ts`, `mcp.json` sont propres au serveur.)
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Manifest — package.json + mcp.json
|
|
243
|
+
|
|
244
|
+
### package.json (champs requis)
|
|
245
|
+
|
|
246
|
+
```json
|
|
247
|
+
{
|
|
248
|
+
"name": "@elpi/mcp-server-name",
|
|
249
|
+
"version": "0.1.0",
|
|
250
|
+
"description": "What it does. When Claude/Cursor should use it. EN.",
|
|
251
|
+
"description_fr": "Ce qu'il fait. Quand l'utiliser. FR.",
|
|
252
|
+
"keywords": ["mcp", "claude-code", "elpi", "<vertical>"],
|
|
253
|
+
"license": "MIT",
|
|
254
|
+
"type": "module",
|
|
255
|
+
"main": "dist/index.js",
|
|
256
|
+
"bin": { "elpi-mcp-server-name": "dist/cli.js" },
|
|
257
|
+
"scripts": {
|
|
258
|
+
"build": "tsup src/index.ts",
|
|
259
|
+
"test": "vitest run",
|
|
260
|
+
"evals": "node scripts/run-evals.js",
|
|
261
|
+
"lint": "biome check ."
|
|
262
|
+
},
|
|
263
|
+
"engines": { "node": ">=20" },
|
|
264
|
+
"peerDependencies": { "@modelcontextprotocol/sdk": "^1.0.0" }
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### mcp.json (capabilities + transport)
|
|
269
|
+
|
|
270
|
+
```json
|
|
271
|
+
{
|
|
272
|
+
"name": "mcp-server-name",
|
|
273
|
+
"version": "0.1.0",
|
|
274
|
+
"transport": {
|
|
275
|
+
"type": "stdio",
|
|
276
|
+
"command": "npx",
|
|
277
|
+
"args": ["-y", "@elpi/mcp-server-name"]
|
|
278
|
+
},
|
|
279
|
+
"capabilities": {
|
|
280
|
+
"tools": true,
|
|
281
|
+
"resources": false,
|
|
282
|
+
"prompts": false,
|
|
283
|
+
"extensions": ["io.modelcontextprotocol/ui"]
|
|
284
|
+
},
|
|
285
|
+
"minProtocolVersion": "2025-06-18"
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
`version` dans `mcp.json` synchronisé avec `package.json` — jamais de drift : la parité déclarée est l'obligation `C-18` du tronc ; `S-7` en est la version observable à l'exécution. Le nommage du champ `name` obéit à `C-2`, la description bilingue à `C-1`, la version à `C-3` — tronc.
|
|
290
|
+
|
|
291
|
+
### Validation rules (propres au serveur)
|
|
292
|
+
|
|
293
|
+
| Champ | Règle |
|
|
294
|
+
|---|---|
|
|
295
|
+
| `minProtocolVersion` | >= `2025-06-18`. |
|
|
296
|
+
| `capabilities.extensions` | déclare `io.modelcontextprotocol/ui` si le serveur retourne aussi une App (voir complément App). |
|
|
297
|
+
| `transport.type` | `stdio` obligatoire (`S-1`) ; un bloc HTTP additionnel si SaaS/monétisé. |
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Tool definitions
|
|
302
|
+
|
|
303
|
+
Chaque tool est un fichier dans `src/tools/`, exporte `{ name, description, inputSchema, outputSchema, handler }`. Le nommage (`C-2`), les schémas typés in+out (`C-4`), l'écriture de la description bilingue (tronc §5.7), et la déclaration d'autorité / verbe / mécanisme / pagination (`C-8` à `C-11`, vocabulaire §3) sont au TRONC. Ne figure ici que la forme protocolaire de l'enveloppe de résultat.
|
|
304
|
+
|
|
305
|
+
### Result envelope — `resultType` (`S-2`)
|
|
306
|
+
|
|
307
|
+
```ts
|
|
308
|
+
export const buildToolResult = (content: ContentBlock[]): CallToolResult => ({
|
|
309
|
+
content,
|
|
310
|
+
resultType: "complete",
|
|
311
|
+
});
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Les serveurs implémentant Multi Round-Trip Requests (MRTR) renvoient `resultType: "input_required"` avec un `inputRequests`, à la place des requêtes serveur-initiées dépréciées (`roots/list`, `sampling/createMessage`, `elicitation/create`).
|
|
315
|
+
|
|
316
|
+
### Cacheable results — `ttlMs` + `cacheScope` (`S-3`)
|
|
317
|
+
|
|
318
|
+
REQUIS sur exactement 5 handlers : `tools/list`, `prompts/list`, `resources/list`, `resources/read`, `resources/templates/list`.
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
export const buildListResult = <T>(
|
|
322
|
+
items: T[],
|
|
323
|
+
opts: { ttlMs: number; cacheScope: "public" | "private" }
|
|
324
|
+
): CacheableResult & { items: T[] } => ({
|
|
325
|
+
items,
|
|
326
|
+
resultType: "complete",
|
|
327
|
+
ttlMs: opts.ttlMs,
|
|
328
|
+
cacheScope: opts.cacheScope,
|
|
329
|
+
});
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Capability discovery — `server/discover` (`S-4`)
|
|
333
|
+
|
|
334
|
+
```ts
|
|
335
|
+
export const handleServerDiscover = (): DiscoverResult => ({
|
|
336
|
+
resultType: "complete",
|
|
337
|
+
ttlMs: 300_000,
|
|
338
|
+
cacheScope: "public",
|
|
339
|
+
supportedVersions: ["2026-07-28", "2025-06-18"],
|
|
340
|
+
capabilities: SERVER_CAPABILITIES,
|
|
341
|
+
instructions: "Bilingual MCP server — see README.md / README.fr.md",
|
|
342
|
+
});
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Error handling
|
|
346
|
+
|
|
347
|
+
Trois niveaux : **validation** (input invalide → `McpError` code `INVALID_PARAMS`, message localisé) ; **domain** (opération métier échoue → `McpError` code `INTERNAL_ERROR` + payload structuré dans `data`) ; **transport** (bug serveur → log structuré + `McpError` générique, jamais de stack trace brute au client — `C-6`). Le refus structuré du backend est préservé en `McpError.data`, jamais aplati en texte opaque (frontière backend, tronc §7).
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## Transport
|
|
352
|
+
|
|
353
|
+
### stdio (default)
|
|
354
|
+
|
|
355
|
+
Cible : Claude Code, Cursor, Windsurf, VS Code Insiders, Goose. Lancement `npx -y <package>`. Zéro infra, isolation processus, démarrage instantané. Un client = un process, pas de partage d'état multi-client.
|
|
356
|
+
|
|
357
|
+
### HTTP (remote)
|
|
358
|
+
|
|
359
|
+
Cible : Goose remote, Postman, intégrations enterprise. Endpoint `POST /mcp` + SSE sur `GET /mcp`. Auth Bearer / API key / OAuth 2.1 (`S-5`). **Hébergement Railway par défaut** — un serveur MCP HTTP est un processus long (pas serverless) : build nixpacks zéro-config, process persistant, healthcheck natif (`/health`, voir `S-7`). Alternatives conformes si un besoin précis les justifie : Render, VPS dédié ; Vercel / Cloudflare Workers ne tiennent pas un serveur stateful sans adaptation.
|
|
360
|
+
|
|
361
|
+
| Critère | stdio | HTTP |
|
|
362
|
+
|---|---|---|
|
|
363
|
+
| Use case | Outil dev local, intégrations 1-1 | SaaS, produits payants, multi-tenant |
|
|
364
|
+
| Auth | OS-level | Token / OAuth (`S-5`) |
|
|
365
|
+
| Default ElPi Corp | OUI (`S-1`) | Si SaaS / monétisé |
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Authentication & Authorization
|
|
370
|
+
|
|
371
|
+
### Public (no auth)
|
|
372
|
+
|
|
373
|
+
Tool 100 % local ou wrap d'API publique. Default des premiers micro-products. Documenté explicitement dans le README : « no authentication required ». (L'autorité de lecture/écriture déclarée reste due — `C-8` : un serveur public déclare `read=public write=public`.)
|
|
374
|
+
|
|
375
|
+
### Token-based (header)
|
|
376
|
+
|
|
377
|
+
`Authorization: Bearer <token>` ou `X-API-Key: <token>`. Token côté client (env var), jamais loggé (`C-6`), rotation documentée. Default des wrappers Firecrawl-style.
|
|
378
|
+
|
|
379
|
+
### OAuth 2.1 resource server (`S-5`)
|
|
380
|
+
|
|
381
|
+
Le MCP server est un **resource server** OAuth 2.1, jamais un authorization server. Handshake, dans l'ordre :
|
|
382
|
+
|
|
383
|
+
1. **401 + `WWW-Authenticate`** :
|
|
384
|
+
```http
|
|
385
|
+
HTTP/1.1 401 Unauthorized
|
|
386
|
+
WWW-Authenticate: Bearer realm="mcp",
|
|
387
|
+
resource_metadata="https://your-server.com/.well-known/oauth-protected-resource"
|
|
388
|
+
```
|
|
389
|
+
2. **Protected Resource Metadata (RFC 9728)** à `/.well-known/oauth-protected-resource` — `resource`, `authorization_servers`, `scopes_supported`.
|
|
390
|
+
3. **Authorization Server discovery** (OIDC / RFC 8414) — `authorization_servers` pointe vers **Clerk** (Clerk-as-AS : déjà l'IdP flotte pour orgs + RBAC).
|
|
391
|
+
4. **Vérification JWT** — RS256/JWKS contre les clés Clerk. Une comparaison de shared secret ne vérifie rien.
|
|
392
|
+
5. **Liaison `aud` / issuer** — un token minté pour un tenant/resource est rejeté par tout autre (anti-replay cross-realm).
|
|
393
|
+
|
|
394
|
+
**Réutilisation — la façade est neuve, rien en aval ne l'est.** Seule la couche vérification-et-discovery est du code neuf. Tout ce qui suit « token vérifié » réutilise `@vantageos/cloud-identity@0.3.0` INCHANGÉ (`./tenancy-domain`, `./scope-filter`, `./bearer-validation`, `./crypto`) — doctrine `one-identity-layer` : UNE couche d'identité flotte, DEUX chemins (session Clerk humaine, token machine), jamais un second stack par serveur.
|
|
395
|
+
|
|
396
|
+
| Type serveur | Auth recommandée |
|
|
397
|
+
|---|---|
|
|
398
|
+
| Outil local CLI wrap | None |
|
|
399
|
+
| API tierce avec quota | API Key |
|
|
400
|
+
| SaaS multi-tenant | OAuth 2.1 (`S-5`) |
|
|
401
|
+
| Serveur payant ElPi Corp | API Key + Polar.sh license validation |
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Examples — patterns extraits du benchmark Day 51
|
|
406
|
+
|
|
407
|
+
### microsoft/playwright-mcp (31K stars) — « outil dev généraliste »
|
|
408
|
+
|
|
409
|
+
Un domaine large (browser automation), tools nombreux, distribution npm + intégration native VS Code. Reuse ElPi Corp : composant interne pour vantage-radar (scraping multi-sources).
|
|
410
|
+
|
|
411
|
+
### github/github-mcp-server (29.3K stars) — « official enterprise »
|
|
412
|
+
|
|
413
|
+
Distribution multi-canal (Docker + GitHub releases + one-click VS Code), modes « read-only » et « lockdown ». Reuse : modèle d'authentification fine-grained (scopes par tool) — précurseur direct de `C-8` (autorité déclarée par outil).
|
|
414
|
+
|
|
415
|
+
### ComposioHQ/composio (27.9K stars) — « agrégateur intégrations »
|
|
416
|
+
|
|
417
|
+
Un serveur = 500+ apps. Le lock-in plateforme est un anti-pattern de portabilité (`C-7` : le client ne dépend pas d'un host unique). Reuse : guide setup-composio FR.
|
|
418
|
+
|
|
419
|
+
### firecrawl/firecrawl-mcp-server (6.1K stars) — « wrap API tierce + freemium »
|
|
420
|
+
|
|
421
|
+
Wrap d'API SaaS, API key required, self-hosted possible, 14 outils ciblés. Reuse : modèle pour vantage-firecrawl-fr.
|
|
422
|
+
|
|
423
|
+
### stripe/agent-toolkit (1.5K stars) — « remote MCP server officiel »
|
|
424
|
+
|
|
425
|
+
Remote endpoint `mcp.stripe.com` + OAuth ; option locale `npx -y @stripe/mcp`. Reuse : modèle d'OAuth 2.1 + PKCE pour les futurs servers payants (`S-5`).
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## ElPi Corp additions (au-delà de la spec MCP)
|
|
430
|
+
|
|
431
|
+
- **SELLABLE AS** dans le README — chaque serveur pointe son nom de plugin marketplace cible.
|
|
432
|
+
- **VantagePeers integration** : quand le serveur interagit avec tasks/memory/messaging, utiliser exclusivement les noms de tools MCP listés dans `CLAUDE.md`.
|
|
433
|
+
- **Source-once, distribute-everywhere** : un serveur publié vit dans son repo dédié + symlink ou submodule dans les workspaces orchestrateurs qui l'utilisent.
|
|
434
|
+
- **Polar.sh license validation** pour tout serveur payant (license keys, usage metering).
|