110. MCP neříká nic, co neříká jeho schopnost
Kontext
ADR 0004 udělalo z každého rozhraní projekci jednoho registru a u dvou věcí, které klient MCP vypisuje, to drželo z konstrukce: tools/list a resources/list se staví procházením registru, takže nástroj nemůže existovat bez schopnosti a schopnost, která nástroj deklaruje, ho nemůže nemít. tools/call, trasa HTTP a příkazová řádka dosahují jednoho executoru a jednoho handleru; neexistuje větev, kterou by šlo jen MCP.
To, co projekce o nástroji říkala, byla jiná věc. Změřeno na stromu, vůči kterému bylo toto rozhodnutí psáno (v0.14.0, 166 nástrojů):
- Tři ze čtyř anotací nástroje byly v kódu protokolu konstanty:
destructiveHint: false,idempotentHint: true,openWorldHint: false. V den, kdy vznikly, platily pro každý nástroj. Od ADR 0040 tři nástroje zapisují do repozitáře — jeden z nich maže soubory — a přesto byl každý klientovi stále oznamován jako neschopný cokoli zničit a bezpečný k opakování. Instrukceinitializese podařilo přimět, aby svou větu o zapisovatelích odvozovaly; anotace vedle nich ne. - Odmítnutí dorazilo ke klientovi MCP jako věta. HTTP projekce téhož handleru odpověděla
invalid_input,not_foundneborefusedjako slovem; klient MCP musel text parsovat, aby zjistil, které z nich. - Příručka, komentář modulu, dokumentační komentář k typu nástroje a šablona webu každá stále uváděly, že žádný nástroj do repozitáře nezapisuje.
- Nic neodpovídalo na „jaká je tady projekce MCP“ jako jedna hodnota. Verze protokolu byly v
initialize, efekt každého nástroje v anotaci, deklarovaní klienti vshare/providers.yamla zda konfigurace klienta spouští server tohoto repozitáře v shellové funkci. Cockpit pro nic z toho neměl stránku.
Rozhodnutí
Bezpečnost nástroje je bezpečností jeho schopnosti. ExecutionPolicy::hints klasifikuje, co smí volající předpokládat, z efektu, který schopnost již nese, a anotace MCP jsou touto hodnotou: čtení je jen pro čtení a idempotentní; změna paměti tohoto procesu není ani jedno a nic neničí; zápis do repozitáře je destruktivní a není idempotentní. Klasifikace je konzervativní ve směru, který volající přežije — žádný handler nedeklaroval, že druhé volání nic nezmění, takže žádný není jako takový ohlášen. Z handleru nic nesahá za stroj, takže openWorldHint je false, a to stejnou klasifikací, nikoli konstantou. Samotný efekt cestuje v _meta.majordomus.effect.
Kategorie odmítnutí je kategorie chyby. CapabilityError::code je jedno slovo na variantu a čtou ho oba transporty: pole error.code v HTTP a _meta.majordomus.error.code ve výsledku nástroje s isError: true. Odmítnutý výsledek nenese structuredContent, protože schéma výstupu popisuje úspěch.
Servírované metody jsou seznam, který čte dispečer. protocol::METHODS se konzultuje dříve, než se požadavek vůbec prohlédne, takže seznam je to, co se servíruje, a ne jeho popis.
Projekce je popsána schopností. mcp.projection odpovídá jako jedna typovaná hodnota: kdo odpovídá na initialize, verze protokolu, přenosy a relace připojené přes každý, obsluhované metody a ty, které ne, každý nástroj s jeho efektem a nápovědami, nástroje, které zapisují do repozitáře, zdroje a kde stojí konfigurace každého deklarovaného klienta v tomto repozitáři. Stránka MCP v Cockpitu, trasa HTTP, nástroj, zdroj a majordomus mcp --inspect ji vykreslují. Nevypisuje nic svého.
Název nástroje zůstává deklarovaný. Deklaruje se jednou, na schopnosti, a registr odmítne duplicitu nebo špatně utvořený název. Neodvozuje se z id schopnosti: 89 ze 166 názvů se liší od toho, co by dalo odvození (capabilities.list je majordomus_capabilities), každý z nich drží klienti i měřená plocha release a odvození by bylo zásadním přejmenováním poloviny plochy bez užitku pro jakéhokoli volajícího. Kontroluje se místo toho jediné místo, kde se název píše podruhé: próza. tests/mcp_prose.rs čte autorské dokumenty, bootstrapové soubory, šablony poskytovatelů a šablony webu a selže na názvu tvaru nástroje, který registr neprojektuje.
Prompty zůstávají neservírované a popis to říká. Promptové assety repozitáře vykreslují kontext lokální pro checkout, který vlastní shellový nástroj; servírované bez vykreslení by se četly jako hotové. Zůstávají zdroji. mcp.projection uvádí prompts: false, místo aby klient musel zjistit z -32601.
Konfigurace klientů zůstávají bootstrap soubory. .mcp.json, .codex/config.toml a soubor nastavení Gemini jsou každý jedna položka jmenující launcher, ve třech formátech vlastněných třemi klienty, a klient je čte dříve, než existuje jakýkoli server, který by je generoval. Negenerují se. Jejich postavení se měří — wired, foreign nebo absent, čteno ze souboru — a konfigurace, která existuje a nejmenuje launcher, je nález s nápravou.
Co se tímto nemění
Nepřidává se žádný nástroj, který zapisuje, ani žádná cesta zápisu do MCP. Schopnost zapisuje do repozitáře, jen když deklaruje .writes_repository(); handler je ten, který dosáhne každý transport, takže co odmítne přes HTTP, odmítne přes MCP týmiž slovy. MCP nedostává vlastní autorizační vrstvu: server je jen na loopbacku, požadavek měnící stav z cizího původu prohlížeče se odmítne před jakýmkoli handlerem a rozhodnutí, zda zavolat něco, co něco mění, zůstává na klientovi, což je přesně to, o čem mají anotace informovat.
Zamítnuté alternativy
- Deklarace
idempotentadestructivena schopnost. Bylo by přesnější pro obslužnou funkci, která jen připisuje, a je to druhé místo, kde si deklarace může odporovat. Efekt je již deklarován; nápovědy ho následují. Deklaraci na úrovni obslužné funkce lze přidat, až se ukáže, že volající potřebuje rozlišení. - Odvozování jmen nástrojů z id schopností. Zamítnuto výše: přejmenování 89 nástrojů.
- Servírování dovedností nebo workflow jako MCP promptů. Už jsou zdroji se stabilními URI. Druhý protokolový povrch pro týž text by potřeboval pravidlo, který z nich má klient preferovat, a nikdo o něj nepožádal.
- Generování klientských konfigurací. Soubor, který klient čte, aby našel server, nelze dlužit serveru.
Co důkaz našel a co po sobě nechal
Sada testů shody psaná vedle tohoto rozhodnutí (tests/mcp_conformance.rs) prošla každý zdroj a každý nástroj čtení na tajemství podstrčené do prostředí a repozitáře. Jedno se dostalo ke klientovi: přihlašovací údaj vložený do URL remote origin vrátil session_domain.identity doslova, přes MCP i HTTP, a shellový nástroj zapsal týž řetězec jako identitu repozitáře do sdílených záznamů, které se sledují a pushují. To je opraveno tam, kde to vzniklo, a ne tady: na vlastní větvi (fix/a-remote-url-is-published-without-its-credential, test/cases/941), před tímto rozhodnutím, protože přihlašovací údaj nečeká na architekturu. Dokud ta větev není v historii, se kterou se tato mergne, test no_secret_in_the_environment_or_the_repository_reaches_a_client sady zde selhává a je ponechán selhávat, ne ignorován.
Změřilo také mezeru, kterou toto rozhodnutí nezavírá. Registr neumí říct, že odpověď schopnosti je funkcí jejího vstupu a samotného repozitáře: properties.rs jmenuje tři výjimky podle id a test parity mezi procesy musel vylučovat podle tagu a měření (environment.preflight, mesh.doctor, executions.list, health.report, server.status a dashboard.overview odpovídají v každém procesu jinak). Deklarovaná klasifikace by umožnila vymezit paritu modelem místo seznamem; dotýká se každé deklarace a je ponechána jako dluh, pojmenovaný zde, aby nebyl znovuobjevován.
Důsledky
- Tři nástroje jsou nyní ohlašovány jako destruktivní a ne idempotentní a každý příkazový nástroj jako ne idempotentní. Klient, který se před destruktivním voláním ptá člověka, se nyní zeptá i před těmito třemi. To je zamýšlená změna.
capabilities.projectionsneseeffectna každém řádku a registr získávámcp.projection. Veřejný povrch se rozšířil, což release engine měří jako minor verzi.- Klient se může větvit podle
_meta.majordomus.error.code. Text se nemění. - Přidání schopnosti, která deklaruje nástroj, ji dostane na stránku MCP v Cockpitu, do generované reference schopností, do dokumentu OpenAPI a do
tools/lists anotacemi, které o ní platí, bez úpravy jiného souboru než její deklarace.