doc-platform
@ingadhoc/docs-platform
Die Plattform der Adhoc-Dokumentation: eine Suchmaschine, ein
MCP-Kern, ein Zugangs-Gate und ein Leak-Guard, die von den
Content-Repos (oba-docs, odumbo-docs, adhoc-docs) per Pin konsumiert werden.
Davor lebten die vier Teile geforkt in den drei Repos: dieselbe Datei mit drei
Dialekten, und jeder Fix wurde von Hand propagiert — oder eben nicht. Die
Messung steht in docs/unificacion/: lib/mcp/indice.mjs hatte 41
Unterschiede zwischen den drei Kopien, und 17 davon waren Fixes, die ein
Repo hatte und die anderen beiden nicht. Der teuerste Fall: Der Leak-Guard
war in zwei Repos byte-identisch und existierte im dritten nicht.
ADR 0006 aus
knowledge-management— ein Repo pro Content-Body, und die Plattform als separates Paket: Content und Engine haben unterschiedliche Lebenszyklen und unterschiedliche Eigentümer.ADR 0007 aus
knowledge-management— das Gate und der Leak-Guard gehören zur Plattform, nicht zu jeder Site: Ein Schutz, den jedes Repo neu implementiert, ist ein Schutz, den irgendein Repo nicht hat.Etappe A der Spec
arquitectura-plataforma-docs: dieses Paket, mit den beiden versionierten Verträgen und dem Drift-Check, der den Rückstand des Pins sichtbar macht.
Wie konsumiert wird
npm i --ignore-scripts github:ingadhoc/doc-platform#v0.1.0Exakter Pin, immer per Tag. Kein ^, kein main, keine Branches: Der Pin
ist das, was verhindert, dass ein Fix der Plattform drei Sites gleichzeitig
zerbricht, und er ist das, was ein Rollback in einer Zeile ermöglicht. Ein
Bereich lässt den docs-drift-check absichtlich fehlschlagen — ein Pin, der
nicht pinnt, ist kein Pin.
--ignore-scripts empfohlen. Dieses Paket hat kein Install-Skript und wird
keins bekommen; das Flag gilt für den gesamten Baum, weil das im
buildCommand von öffentlichen Sites läuft. Aus demselben Grund hat das
Paket genau eine Abhängigkeit (minisearch, die die Suchmaschine braucht)
und null devDependencies: minimale Angriffsfläche im Build.
Was der Konsument bereits hat und dieses Paket nicht deklariert:
mcp-handler und zod, die lib/mcp/mcp-handler.mjs importiert. Sie sind
absichtlich Abhängigkeiten des Repos: Das Repo entscheidet, mit welcher Version
des MCP-Frameworks deployed wird, und das Paket drückt ihm keine auf. Alle drei
Repos haben sie heute.
Nach dem npm i hat das konsumierende Repo drei Zeilen Kleber:
// api/mcp.mjs
import { crearMcp } from '@ingadhoc/docs-platform/mcp-handler';
import { crearFeedback } from '@ingadhoc/docs-platform/feedback';
import * as indice from '@ingadhoc/docs-platform/indice';
import { config } from '../docs.mcp.config.mjs';
const { handler } = crearMcp({
config,
indice,
crearIssue: crearFeedback(config.feedback),
});
export default handler; // sin default export Vercel no encuentra el handler// middleware.js — en la RAÍZ del repo (Vercel lo exige ahí)
import { next } from '@vercel/functions';
// Por RUTA RELATIVA, no por especificador de paquete: el bundler del edge
// rechaza `@ingadhoc/docs-platform/gate` cuando el repo consumidor no es
// `"type": "module"` (Docusaurus lo impide) — "unsupported modules".
// Y ojo con renombrar a middleware.mjs: el deploy queda VERDE y SIN
// middleware (la ausencia silenciosa del gate). Hallazgo del piloto
// odumbo-docs, deployment 3mXWLwPHPgcwasEji49pGEg7Lyuv.
import { decidir } from './node_modules/@ingadhoc/docs-platform/lib/mcp/gate.mjs';
const AUDIENCIAS = ['publico', 'interno']; // adhoc-docs: ['interno']
export default function middleware(request) {
return decidir(request, process.env, { audiencias: AUDIENCIAS }) ?? next();
}// package.json del consumidor — el guard, dentro del buildCommand
"build:publico": "node tools/build.mjs --audience=publico && npm --prefix site run build && npx docs-guard-fuga --salida=dist/publico"Das && ist nicht kosmetisch: Es ist das, was den Deploy abbricht, wenn der
Guard mit 1 endet. Ändere es nicht in ;.
Related MCP server: docrag
Was es exportiert
Import | Was es ist |
| Suchmaschine: |
|
|
|
|
| Token-Vergleich in konstanter Zeit (nutzt |
| die Grammatik von |
|
|
|
|
|
|
| das Referenz- |
Bin | der Leak-Guard, für den |
Bin | der Drift-Check, für das CI des Konsumenten |
Die beiden Verträge
Beide tragen schemaVersion, und beide Leser werfen, wenn der Emittent
eine neuere Version deklariert, als sie lesen können — oder wenn er sie nicht
deklariert. Kein stilles Degradieren: Ein falsch antwortender Index ist
schlimmer als einer, der nicht antwortet.
Config ↔ Plattform:
docs.config.json, mit Schema veröffentlicht inschema/docs.config.schema.jsonund Validator inlib/config.mjs(eigen, ohne Abhängigkeiten:ajvkommt nicht in den Build einer öffentlichen Site). Das Design jedes Felds, mit der gemessenen Evidenz, steht indocs/unificacion/diseno-eje.md; die drei aktuellen Configs übersetzt inmapeo-configs.md.Index ↔ Engine: Es emittiert
tools/build.mjsvon jedem Repo und liest eslib/mcp/indice.mjs. Es ist spezifiziert indocs/unificacion/contrato-indice.md.
Die Achse, in einer Tabelle
Der Korpus deklariert eine Achse als Objekt: { tipo, default?, valores[] }.
| corpus | param in den Tools |
| Wildcard (Artikel außerhalb der Achse) |
| oba-docs |
| wählt den | ja ( |
| adhoc-docs |
| strukturierte Mehrdeutigkeit (deklariert kein | nein |
| odumbo-docs | (wird nicht exponiert) | — | — |
Die Regel von leer() ist eine und hat kein if pro Achsentyp: Sie wählt
nur, wenn die Config deklariert hat, wen sie wählen soll. Was das Verhalten
ändert, ist das Vorhandensein von eje.default, nicht der Typ — und es gibt
einen Test, der das beweist, indem er einem Korpus mit Achse project einen
default gibt.
Tests ausführen
npm install && npm test # 227 casosbloques braucht ein Content-Repo (es führt dessen tools/build.mjs wirklich
über die Incident-Fixtures aus) und wird mit Grund geskippt, wenn keins da
ist:
DOCS_REPO=~/repositorios/oba-docs node --test tests/bloques.test.mjsDer HTTP-Handler-Abschnitt von mcp.test.mjs (16 Fälle) wird ebenfalls mit
Grund geskippt, wenn der Checkout kein mcp-handler/zod hat, die
Abhängigkeiten des Konsumenten und nicht dieses Pakets sind. Mit beiden
installiert ergibt mcp 57. Ein Fall ohne seine Capability wird explizit
geskippt; er wird nicht degradiert ausgeführt.
Für jjs — offene Entscheidungen
Was dieser Zusammenbau nicht allein löst. Die ersten drei stammen aus
diseno-eje.md §7 und betreffen den Vertrag; die übrigen kamen aus den vier
Analysen und bleiben nach der Vereinheitlichung weiter offen.
1. Eine Achse pro Korpus: Wird die Obergrenze akzeptiert?
schemaVersion: 1 erlaubt eine Achse pro Config, und das reicht heute für
die drei Repos. An dem Tag, an dem ein Korpus project × version gleichzeitig
braucht, drückt das Schema es nicht aus, und die Lösung ist ein
schemaVersion: 2 mit ejes: [...] (Plural). Design-Empfehlung: Die
Obergrenze explizit akzeptieren und die reale Notwendigkeit sie mit Evidenz
wieder öffnen lassen (gleiches Kriterium wie der Bump-Alarm der Etappe B). Es
ist deine Entscheidung, weil es das Major betrifft.
2. metadata.types: Vokabular pro Korpus oder Adhoc-weit?
Heute hat nur adhoc-docs types, und seine 6 Werte ähneln stark einem
Standard aus knowledge-management (concepto, referencia, procedimiento,
troubleshooting, guia, indice). Wenn das Vokabular Adhoc-weit ist, gehört
es nicht in die Config jedes Repos: Es gehört ins Paket, und die Config sagt
nur, ob sie es verlangt. Das ist eine Content-Governance-Entscheidung, keine
Schema-Entscheidung; solange sie nicht entschieden ist, lässt das Schema es als
Liste pro Korpus (kompatibel mit beiden Ausgängen).
3. Das Opt-out des Leak-Guards in adhoc-docs: Unterschreibst du es?
Das Schema verpflichtet zur Deklaration von deploy.guardDeFuga, also ist
das stille Weglassen nicht mehr möglich. Es bleiben die beiden Ausgänge, beide
vertretbar: {"activo": false, "motivo": "…"} (dieses Repo hat keinen
öffentlichen Build: Sein Gate ist unbedingt, und der Guard schützt gegen den
Leak in den öffentlichen Build), oder der Guard kommt trotzdem rein, als
Sicherheitsgurt. Das motivo, das heute in mapeo-configs.md steht, lautet
wörtlich "PENDIENTE DE FIRMA (jjs)".
Und es gibt einen technischen Teil, der sich nicht durch Kopieren der Datei
beheben lässt (FRAGE 1 aus analisis-04-seguridad.md): adhoc-docs hat keine
:::interno-Blöcke, emittiert kein site/generated.json mit Audienz und hat
keine deploy.proyectos-Map. Mit dem Guard aktiv wie er ist, schlägt sein
Build von Anfang an fehl wegen "site/generated.json existiert nicht". Die
strenge Lösung ist, dass es diese beiden Dinge emittiert.
4. Die Audienzliste ist weiterhin dupliziert, und der Drift-Check vergleicht sie noch nicht
docs.config.json → audiences und middleware.js → AUDIENCIAS müssen
übereinstimmen, und es gibt keine Möglichkeit, die Duplizierung zu vermeiden:
Der Edge liest nicht vom Dateisystem. Das ist genau die Art von stillem Drift,
bei dem der Fork begann. Es fehlt ein CI-Fall, der sie vergleicht (der heutige
docs-drift-check misst den Pin, nicht diese Kohärenz).
5. Drei Dinge, die in den Repos vor dem Taggen geprüft werden müssen
DOCS_AUDIENCEin den drei Environments jedes Vercel-Projekts (Production, Preview und Development) vor dem Merge, der das Paket übernimmt. Mit dem Fail-Closed gibt ein Projekt ohne die Variable 503 zurück. Das ist die sichere Richtung, aber nicht kostenlos.--esperadain den aktuellenbuildCommands: Jetzt lehnt der Guard es ab, wenn es in Vercel läuft. Wenn irgendein buildCommand es heute übergibt, beginnt dieser Deploy zu scheitern. Das konnte von den Snapshots aus nicht verifiziert werden.Der GET des MCP gibt 503 zurück, wenn der Deployment keine bedienbare Audienz deklariert. Das ist eine für den Konsumenten sichtbare Änderung: Der Preflight von Claude Code erhält 503 statt des Hinweisschilds, wenn der Deployment falsch konfiguriert ist.
6. Gemessene Schuld, die dieses Paket nicht schließen kann
Der Fail-closed des Präprozessors gibt aus und schlägt danach fehl. Mit einer falsch geschriebenen Direktive (
::: interno) schreibtbuild.mjssite/docs/**mit der internen Zeile darin und danach endet es mit 1. Heute leakt es nicht, weil derbuildCommandmit&&verknüpft: Der Schutz liegt im Operator, nicht im Programm. Es ist alstodointests/bloques.test.mjsdeklariert, und die Vereinheitlichung vonbuild.mjsbehebt es — die nicht in diese Stufe eingegangen ist.tests/bloques.test.mjsschreibt in<repo>/site/, weil in oba und odumbo die Build-Ausgabe hartcodiert ist. Nach dem Ausführen der Suite muss man mitnpm run genneu generieren.Grenzen des lexikalischen Ansatzes des Guards: Zahlen und Strings mit weniger als 5 Zeichen haben nie eine Sonde (ein Schlüssel
4821, ein Akronym), Bilder werden nicht gescannt, und ein Leck innerhalb vonapplyBlockserzeugt keine Sonde. Es steht im Header des Guards; ich wiederhole es hier, weil es der Teil ist, der mit Abdeckung verwechselt werden kann.serverInfo.versionbleibt in'1.0.0'hartcodiert im Handler. Es sollte aus derpackage.jsondes gepinnten Pakets kommen, damit ein MCP-Client melden kann, gegen welche Version der Plattform er gesprochen hat. Es wurde nicht geändert: Es wäre Verhalten erfunden.Der Platzhalter gehört zum
tipoder Achse, nicht zum Korpus. Ein Korpus mit Achseprojectkann kein transversales Dokument haben (eje: nullbleibt für jeden Filter unsichtbar). Falls es eines Tages nötig ist, ist die strikte Ausgabe, dass der Vertrag des Index es verbietet, während der Platzhalter ausgeschaltet ist, damit der Widerspruch im Build fehlschlägt und nicht zur Laufzeit.Die Spec sagt "vitest" als Testkonvention der Stufe A, und keines der drei Repos verwendet vitest: Die tatsächliche Konvention — und die dieses Pakets — ist natives
node:test. Es lohnt sich, diese Zeile zu korrigieren, bevor jemand vitest installiert, um sie zu erfüllen.
This server cannot be deployed
Maintenance
Related MCP Connectors
Team docs served to AI agents over MCP - search, Markdown reads, version pinning, read audit.
Read-only MCP server for the OrchestKit docs: full-text search + Markdown fetch. No auth.
Political Comms documentation MCP server: search docs, query the docs filesystem. No auth.
Public read-only MCP for products, frameworks, guides, methodology, and blog metadata.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables MCP clients to access and read mdbook documentation, including structure, content, and search.12 npm3MIT
- AlicenseNot gradedqualityDmaintenanceProvides RAG (Retrieval Augmented Generation) access to technical documentation through MCP, enabling LLMs to search and retrieve relevant documentation on-demand.4MIT
- AlicenseNot gradedqualityAmaintenanceEnables searching, reading, and navigating MkDocs documentation sites through MCP tools for keyword, semantic, or hybrid search, document browsing, and project metadata.1BSD 2-Clause "Simplified"
- FlicenseAqualityCmaintenanceEnables MCP clients to retrieve authoritative AI Square documentation, API references, SDK examples, guides, and troubleshooting information through citation-safe hybrid search and static resources.11-