@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,354 @@
|
|
|
1
|
+
# MCP Standard — Tronc commun (serveur · extension · App)
|
|
2
|
+
|
|
3
|
+
Version : v2.3 — 2026-08-16. Chaque règle porte sa spécification de détecteur (genre statique · dynamique · gate).
|
|
4
|
+
Owner : gamma
|
|
5
|
+
Status : en vigueur — aucun standard de cette famille n'est déclaré fini tant que l'opérateur ne l'a pas dit.
|
|
6
|
+
Référence template : `resources/references/skill-standard-v2.md`
|
|
7
|
+
|
|
8
|
+
Source canonique : `resources/references/mcp-standard.md`. **Ce document est le tronc.** Il porte tout ce qui est vrai des trois objets que la flotte publie sous MCP — un serveur, une extension, une App. Trois compléments courts portent ensuite ce qui est réellement propre à chacun. Une obligation commune ne s'écrit qu'ici, et une correction ne se fait qu'une fois.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## La famille, en un coup d'œil
|
|
13
|
+
|
|
14
|
+
| Document | Ce qu'il gouverne | Règles propres |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| **`mcp-standard.md`** (ce document) | tout ce qui vaut pour les trois objets | ses `C-*`, comptés au §8 |
|
|
17
|
+
| `mcp-server-standard.md` | un serveur MCP : transport, protocole, découverte, autorisation, cache, version, sécurité | ses `S-*`, comptés dans ce document |
|
|
18
|
+
| `mcp-extension-standard.md` | une extension : greffée sur un serveur existant sans toucher son code | ses `E-*`, comptés dans ce document |
|
|
19
|
+
| `mcp-app-standard.md` | une App : un serveur qui renvoie en plus une interface visuelle | ses `A-*`, comptés dans ce document |
|
|
20
|
+
| `analysis/vantagepeers/backend-standard/backend-standard.md` | la couche données derrière un serveur | ses `R-*`, comptés dans ce document |
|
|
21
|
+
|
|
22
|
+
**Comment lire.** On lit le tronc, puis le seul complément qui correspond à ce qu'on publie. Un serveur : tronc + complément serveur. Une extension : tronc + complément extension. Une App est aussi un serveur : elle tient le tronc, le complément serveur, ET le complément App. Un lecteur arrivant sur n'importe quel document doit pouvoir dire, sans deviner, s'il est au bon endroit — chaque document ouvre en disant ce qu'il gouverne, quel document porte le reste, et où passe la frontière.
|
|
23
|
+
|
|
24
|
+
**Comment corriger.** Une obligation vraie de plusieurs objets se corrige ici, une seule fois. Écrire la même obligation dans deux compléments est la faute que cette structure existe pour rendre impossible : c'est ainsi que deux règles retirées d'un document ont survécu dans deux autres, mot pour mot, jusqu'à ce qu'un humain les repère. La structure était le défaut, pas la formulation.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## §1 — Objet
|
|
29
|
+
|
|
30
|
+
Ce standard produit des serveurs, extensions et Apps MCP **prêts pour la production, de qualité entreprise, hautement sécurisés, économes en jetons, et cloisonnés entre organisations (multi-tenant — isolation par tenant)**. Ces cinq propriétés sont la raison d'être de chaque règle. Toute règle trace à au moins l'une d'elles (§6) ; une règle qui n'en sert aucune est supprimée, jamais conservée par précaution.
|
|
31
|
+
|
|
32
|
+
> **Porte de dispatch, avant d'écrire une ligne.** La *Doctrine Flexibilité 5/5* (`decisions/doctrine-flexibilite-2026-04-14.md`) est un formulaire à remplir AVANT de construire, pas une propriété d'un objet fini. Elle se contrôle à la porte de dispatch, jamais dans une liste de conformité ni dans `mcp-doctor`.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## §2 — Principe directeur
|
|
37
|
+
|
|
38
|
+
Ce que la flotte publie sous MCP est un produit, pas un script. Il expose un sous-domaine fonctionnel, fonctionne hors de notre pile, et s'installe en une commande sur tout client conforme à la spec `2026-07-28` — la version courante conformante — tout en négociant `2025-06-18` comme plancher de compatibilité. Le standard garantit la portabilité protocolaire, une expérience de développeur irréprochable, le bilinguisme par construction, une observabilité minimale, une sécurité explicite, et une obtention qui ne dépend d'aucun compte ni hébergeur que nous contrôlons seuls. Tout objet qui déroge à ce tronc ou à son complément ne passe pas la review Pi.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## §3 — Le vocabulaire partagé — quatre noms, arbitrés une fois
|
|
43
|
+
|
|
44
|
+
Ces quatre noms sont **liés au mot près** avec le standard backend (`analysis/vantagepeers/backend-standard/backend-standard.md` §3). Le tronc MCP énonce la **déclaration due sur la surface servie** — ce qu'un appelant lit d'une liste d'outils sans voir le code. Le standard backend énonce l'**application due dans la couche données** — si le cloisonnement est appliqué DANS la requête, si l'organisation d'une écriture vient d'une identité vérifiée, quel index sert réellement une lecture, si la borne est appliquée par l'outil ou héritée d'un magasin.
|
|
45
|
+
|
|
46
|
+
Deux noms pour un même niveau de droits, ou deux verbes pour une même opération, est exactement la divergence que les audits par outil ont mesurée. D'où : un seul jeu de noms, des deux côtés.
|
|
47
|
+
|
|
48
|
+
**1. Niveaux d'autorité — le contrôle d'accès basé sur les rôles (RBAC), jeu fermé.** Le nom que l'industrie cherche est *RBAC* ; ce que nous en faisons est ce jeu fermé de cinq tiers. `public` · `org-member` · `org-admin` · `fleet-internal` · `master`. Tout outil déclare le **couple** (autorité de lecture, autorité d'écriture), les deux distinctes, **résolues depuis une identité vérifiée** et jamais depuis une valeur fournie par l'appelant. Un niveau lu dans un argument n'est pas un niveau — vérifier « est-ce un appelant connu » dans une liste blanche n'est pas vérifier « cet appelant peut-il écrire cette ligne ».
|
|
49
|
+
|
|
50
|
+
**2. Verbes d'opération — le jeu CRUD et au-delà, jeu fermé.** Le nom que l'industrie cherche est *CRUD* (create · read · update · delete) ; notre jeu fermé le contient (`create` · `get`/`list`/`search` en lecture · `update` · `delete`) et l'étend (`upsert`, `transition`, `bulk-write`, `aggregate`).
|
|
51
|
+
|
|
52
|
+
- lectures — `list` · `get` · `search` · `aggregate` (calcul en lecture seule sur plusieurs lignes) ;
|
|
53
|
+
- écritures — `create` · `update` · `delete` · `upsert` · `transition` (changement d'état contraint, distinct d'un `update` général) · `bulk-write` (écrit N lignes choisies par **filtre** et non par identifiant — son propre verbe, parce que sa portée est illimitée par nature) ;
|
|
54
|
+
- ni l'un ni l'autre — `pure` (aucun accès aux données) · `introspect` ;
|
|
55
|
+
- hors couche données — `external-effect`, déclaré avec ce qu'il touche.
|
|
56
|
+
|
|
57
|
+
`rpc` n'est pas un verbe — c'est l'absence de verbe — et il ne se publie pas. Le couple `(effet, cardinalité)` se **dérive** du verbe, fixé par verbe, jamais déclaré à côté, si bien qu'un appelant filtre sur lecture-vs-écriture ou un-vs-plusieurs sans que personne les redouble. Un outil qui écrit plusieurs tables garde son verbe et déclare chaque table ; `composite` n'est pas un verbe.
|
|
58
|
+
|
|
59
|
+
**3. Mécanismes de recherche — la stratégie d'indexation déclarée (indexing strategy), jeu fermé.** `indexed-range` · `indexed-bm25` · `indexed-vector(cosine, dims, topK)` · `indexed-hybrid-rrf` · `unindexed-scan`. Toute liste et toute recherche en déclare un — une liste est une recherche aussi. `unindexed-scan` (charger une table en mémoire pour la filtrer) est **refusé à la publication** : c'est la panne qui achève un produit chez son premier vrai client. Un mécanisme `indexed-vector` déclare la portée à laquelle il est confiné et sa borne `topK` — une recherche vectorielle sans portée traverse les embeddings de tous les tenants à la fois.
|
|
60
|
+
|
|
61
|
+
**4. Pagination.** Toute liste et toute recherche déclare un **curseur**, une valeur par **défaut** et un **maximum**, tous trois **appliqués par l'outil** et jamais hérités du magasin qui est derrière. Épuisé et tronqué sont distinguables dans la réponse — une troncature est un curseur, pas une coupe silencieuse. Un outil dont le maximum n'est pas borné ne se publie pas.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## §4 — Règles communes (`C-*` — comptées au §8)
|
|
66
|
+
|
|
67
|
+
Ces règles communes (la liste `C-*` ci-dessous, comptée au §8) engagent un serveur, une extension et une App, à l'identique. Aucune ne se réécrit dans un complément.
|
|
68
|
+
|
|
69
|
+
1. **`C-1` — Bilingue ou rien.** Aucune publication sans `description_fr` et `README.fr.md`. Le français et l'anglais sont le socle ; l'extension à d'autres langues est prévue par construction (§5.1), jamais rétro-ajustée. **Détecteur :** *lit* le paquet publié — présence de `description_fr` (ou `description@fr`) et de `README.fr.md` ; *rouge* l'un des deux absent ; *vert* les deux présents ; *genre* **statique**. (L'égalité de qualité de la traduction, relue par un humain, est la moitié process de §5.1 / `C-20`, non lisible par un détecteur.)
|
|
70
|
+
2. **`C-2` — Nommage.** `snake_case` pour les outils, `kebab-case` pour le paquet, minuscules partout. Aucune exception. **Détecteur :** *lit* les noms d'outils et le nom du paquet ; *rouge* un outil non `snake_case`, un paquet non `kebab-case`, une majuscule ; *vert* tout conforme ; *genre* **statique**.
|
|
71
|
+
3. **`C-3` — Numérotation de version stricte (SemVer) et journal des changements**, tenus à chaque publication. Un changement cassant est une version majeure — y compris un changement d'interface visuelle qui ne touche pas l'API de l'outil. **Détecteur :** *lit* le champ version + le `CHANGELOG` ; *rouge* version non conforme SemVer, ou aucune entrée de changelog à une publication ; *vert* SemVer + une entrée par publication ; *genre* **statique** (le « un changement cassant est majeur » reste un jugement humain à la revue ; la moitié format est mécanique).
|
|
72
|
+
4. **`C-4` — Schémas typés obligatoires en entrée ET en sortie** (Zod / Pydantic). Aucun type fourre-tout (`z.any()`). La validation se fait à la frontière, avant toute exécution ; la sortie est validée avant sérialisation. **Détecteur :** *lit* `inputSchema`/`outputSchema` de chaque outil ; *rouge* un schéma d'entrée ou de sortie absent, ou un type fourre-tout ; *vert* les deux typés, sans fourre-tout ; *genre* **statique**.
|
|
73
|
+
5. **`C-5` — Suite d'évaluation d'au moins trois cas par outil**, au vert avant l'étiquetage d'une version, et incluant le **cas dégradé propre à l'objet publié** : l'entrée invalide pour un serveur, l'extension absente pour une extension, le client sans couche visuelle pour une App. **Détecteur :** *lit* `evals/evals.json` ; *rouge* absent, moins de trois cas par outil, ou le cas dégradé propre à l'objet manquant ; *vert* présent, ≥3 cas/outil, cas dégradé inclus ; *genre* **statique** (présence/forme). Le passage au vert de la suite est contrôlé au gate par `C-22`.
|
|
74
|
+
6. **`C-6` — Aucun secret, aucune PII, aucun détail système interne journalisé ; aucune trace d'erreur brute exposée au client.** Un log ne porte ni identifiant/secret, ni donnée personnelle identifiante (email, IP, nom complet), ni détail interne (chemin absolu, nom d'hôte, version d'infra) qui aiderait un attaquant. La spec ne nomme explicitement que les secrets ; l'élargissement à la PII et aux détails internes est l'obligation de la page sécurité, portée ici une fois pour serveur, extension et App. Mode diagnostic sur activation explicite uniquement. **Détecteur :** *lit* la configuration du puits de logs + le code de journalisation ; *rouge* une instruction de log interpolant un secret/PII/chemin interne sans rédaction, ou une trace brute renvoyée au client ; *vert* couche de rédaction + erreurs client génériques ; *genre* **statique** ; la disposition relève d'un lecteur humain — registre de motifs relu, jamais un mot-clé unique qui sous- et sur-détecte (`guard-formulation-census.md`). *(Source : spec MCP 2025-06-18 `server/utilities/logging` §Security — MUST NOT (secrets, PII, détails système).)*
|
|
75
|
+
7. **`C-7` — Le client peut obtenir, épingler et vérifier l'artefact sans dépendre de nous.** Propriété lisible sur l'artefact publié que le client reçoit, jamais sur nos chiffres d'adoption : (a) **installable en une commande** depuis un registre nommé, la commande présente dans le `README` ; (b) le client peut **épingler une version exacte et revenir à la précédente**, aucune version antérieure dépubliée ; (c) l'artefact publié **correspond à la source relue**, par un signal que le client contrôle sans nous faire confiance — étiquette de version égale à la version publiée, provenance ou somme de contrôle publiée ; (d) l'obtention **ne dépend d'aucun compte ni hébergeur unique** que nous contrôlons — au moins deux canaux indépendants (npm + GitHub release). Pour une App, (c) porte sur le bundle single-file effectivement servi : c'est lui que le client exécute. **Détecteur :** *lit* la commande d'installation du `README`, le registre nommé, le signal de provenance/somme de contrôle, le nombre de canaux ; *rouge* pas d'installation en une commande depuis un registre nommé, version non épinglable, aucun signal de correspondance source, canal unique ; *vert* commande + épingle + provenance + ≥2 canaux ; *genre* **statique + gate** (la correspondance artefact-publié ↔ source se lit au registre à la publication).
|
|
76
|
+
8. **`C-8` — Autorité déclarée par outil (RBAC).** Le couple (lecture, écriture) du §3, lisible sur la surface servie. Un appelant doit distinguer une lecture publique d'une écriture d'administration sans lire le code source. Replier les deux tiers sur un seul efface la divergence `read=public write=admin` ; le tier est résolu d'une identité vérifiée, jamais d'un argument fourni par l'appelant. **Détecteur :** *lit* la paire (lecture, écriture) déclarée par outil ; *rouge* paire absente, valeur hors du jeu fermé, ou deux tiers repliés en un ; *vert* paire présente, les deux dans le jeu, distinctes ; *genre* **statique**.
|
|
77
|
+
9. **`C-9` — Verbe d'opération déclaré (CRUD et au-delà)**, du jeu fermé du §3. Rien de publié ne doit laisser un appelant incapable de dire quels outils écrivent. `rpc` ne publie pas. **Détecteur :** *lit* le verbe déclaré par outil ; *rouge* verbe absent, hors du jeu fermé, ou `rpc` publié ; *vert* verbe présent, dans le jeu ; *genre* **statique**.
|
|
78
|
+
10. **`C-10` — Mécanisme de recherche déclaré (stratégie d'indexation)**, du jeu fermé du §3, sur toute liste et toute recherche. `unindexed-scan` ne se publie pas. **Détecteur :** *lit* le mécanisme déclaré de chaque liste/recherche ; *rouge* mécanisme absent, hors du jeu, ou `unindexed-scan` publié ; *vert* mécanisme présent, dans le jeu ; *genre* **statique**.
|
|
79
|
+
11. **`C-11` — Pagination déclarée et appliquée par l'outil**, selon le §3. Un maximum non borné ne se publie pas. Le curseur est **stable** dans sa fenêtre de validité, et un curseur invalide ou expiré produit une erreur `-32602 Invalid params` — jamais une exception non gérée ni une page-1 silencieuse. **Détecteur :** *lit* (statique) la présence curseur/défaut/max et la finitude du max ; (dynamique) envoie un curseur invalide et lit le code d'erreur ; *rouge* max non borné, curseur/défaut absent, ou curseur invalide ne renvoyant pas `-32602` ; *vert* les trois présents, max fini, `-32602` sur curseur invalide ; *genre* **statique + dynamique**. *(Stabilité + gestion gracieuse du curseur invalide : spec MCP 2025-06-18 `server/utilities/pagination` §Implementation Guidelines + §Error Handling — SHOULD.)*
|
|
80
|
+
12. **`C-12` — Aucun doublon.** Deux outils publiés ne peuvent pas effectuer la même opération sur la même donnée. Un synonyme est une entrée de documentation, jamais un second outil. **Détecteur :** *lit* le triplet (verbe `C-9`, table, mécanisme `C-10`) déclaré de chaque outil ; *rouge* deux outils au triplet identique ; *vert* aucun triplet dupliqué ; *genre* **statique**.
|
|
81
|
+
13. **`C-13` — Plafond de contexte à la connexion.** La surface chargée au moment où un client se connecte porte un plafond chiffré, global ET par outil ; chaque outil déclare son `tokens_estime_contexte`. Au-delà, la publication se fait par **paliers** : le schéma détaillé se récupère au moment où l'outil sert, il ne reste pas en contexte toute la session. Un catalogue qui coûte plus cher que le travail qu'il permet n'est pas économe en jetons, quelle que soit la qualité de ses réponses. **Détecteur :** *lit* les `tokens_estime_contexte` déclarés et le plafond global/par outil ; *rouge* un plafond déclaré dépassé, ou un `tokens_estime_contexte` absent ; *vert* déclarations présentes et sous le plafond ; *genre* **statique** (la déclaration). La mesure du contexte réellement chargé à la connexion est la moitié dynamique, portée par `C-21`.
|
|
82
|
+
14. **`C-14` — Inscription au catalogue de composants** (VantageRegistry) à chaque publication stable, avec sa catégorie. Un objet publié et introuvable dans le catalogue n'existe pas pour la flotte. **Détecteur :** *lit*, à la publication, l'entrée de catalogue (`search_components`) et ses champs requis ; *rouge* objet publié sans entrée résolvable, ou champ requis manquant ; *vert* entrée présente et trouvable avec ses champs ; *genre* **gate + dynamique** (contrôle à la publication ; la recherche `search_components` résout sur le serveur en marche).
|
|
83
|
+
|
|
84
|
+
### Seconde source — pratique établie au-delà de nos propres produits
|
|
85
|
+
|
|
86
|
+
Les règles ci-dessus dérivaient d'une seule source : ce que nos produits ont exercé, plus le délta de spec. Une source silencieuse sur un sujet n'interdit rien à son propos. Les trois règles suivantes reposent sur une **pratique externe établie, citée** — la spec MCP officielle, sa page Security Best Practices, OWASP — pour des sujets vrais des trois objets (serveur, extension, App). Chaque règle porte sa source ; les sujets propres à un seul objet sont dans son complément, ceux qui finissent en exclusion motivée dans le complément serveur.
|
|
87
|
+
|
|
88
|
+
15. **`C-15` — Annotations d'outil déclarées, et consommées comme non fiables.** Tout outil publié déclare ses annotations comportementales — `readOnlyHint` · `destructiveHint` · `idempotentHint` · `openWorldHint`. Ces annotations sont des **métadonnées non fiables par défaut** : un consommateur (App, client, middleware) ne conditionne JAMAIS une auto-approbation d'action sur une annotation seule quand la source n'est pas épinglée. **Détecteur :** *lit* les annotations déclarées par outil + la logique d'auto-approbation d'un consommateur ; *rouge* une annotation comportementale absente, ou un consommateur auto-approuvant sur une annotation d'une source non épinglée ; *vert* annotations déclarées + aucune auto-approbation sur annotation seule ; *genre* **statique** ; la logique d'auto-approbation du consommateur relève d'un lecteur humain. *(Source : spec MCP 2025-06-18 `server/tools`, Data Types/Tool — « clients MUST consider tool annotations to be untrusted unless they come from trusted servers ».)*
|
|
89
|
+
16. **`C-16` — Le contenu renvoyé au modèle est une donnée non fiable.** Tout résultat d'outil et tout contenu de ressource rendu au modèle est traité comme donnée non fiable : il ne peut PAS altérer une instruction de niveau système sans un marqueur de frontière de confiance explicite. Le serveur assainit ses sorties ; le consommateur valide un résultat avant de le passer au LLM. La moitié couche données — le backend n'interpole jamais un contenu tiers dans une position d'instruction et marque la provenance de ce qu'il renvoie vers un modèle — est **`R-35`** du standard backend ; `C-16` garde la frontière sur la surface servie, `R-35` l'applique dans la donnée, chaque côté nomme l'autre (§7). **Détecteur :** *lit* l'assainissement des sorties + le marquage de frontière de confiance ; *rouge* un contenu tiers interpolé dans une position d'instruction sans marqueur de frontière ; *vert* sorties assainies + provenance marquée ; *genre* **statique** ; disposition par lecteur humain, même précaution de registre de motifs que `C-6`. *(Source : spec MCP 2025-06-18 `server/tools` §Security Considerations — server MUST sanitize outputs, client SHOULD validate ; OWASP LLM Top 10 LLM01 Prompt Injection. Sujet sans source MCP dédiée — ce standard mène plutôt qu'il ne suit.)*
|
|
90
|
+
17. **`C-17` — Chaîne d'approvisionnement du paquet publié.** Tout paquet publié (serveur, extension, App) porte une attestation de **provenance** (`npm publish --provenance` ou équivalent) et des dépendances **épinglées par lockfile**. C'est notre exigence propre : la spec MCP ne couvre pas la supply-chain, et cette absence est un sujet où le standard dépasse le marché. **Obligation sœur, deux artefacts, deux moitiés qui se nomment :** `R-36` du standard backend porte la même exigence sur l'artefact du **backend** (le service de la couche données) ; `C-17` la porte sur le **paquet MCP publié** (serveur / extension / App). Deux artefacts distincts sur deux couches, un seul énoncé — chaque côté nomme l'autre pour que le doublon apparent soit lu comme une frontière, pas comme la dérive que la restructure existe pour rendre impossible (§7). **Détecteur :** *lit* le lockfile + l'attestation de provenance de l'artefact publié ; *rouge* une dépendance non épinglée, ou aucune attestation de provenance ; *vert* dépendances épinglées par lockfile + provenance présente ; *genre* **statique + gate** (la provenance s'atteste à la publication). *(Source : notre addition ; npm provenance attestation, principe OWASP supply-chain — aucune source spec MCP, constaté absent de `basic/security_best_practices`.)*
|
|
91
|
+
|
|
92
|
+
### Prescriptions promues — chaque section normative devient une règle avec son contrôle
|
|
93
|
+
|
|
94
|
+
Cinq sections de ce standard obligeaient un constructeur sans qu'aucun contrôle ne les lise — la catégorie « prescriptions normatives sans contrôle ». L'opérateur la ferme : ce qui se vérifie devient une règle numérotée portant son contrôle. Le déblocage est qu'un contrôle peut être **statique** (lire l'arbre), **dynamique** (appeler le serveur en marche), ou **`gate`** (vérification au point de livraison) — les trois sont des contrôles légaux, définis en §5.2. Aucune de ces cinq sections n'était illisible ; personne ne l'avait écrite comme règle.
|
|
95
|
+
|
|
96
|
+
18. **`C-18` — Structure et manifeste conformes.** L'arborescence imposée existe, le manifeste porte ses champs requis, les capabilities et le transport sont déclarés, et la version du manifeste **égale** celle du paquet. L'arborescence exacte est propre à chaque objet (chaque complément porte la sienne — voir « File structure » du complément serveur / extension / App) ; l'obligation de conformité est commune. **Détecteur :** *lit* l'arbre du dépôt + le manifeste **propre à l'objet**, chaque nom dérivé de la section « File structure » du complément correspondant — **serveur** : `mcp.json` + `package.json` ; **App** : `mcp.json` + `package.json` (une App est un serveur) ; **extension** : `extension.json` + `package.json` (une extension ne porte pas de `mcp.json`) ; *rouge* une entrée d'arbre requise absente, un champ requis absent du manifeste de CET objet, capabilities/transport non déclarés (serveur/App) ou `type`/`lifecycle` non déclarés (extension), ou version manifeste ≠ version paquet ; *vert* tout présent + parité de version ; *genre* **statique**. (La version observable à l'exécution est `S-7` ; `C-18` en contrôle la parité déclarée. Le nom hardcodé `mcp.json` sur les trois objets refuserait une extension conforme, qui porte `extension.json` — d'où la dérivation par objet.)
|
|
97
|
+
19. **`C-19` — Descriptions d'outils servies, mesurées.** La moitié mécanique de §5.7 devient contrôlable en interrogeant le serveur : une description sert, dans les deux langues, dès la première phrase ce que l'outil fait et quand l'employer, avec ses phrases déclencheuses, sous le plafond de contexte. **Détecteur :** *lit* la liste d'outils **effectivement servie** (appel `tools/list` sur le serveur en marche) ; *rouge* une description dépassant le budget niveau-2 (§5.4 / `C-13`), une des deux langues absente, ou la phrase déclencheuse absente ; *vert* sous budget + deux langues + déclencheur présent ; *genre* **dynamique**. La qualité réelle de la prose reste un jugement humain — la règle ne contrôle que sa moitié mécanique, et le dit.
|
|
98
|
+
20. **`C-20` — Locales dérivées et vérifiées.** Le complément contrôlable de `C-1` : l'ensemble des locales est une DONNÉE dérivée du dossier `src/i18n/`, jamais un ensemble codé en dur dans un schéma ou du code, et le serveur répond réellement dans une locale déclarée. **Détecteur :** *lit* (statique) le dossier `src/i18n/` et cherche toute énumération de locales codée en dur ailleurs ; (dynamique) demande une locale déclarée et lit la réponse ; *rouge* un ensemble de locales codé en dur, ou un serveur incapable de répondre dans une locale qu'il déclare ; *vert* ensemble dérivé du dossier + réponse correcte par locale déclarée ; *genre* **statique + dynamique**. (Présence bilingue = `C-1` ; mécanisme dérivé et vérifié = `C-20`.)
|
|
99
|
+
21. **`C-21` — Divulgation progressive mesurée.** `C-13` déclare un plafond de contexte ; `C-21` le mesure. En se connectant et en mesurant ce qui atterrit RÉELLEMENT en contexte à la connexion (métadonnées + charge de la poignée de main), le plafond cesse d'être une déclaration et devient une mesure. **Détecteur :** *lit* le contexte effectivement chargé à la connexion (appel de connexion + poignée de main), en jetons ; *rouge* la mesure dépasse le plafond déclaré (§5.4 niveaux 1–2 / `C-13`) ; *vert* mesure ≤ plafond ; *genre* **dynamique**. `C-21` est la moitié opposable et mesurée de `C-13` — elle ne redouble pas l'obligation, elle la vérifie sur le serveur en marche.
|
|
100
|
+
22. **`C-22` — Ratio de tests adossé à un passage automatique.** Un ratio de tests ou de couverture n'est cité que s'il porte l'**identifiant d'une exécution automatique qui résout** — un ratio produit sur la machine de son auteur n'est pas un ratio (§5.2). Non lisible sur un serveur, cette règle est contrôlée au gate. **Détecteur :** *lit*, au gate de fusion, le ratio cité et l'identifiant du passage CI qu'il porte ; *rouge* un ratio sans identifiant de passage, ou un identifiant qui ne résout pas ; *vert* un ratio portant un identifiant de passage automatique qui résout ; *genre* **gate** (gate nommé : l'exécution automatique des tests + évaluations à la demande de fusion, §5.2 « Exécution automatique »). Complète `C-5` (la suite d'évaluation existe) par l'exigence que son résultat cité soit adossé.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## §5 — Sections normatives communes
|
|
105
|
+
|
|
106
|
+
Ces sections sont le **détail normatif** des règles numérotées qui les contrôlent : structure et manifeste (`C-18`), écriture des descriptions d'outils (`C-19`), support multilingue (`C-20`), divulgation progressive (`C-21`), tests et conformité (`C-22`). La catégorie « prescriptions normatives sans contrôle » n'existe plus — chaque section prescrit, et une règle numérotée porte son contrôle (statique, dynamique, ou de gate — §5.2). L'arborescence et le manifeste ne figurent PAS dans ce §5 : leur forme exacte est propre à chaque objet et chaque complément porte la sienne, mais leur conformité est contrôlée en commun par `C-18`.
|
|
107
|
+
|
|
108
|
+
### 5.1 Support multilingue (socle : FR + EN)
|
|
109
|
+
|
|
110
|
+
Obligatoire par conception. Contrôlée par `C-1` (présence bilingue) et `C-20` (locales dérivées et vérifiées). **Le jeu de langues est une DONNÉE, jamais la forme.** On démarre avec deux locales — `en` (canonique) et `fr` — et ajouter une troisième langue ne coûte qu'une entrée de données, jamais un nouveau champ, un nouveau fichier ni un nouvel exemple par outil. Partout où une chaîne existe en deux langues aujourd'hui, elle vit dans une **table indexée par clé de locale**, et l'ensemble des locales se DÉRIVE de cette table — il ne s'énumère pas à la main dans un schéma.
|
|
111
|
+
|
|
112
|
+
- **Registre de locales** : `src/i18n/<locale>.json`, un fichier par locale, socle `en.json` + `fr.json`. L'ensemble des locales supportées est dérivé du contenu de ce dossier, jamais codé en dur ailleurs. Ajouter `de` = déposer `de.json`, rien d'autre.
|
|
113
|
+
- **Métadonnées localisées** : la `description`, et toute chaîne lisible, est résolue par clé de locale depuis le registre, avec `en` en repli. Le champ historique `description_fr` reste accepté comme **alias de compatibilité** de `description@fr` sans être la forme canonique — une troisième langue ne crée pas un `description_de`.
|
|
114
|
+
- **Fichier de présentation** : `README.md` (anglais canonique) est la source ; les variantes par langue (`README.<locale>.md`, dont `README.fr.md`) sont **générées** depuis un gabarit et le registre, jamais maintenues à la main une par langue.
|
|
115
|
+
- **Exemples** : un exemple par outil, libellés résolus par clé de locale — pas un fichier d'exemple par (outil × langue).
|
|
116
|
+
- **Messages d'erreur** : déjà une table de chaînes localisées dans `src/i18n/` — c'est exactement le modèle à généraliser à toute la surface lisible.
|
|
117
|
+
- **Égalité de qualité** : aucune traduction automatique en sortie. Relecture humaine obligatoire par locale avant publication.
|
|
118
|
+
|
|
119
|
+
**Câblage des langues — décision, pas préférence.** On n'utilise PAS `@vantageos/mosaic-i18n` pour un objet MCP. Ce paquet (v0.1.x) code en dur exactement deux fichiers de locale (`./locales/en.json`, `./locales/fr.json`) dans sa table d'exports — il porte donc le défaut corrigé ici, une couche plus bas — et c'est un paquet React (câblage `react-i18next` pour la bibliothèque d'interface `mosaic`). Un serveur MCP stdio/HTTP n'a pas de moteur React : son câblage de langue n'a pas sa place dans un paquet d'interface. Les chaînes vivent donc dans l'objet publié, en données (`src/i18n/<locale>.json`), résolues par un utilitaire local ; si un utilitaire de résolution partagé se justifie un jour, c'est un paquet server-side dédié, pas la lib React.
|
|
120
|
+
|
|
121
|
+
### 5.2 Tests et conformité
|
|
122
|
+
|
|
123
|
+
**Unitaires (obligatoires).** Au moins un fichier de test par outil dans `tests/unit/`. Couverture du chemin nominal, d'au moins un cas limite et d'au moins un cas d'échec. Seuil global : 80 % des lignes et 80 % des branches.
|
|
124
|
+
|
|
125
|
+
**Suite d'évaluation.** `evals/evals.json`, format commun avec le standard des skills (§5.5). Au moins trois cas par outil dont le cas dégradé (`C-5`). Exécutée à chaque demande de fusion ; tous les cas doivent passer pour fusionner.
|
|
126
|
+
|
|
127
|
+
**Conformité — `mcp-doctor`.** Les tests ci-dessus vérifient que l'objet FONCTIONNE ; `mcp-doctor` vérifie qu'il est CONFORME à ce standard. C'est l'instrument, et il fait partie du gate au même titre que les évaluations : un objet ne passe pas la review Pi si `mcp-doctor` rend un verdict non-conforme sur une règle qu'il sait lire.
|
|
128
|
+
|
|
129
|
+
- **Le genre d'un contrôle — jeu fermé de trois, une seule orthographe dans les quatre documents : `statique` · `dynamique` · `gate`.** Un détecteur n'est PAS restreint à la lecture statique. (1) **`statique`** — il lit l'arbre du dépôt, le manifeste, ou le code par AST/motif. (2) **`dynamique`** — il **se connecte au serveur en marche et l'interroge** : liste ses outils, lit ce qui est réellement servi, mesure ce qui atterrit en contexte à la connexion, demande une locale et lit la réponse. (3) **`gate`** — au point de livraison, il exige qu'une valeur citée (un ratio de tests) porte l'identifiant d'un passage automatique qui résout. Écrire les trois est ce qui empêche une règle future d'être rabotée à ce qu'une lecture de fichier voit — la cause même par laquelle cinq prescriptions étaient restées sans contrôle. Le contrat `Detector.run` reçoit donc, selon le genre, l'arbre, une session serveur, ou le contexte de gate.
|
|
130
|
+
- **La colonne Genre ne porte QUE ces trois membres**, seuls ou combinés par `+` (p. ex. `statique + dynamique`). `jugement` et `process` **ne sont pas des genres** : ils qualifient la part non mécanique d'un genre (un lecteur humain, ou la preuve d'un acte comme un log ou un test), et cette part se dit dans le **texte du check** (les colonnes Rouge/Vert, ou la prose du détecteur), jamais dans la colonne Genre. Un constructeur route un détecteur par cette colonne — `statique + jugement` n'est pas un routeur.
|
|
131
|
+
- Contrat : `Detector.run(ctx) -> { state, verdict }`, où `ctx` est, **selon le genre du détecteur**, `{ tree }` (statique — l'arbre du dépôt + le manifeste), `{ session }` (dynamique — une session vers le serveur en marche), ou `{ gate }` (genre `gate` — le contexte de livraison). L'ensemble d'états est inchangé : `state ∈ { present, absent, non-conforming, could-not-judge }`. Le nombre de détecteurs se dérive du registre du dépôt (`src/detectors/index.ts`), il ne s'écrit pas ici : il change à chaque livraison.
|
|
132
|
+
- `could-not-judge` n'est **pas** un feu vert : il dit que l'instrument n'a pas su lire, pas que la règle est tenue. Un bulletin qui porte cette mention n'est pas un bulletin propre.
|
|
133
|
+
- La couverture est partielle et la table qui la décrit vit **avec l'instrument**, dans son dépôt (`elpiarthera/mcp-doctor`, `analysis/mcp-doctor-coverage-grid.md`), jamais ici : une copie dans ce dépôt serait une seconde autorité qui diverge au premier détecteur ajouté. Une règle « couverte » par un détecteur qui n'en lit que la moitié y figure avec ce qu'il asserte réellement en regard de ce que la règle exige.
|
|
134
|
+
- **Deux détecteurs répondent à aucune règle numérotée, par conception, et c'est déclaré ici plutôt que laissé en suspens.** `D2` (signal de session : le littéral d'en-tête `mcp-session-id`) garde une propriété protocolaire — l'absence de clé de session sur un transport Streamable HTTP (`S-1`, délta `2026-07-28`) — sans être promue en règle numérotée. `D7` (adoption de `@vantageos/mcp-protocol-kit`) garde un vecteur d'implémentation interne qui satisfait matériellement `S-2`/`S-3`/`S-4`, sans être lui-même une règle. Les deux sont des détecteurs adjacents à la conformité, conservés tels quels ; ils ne sont ni promus en règle, ni retirés.
|
|
135
|
+
|
|
136
|
+
**Exécution automatique.** Analyse statique, tests unitaires, intégration et évaluations à chaque envoi. Publication bloquée si une étape échoue. **Un ratio de tests produit sur la machine de son auteur n'est pas un ratio** : il est produit par une exécution automatique, avec son identifiant de passage, ou il n'est pas cité (règle `C-22`, contrôle de gate).
|
|
137
|
+
|
|
138
|
+
### 5.3 Structure du fichier de présentation
|
|
139
|
+
|
|
140
|
+
```markdown
|
|
141
|
+
# <Nom>
|
|
142
|
+
|
|
143
|
+
Objet en une ligne (anglais).
|
|
144
|
+
|
|
145
|
+
## Installation
|
|
146
|
+
## Configuration
|
|
147
|
+
## Tools
|
|
148
|
+
## Examples
|
|
149
|
+
## Authentication (if any)
|
|
150
|
+
## Troubleshooting
|
|
151
|
+
## License & Attribution
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
La variante française porte la structure identique. Contrôlée par `C-18` (structure et manifeste conformes). Règles de corps : moins de 500 lignes, le détail va dans `docs/` ; impératif (« Run X », pas « You should run X ») ; expliquer le POURQUOI et pas seulement le QUOI ; toujours un exemple exécutable copiable ; toujours un dépannage d'au moins cinq questions fréquentes.
|
|
155
|
+
|
|
156
|
+
### 5.4 Divulgation progressive — trois niveaux
|
|
157
|
+
|
|
158
|
+
1. **Métadonnées** (`description`, mots-clés) — toujours en contexte côté client, environ deux cents mots au maximum.
|
|
159
|
+
2. **Présentation et descriptions d'outils** — chargées à la poignée de main, environ deux mille mots au maximum.
|
|
160
|
+
3. **`docs/`** — chargés à la demande, sans limite.
|
|
161
|
+
|
|
162
|
+
Ces plafonds sont la moitié « conception » de `C-13` ; `C-13` en est la moitié opposable déclarée, et `C-21` la moitié **mesurée** — le détecteur se connecte et mesure ce qui atterrit réellement en contexte à la connexion, comparé au plafond. Trois faces d'une obligation, chacune nommée, aucune redoublée.
|
|
163
|
+
|
|
164
|
+
### 5.5 Format des évaluations
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"server_name": "mcp-server-name",
|
|
169
|
+
"evals": [
|
|
170
|
+
{
|
|
171
|
+
"id": 1,
|
|
172
|
+
"tool": "scrape_url",
|
|
173
|
+
"prompt": "Scrape https://example.com and give me the title.",
|
|
174
|
+
"expected_output": "An object with title='Example Domain' and content non-empty",
|
|
175
|
+
"expectations": [
|
|
176
|
+
"tool 'scrape_url' was called",
|
|
177
|
+
"output validates against outputSchema",
|
|
178
|
+
"title field is non-null"
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Portes de qualité avant publication : toutes les évaluations au vert, couverture ≥ 80 %, latence au 95ᵉ centile documentée dans le journal des changements, documentation à jour dans les deux langues. Contrôlée par `C-5` (suite d'évaluation, ≥ 3 cas/outil, cas dégradé) et `C-22` (le ratio cité adossé à un passage automatique qui résout).
|
|
186
|
+
|
|
187
|
+
### 5.6 Inscription au catalogue de composants
|
|
188
|
+
|
|
189
|
+
À chaque publication stable (`C-14`). Champs obligatoires : `name`, `version`, description dans les deux langues, `repo_url`, `npm_url`, `keywords`, `auth_type`, `transport`, `bilingual: true`. Tout orchestrateur doit pouvoir trouver l'objet par une recherche dans le catalogue (`search_components`) sans accéder au dépôt.
|
|
190
|
+
|
|
191
|
+
### 5.7 Écriture des descriptions d'outils
|
|
192
|
+
|
|
193
|
+
Une description dit ce que l'outil fait et quand l'employer, dès la première phrase, dans les deux langues. Comme pour les skills, Claude **sous-déclenche** les outils par défaut : la description liste ses phrases déclencheuses (« Use this whenever the user says X, Y or Z ») et se termine par « even if they don't say '<tool_name>' explicitly ». Elle ne décrit pas son implémentation. C'est le texte que tout appelant paiera en contexte à chaque connexion : sa longueur est un coût, pas un espace libre (`C-13`).
|
|
194
|
+
|
|
195
|
+
### 5.8 Journalisation et observabilité
|
|
196
|
+
|
|
197
|
+
Logs structurés JSON (`level`, `tool`, `duration_ms`, `error`, `request_id`). Aucune donnée utilisateur en log par défaut ; mode debug opt-in. Aucun secret, aucune trace d'erreur brute renvoyée au client (`C-6`). Métriques minimales : `call_count`, `latency_p50/p95/p99`, `error_rate`. Sentry ou équivalent obligatoire pour tout objet payant.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## §6 — Traçabilité des règles communes
|
|
202
|
+
|
|
203
|
+
Chaque règle commune sert au moins un des cinq objectifs. Une règle qui n'en servirait aucun se supprime.
|
|
204
|
+
|
|
205
|
+
| Règle | production | entreprise | sécurité | jetons | cloisonnement |
|
|
206
|
+
|---|:---:|:---:|:---:|:---:|:---:|
|
|
207
|
+
| C-1 bilingue | ✓ | ✓ | | | |
|
|
208
|
+
| C-2 nommage | ✓ | ✓ | | | |
|
|
209
|
+
| C-3 version + journal | ✓ | ✓ | | | |
|
|
210
|
+
| C-4 schémas typés entrée+sortie | ✓ | ✓ | | | |
|
|
211
|
+
| C-5 suite d'évaluation | ✓ | ✓ | | | |
|
|
212
|
+
| C-6 aucun secret, aucune trace brute | | | ✓ | | |
|
|
213
|
+
| C-7 obtenir / épingler / vérifier | ✓ | ✓ | ✓ | | |
|
|
214
|
+
| C-8 autorité déclarée | | ✓ | ✓ | | ✓ |
|
|
215
|
+
| C-9 verbe déclaré | ✓ | ✓ | | | |
|
|
216
|
+
| C-10 mécanisme de recherche déclaré | ✓ | | | ✓ | |
|
|
217
|
+
| C-11 pagination appliquée par l'outil | ✓ | | | ✓ | |
|
|
218
|
+
| C-12 aucun doublon | | ✓ | | ✓ | |
|
|
219
|
+
| C-13 plafond de contexte | | | | ✓ | |
|
|
220
|
+
| C-14 inscription au catalogue | | ✓ | | | |
|
|
221
|
+
| C-15 annotations déclarées, non fiables | | ✓ | ✓ | | ✓ |
|
|
222
|
+
| C-16 contenu renvoyé au modèle non fiable | | ✓ | ✓ | | |
|
|
223
|
+
| C-17 supply-chain (provenance + lockfile) | ✓ | ✓ | ✓ | | |
|
|
224
|
+
| C-18 structure et manifeste conformes | ✓ | ✓ | | | |
|
|
225
|
+
| C-19 descriptions d'outils servies, mesurées | ✓ | ✓ | | ✓ | |
|
|
226
|
+
| C-20 locales dérivées et vérifiées | ✓ | ✓ | | | |
|
|
227
|
+
| C-21 divulgation progressive mesurée | | | | ✓ | |
|
|
228
|
+
| C-22 ratio de tests adossé à un passage automatique | ✓ | ✓ | | | |
|
|
229
|
+
|
|
230
|
+
**Aucune ligne vide : chaque règle commune sert au moins un objectif.** Deux objectifs restent plus faiblement servis, côté commun, et le document le dit plutôt que de le masquer. Le cloisonnement entre organisations ne tient, côté tronc, qu'à `C-8` (déclaration) — l'essentiel de sa garantie est dans le complément serveur (`S-5` liaison `aud`/issuer) et dans le standard backend (isolation appliquée DANS la requête). L'économie de jetons repose sur `C-10`, `C-11`, `C-12`, `C-13`, qui sont neufs : avant eux, rien ne bornait ce qu'un objet publié imposait au contexte de son appelant.
|
|
231
|
+
|
|
232
|
+
### Traçabilité — règles communes → source
|
|
233
|
+
|
|
234
|
+
Chaque règle nomme sa source, une cellule non vide par règle.
|
|
235
|
+
|
|
236
|
+
| Règle | Source |
|
|
237
|
+
|---|---|
|
|
238
|
+
| C-1..C-14 | source une — nos produits + délta de spec `2026-07-28` (voir §8) |
|
|
239
|
+
| C-15 | pratique — spec MCP 2025-06-18 `server/tools`, Data Types/Tool |
|
|
240
|
+
| C-16 | pratique — spec 2025-06-18 `server/tools` §Security ; OWASP LLM01 |
|
|
241
|
+
| C-17 | notre addition — npm provenance ; principe OWASP supply-chain |
|
|
242
|
+
| C-18..C-22 | prescriptions normatives de ce standard (§5), promues en règles contrôlées sur décision opérateur |
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## §7 — Frontière avec le standard backend
|
|
247
|
+
|
|
248
|
+
Un objet MCP et le backend qui le sert sont deux couches, régies par deux standards. La frontière se lit à une seule question : la propriété est-elle observable sur la réponse renvoyée au client, ou sur la donnée qui la produit ?
|
|
249
|
+
|
|
250
|
+
| Propriété | Standard qui l'impose | Où elle s'observe |
|
|
251
|
+
|---|---|---|
|
|
252
|
+
| Forme de la réponse, type de résultat, durée de validité du cache | MCP (complément serveur `S-2`/`S-3`/`S-6`) | la réponse sur le transport |
|
|
253
|
+
| Découverte de version et de capacités | MCP (complément serveur `S-4`/`S-7`) | la surface de découverte, sans authentification |
|
|
254
|
+
| Refus initial, métadonnées de ressource, liaison du jeton à son destinataire | MCP (complément serveur `S-5`) | l'échange d'autorisation |
|
|
255
|
+
| Autorité, verbe, mécanisme de recherche, pagination — **déclaration** | MCP (tronc `C-8` à `C-11`) | ce qu'un appelant lit de la liste d'outils |
|
|
256
|
+
| Autorité, verbe, mécanisme de recherche, pagination — **application** | backend (`R-5`/`R-10`/`R-11`) | le code d'accès à la donnée |
|
|
257
|
+
| Cloisonnement entre organisations au niveau de la ligne | backend (`R-11`) | la requête, jamais un tri après lecture |
|
|
258
|
+
| Doublons et plafond du catalogue | MCP (tronc `C-12`, `C-13`) | la surface servie |
|
|
259
|
+
|
|
260
|
+
Une règle écrite dans les deux documents dérive. C'est pourquoi la déclaration et l'application portent les **mêmes quatre noms** (§3) mais vivent chacune d'un seul côté.
|
|
261
|
+
|
|
262
|
+
### Sujets partagés — réconciliés dans les deux sens
|
|
263
|
+
|
|
264
|
+
Un sujet qui vit réellement sur les deux couches porte une règle de chaque côté, et **chaque côté nomme l'autre**. Sans ce nommage réciproque, le lecteur d'un document croit le sujet non traité, ou croit à un doublon. Une ligne par sujet, les deux identifiants, ce que chaque côté possède.
|
|
265
|
+
|
|
266
|
+
| Sujet partagé | Surface servie (MCP) | Couche données (backend) | Qui possède quoi |
|
|
267
|
+
|---|---|---|---|
|
|
268
|
+
| Autorité · verbe · mécanisme · pagination | `C-8`..`C-11` (déclaration) | `R-5` / `R-10` / `R-11` (application) | le nom déclaré sur la liste d'outils vs son application dans la requête |
|
|
269
|
+
| Refus structuré (no-opaque-error) | corollaire — préservé en `McpError.data` | `R-16` (rôle-racine) | le backend énonce le refus structuré, MCP le préserve sans l'aplatir |
|
|
270
|
+
| Fetch sortant (SSRF) | `S-10` (garde déclarée, surface serveur) | `R-33` (appliqué dans le code d'accès) | `S-10` déclare la garde, `R-33` l'exécute dans la requête |
|
|
271
|
+
| Passthrough de jeton / audience | `S-8` (façade servie) | `R-34` (validé dans l'accès) | `S-8` garde la façade, `R-34` valide l'audience dans la donnée |
|
|
272
|
+
| Contenu renvoyé vers le modèle | `C-16` (frontière sur la surface) | `R-35` (appliqué dans la donnée) | `C-16` pose la frontière servie, `R-35` l'applique dans ce que le backend renvoie |
|
|
273
|
+
| Chaîne d'approvisionnement / provenance | `C-17` (paquet MCP publié) | `R-36` (artefact backend) | deux artefacts distincts, un seul énoncé, chaque côté nomme l'autre |
|
|
274
|
+
|
|
275
|
+
**Pourquoi les autres règles backend ne figurent pas ici.** Elles sont purement couche données — invisibles sur la surface servie, sans moitié MCP à réconcilier : leur locus n'existe pas sur la réponse au client. Leur absence de cette table est une frontière (le sujet ne traverse pas), pas un angle mort. Le critère de présence dans cette table est unique : le sujet a une moitié observable des deux côtés.
|
|
276
|
+
|
|
277
|
+
### Frontière plateforme — ce qui est hérité, ce qui est à nous
|
|
278
|
+
|
|
279
|
+
Une obligation du protocole peut être tenue par trois détenteurs : le SDK/la plateforme (garantie sans code de notre part), NOUS (une règle numérotée l'applique), ou le CLIENT (hors de la surface serveur). Un plafond supposé à la plateforme et en fait à nous est une limite que personne n'applique — d'où ce tableau, qui nomme le côté qui possède.
|
|
280
|
+
|
|
281
|
+
| Obligation du protocole | Détenteur | Pourquoi |
|
|
282
|
+
|---|---|---|
|
|
283
|
+
| Cadre JSON-RPC, encodage UTF-8, délimitage des messages stdio | **plateforme / SDK** (`@modelcontextprotocol/sdk`) | le SDK tient le format de fil ; jamais revérifié par serveur |
|
|
284
|
+
| Poignée de main `initialize`/`initialized`, ordre pré-init, écho de négociation, réponse immédiate au `ping` | **plateforme / SDK**, héritée | la séquence de lifecycle est gérée par le SDK ; `S-14` n'ajoute que le gating des capabilities par version |
|
|
285
|
+
| Déclaration des capabilities à partir des handlers enregistrés | **plateforme / SDK** pour la déclaration ; **nous** pour la véracité (`S-26` : une capability `listChanged` déclarée émet réellement sa notification) | le SDK déclare, il n'émet pas à notre place |
|
|
286
|
+
| Validation `Origin` entrante, en-tête `MCP-Protocol-Version`, limitation de débit par outil | **nous** (`S-22`, `S-23`, `S-24`) | aucun défaut du SDK ne les garantit ; notre serveur tient le transport HTTP et les handlers |
|
|
287
|
+
| PKCE, paramètre `resource` (RFC 8707), envoi de l'en-tête `MCP-Protocol-Version`, curseur traité comme opaque côté appelant, capabilities client `sampling`/`roots`/`elicitation`, consentement d'une config one-click | **client MCP** | hors de la surface serveur ; nommé ici pour que son absence de nos règles soit une décision, pas un oubli |
|
|
288
|
+
|
|
289
|
+
**Routage inter-couches (anti-dérive).** La règle *no-opaque-error* (un refus nomme champ + raison + remède, en DONNÉES et non en phrase) est **rootée BACKEND** (`R-16` du standard backend, mémoire `j575xt4w8569fa00a870z9fyy18cd9fx`) : elle vit dans le standard backend, ce document la RÉFÉRENCE sans la réécrire. La moitié qui appartient à la couche MCP — la frontière préserve le refus structuré du backend en `McpError.data`, jamais aplati en texte opaque — est un corollaire de cette règle backend, pas une règle MCP distincte.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## §8 — Ce que ce document remplace
|
|
294
|
+
|
|
295
|
+
Trois documents portaient auparavant chacun leur propre liste de règles critiques, et les mêmes obligations y étaient réécrites : le bilinguisme dans les trois, la numérotation de version dans les trois, la suite d'évaluation dans les trois, l'obtention vérifiable de l'artefact dans deux. Corriger une obligation demandait trois modifications et que quelqu'un pense aux autres — ce qui a échoué : deux règles retirées d'un document ont survécu dans les deux autres, mot pour mot. **La structure était le défaut, pas la formulation.** Depuis cette version, une obligation commune n'existe qu'ici.
|
|
296
|
+
|
|
297
|
+
### Décompte des règles, dérivé
|
|
298
|
+
|
|
299
|
+
Restructure (source une — nos produits + délta de spec) : 21 (serveur) + 9 (extension) + 10 (App) = **40** règles numérotées, dont sept étaient des doublons d'une même obligation. Après restructure : 14 (tronc) + 7 (serveur) + 6 (extension) + 6 (App) = **33** règles, chaque obligation écrite une seule fois. Les sept doublons éliminés : bilingue (×3 → 1), SemVer (×3 → 1), suite d'évaluation (×3 → 1), obtenir/épingler/vérifier (×2 → 1).
|
|
300
|
+
|
|
301
|
+
**Seconde source (pratique externe établie — spec MCP, Security Best Practices, OWASP, RFC).** Une source silencieuse n'interdit rien ; les sujets que nos produits n'ont jamais exercés sont balayés et chacun finit en règle numérotée citant sa source externe, ou en exclusion motivée (jamais en silence). Elle ajoute **quatorze règles** : tronc `C-15`..`C-17` (3, tous objets), complément serveur `S-8`..`S-17` (10, exécution/protocole/auth), complément App `A-7` (1, consentement destructif). Quatre sujets finissent en **exclusion motivée** dans le complément serveur (Sampling, Roots, la capability Logging — dépréciées `2026-07-28` ; l'abonnement aux ressources — optionnel, mécanisme dépendant de la version négociée).
|
|
302
|
+
|
|
303
|
+
Total après ce premier passage de seconde source : 17 (tronc) + 17 (serveur) + 6 (extension) + 7 (App) = **47** règles.
|
|
304
|
+
|
|
305
|
+
**Seconde source, second passage — la page sécurité lue en entier.** Le premier passage n'avait pris de la page `basic/security_best_practices` que les attaques remontées en premier (SSRF, député confus, passthrough). Une couverture partielle d'une page sécurité est le défaut que la page existe pour prévenir : les huit attaques sont désormais routées (complément serveur, table de routage à huit lignes), et `S-10` est reconstruit contre l'intégralité de l'OWASP SSRF Prevention Cheat Sheet. Ce passage ajoute **quatre règles serveur** — `S-18` intégrité de session, `S-19` validation d'URL d'autorisation, `S-20` minimisation des scopes, `S-21` bac à sable proxy stdio — et complète `S-9` et `S-16` sans nouveau numéro.
|
|
306
|
+
|
|
307
|
+
Total après ce second passage : 17 (tronc) + 21 (serveur) + 6 (extension) + 7 (App) = **51** règles.
|
|
308
|
+
|
|
309
|
+
**Seconde source, troisième passage — le protocole lu en entier, pas son changelog.** Les deux premiers passages lisaient le délta `2026-07-28` et la page sécurité ; un changelog dit ce qui a bougé, jamais ce que le protocole oblige depuis toujours et que nous n'avions pas écrit. Ce passage lit les quatorze sections de la spec `2025-06-18` et son plancher, mappe chaque MUST/SHOULD serveur en règle / complétion / hors-périmètre, et **ne laisse aucune ligne non tranchée**. Il ajoute **six règles serveur** — `S-22` validation `Origin` (anti-DNS-rebinding entrant), `S-23` en-tête `MCP-Protocol-Version` validé, `S-24` limitation de débit par outil, `S-25` discipline des ressources et encodage, `S-26` parité des notifications de capability, `S-27` élicitation au plancher — complète `C-6` (PII + détails internes), `C-11` (stabilité + curseur invalide), `S-9`, `S-13` sans nouveau numéro, et déclare la frontière plateforme (§7) pour les obligations héritées du SDK ou propres au client.
|
|
310
|
+
|
|
311
|
+
Total après ce troisième passage : 17 (tronc) + 27 (serveur) + 6 (extension) + 7 (App) = **57** règles.
|
|
312
|
+
|
|
313
|
+
**Prescriptions promues — plus une seule sans contrôle.** L'opérateur ferme la catégorie « prescriptions normatives sans contrôle » : ce qui se vérifie devient une règle numérotée portant son contrôle. Le déblocage est qu'un contrôle peut être statique, **dynamique** (le détecteur appelle le serveur en marche), ou **`gate`** — les trois écrits en §5.2. Cinq sections promues, chacune avec son détecteur et son genre : tronc `C-18` structure et manifeste (statique), `C-19` descriptions d'outils servies (dynamique), `C-20` locales dérivées et vérifiées (statique + dynamique), `C-21` divulgation progressive mesurée (dynamique, la moitié mesurée de `C-13`), `C-22` ratio de tests adossé (`gate`). Aucune règle retirée.
|
|
314
|
+
|
|
315
|
+
Total : 22 (tronc) + 27 (serveur) + 6 (extension) + 7 (App) = **62** règles. Ce décompte se re-dérive par commande sur les quatre documents ; il ne se recopie pas. Chaque nouvelle règle porte sa spécification de détecteur et son genre (statique / dynamique / gate).
|
|
316
|
+
|
|
317
|
+
### Correspondance ancienne → nouvelle numérotation (rien n'est perdu)
|
|
318
|
+
|
|
319
|
+
**Serveur (ancien 1–21) :**
|
|
320
|
+
|
|
321
|
+
| Ancien | Nouveau | | Ancien | Nouveau |
|
|
322
|
+
|---|---|---|---|---|
|
|
323
|
+
| S1 bilingue | C-1 | | S11 CacheableResult | S-3 |
|
|
324
|
+
| S2 nommage | C-2 | | S12 server/discover | S-4 |
|
|
325
|
+
| S3 SemVer | C-3 | | S13 OAuth 2.1 | S-5 |
|
|
326
|
+
| S4 schémas | C-4 | | S14 cohérence cache | S-6 |
|
|
327
|
+
| S5 évaluations | C-5 | | S15 version observable | S-7 |
|
|
328
|
+
| S6 pas de secret | C-6 | | S16 autorité | C-8 |
|
|
329
|
+
| S7 stdio | S-1 | | S17 verbe | C-9 |
|
|
330
|
+
| S8 fallback texte | A-1 | | S18 mécanisme | C-10 |
|
|
331
|
+
| S9 obtenir/vérifier | C-7 | | S19 pagination | C-11 |
|
|
332
|
+
| S10 resultType | S-2 | | S20 aucun doublon | C-12 |
|
|
333
|
+
| | | | S21 plafond catalogue | C-13 |
|
|
334
|
+
|
|
335
|
+
*(inscription au catalogue, ancienne section « Plugin Registry integration » → C-14.)*
|
|
336
|
+
|
|
337
|
+
**Extension (ancien 1–9) :** E1 identifier→E-1 · E2 dégradation→E-2 · E3 un-type→E-3 · E4 hooks→E-4 · E5 bilingue→C-1 · E6 non-mutation→E-5 · E7 évaluations→C-5 · E8 SemVer→C-3 · E9 timeouts→E-6.
|
|
338
|
+
|
|
339
|
+
**App (ancien 1–10) :** A1 fallback→A-1 · A2 bilingue→C-1 · A3 single-file→A-2 · A4 sandbox→A-3 · A5 WCAG→A-4 · A6 perf→A-5 · A7 évaluations→C-5 · A8 SemVer→C-3 · A9 obtenir/vérifier→C-7 · A10 clé UI GA→A-6.
|
|
340
|
+
|
|
341
|
+
### Les deux faits de la réconciliation, répondus
|
|
342
|
+
|
|
343
|
+
1. **Deux détecteurs répondent à aucune règle numérotée** (`D2` session-keying, `D7` adoption du kit interne). Répondu en §5.2 : ils sont déclarés détecteurs adjacents à la conformité, conservés tels quels, ni promus en règle ni retirés. La grille de couverture (`analysis/mcp-doctor-coverage-grid.md`, vivant avec l'instrument) reste la trace de record ; elle se re-numérote dans la même livraison que les détecteurs qu'elle décrit — travail d'instrument, tenu jusqu'à ce que l'opérateur déclare les standards bons.
|
|
344
|
+
2. **Le total réconcilié** n'était plus « 35 » (chiffre calculé quand le serveur portait 14 règles, avant les six règles de surface servie et le renommage) : il valait **33** au sortir de la restructure (14+7+6+6, ci-dessus). Ce 33 est un jalon historique de la restructure, pas le total courant — les passages de seconde source l'ont porté à **62**, seul chiffre que re-dérive une commande aujourd'hui (total en tête de §8).
|
|
345
|
+
|
|
346
|
+
### Ce qui n'est PAS un gel
|
|
347
|
+
|
|
348
|
+
Fusionner ce document ne rend fini aucun standard de cette famille — ni celui-ci, ni le backend. Aucun n'est déclaré fini tant que l'opérateur ne l'a pas dit.
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Examples — patterns extraits du benchmark Day 51
|
|
353
|
+
|
|
354
|
+
*(Les patterns externes qui inspirent la famille — playwright-mcp, github-mcp-server, composio, firecrawl, stripe/agent-toolkit — sont détaillés dans le complément serveur `mcp-server-standard.md`, où ils éclairent les choix de transport, d'authentification et de distribution.)*
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
8b2c7b6295d3988e383c5b30e942de4d0c9c8ad5
|