doc-platform
Registers MDX components and theme CSS into a Docusaurus site, enabling video embeds, country badges, and customized article headings.
Embeds YouTube videos through a Video component that shows a thumbnail first and loads an autoplaying iframe only on click.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@doc-platformsearch docs for audience gating setup"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
@ingadhoc/docs-platform
The Adhoc documentation platform: one search engine, one MCP core, one access gate, and one leak guard, consumed pinned by the content repos (oba-docs, odumbo-docs, adhoc-docs).
Before this, the four pieces lived forked across the three repos: the same file with three dialects, and each fix propagated by hand — or not propagated at all. The measurement is in docs/unificacion/: lib/mcp/indice.mjs had 41 differences between the three copies, and 17 were fixes that one repo had and the other two didn't. The most expensive case: the leak guard was byte-identical in two repos and didn't exist in the third.
ADR 0006 from
knowledge-management— one repo per content body, and the platform as a separate package: content and engine have different lifecycles and different owners.ADR 0007 from
knowledge-management— the gate and the leak guard belong to the platform, not to each site: a protection that each repo reimplements is a protection that some repo doesn't have.Stage A of the
arquitectura-plataforma-docsspec: this package, with the two versioned contracts and the drift-check that makes the pin lag visible.
How it's consumed
npm i --ignore-scripts github:ingadhoc/doc-platform#v0.1.0Exact pin, always by tag. No ^, no main, no branches: the pin is what prevents a platform fix from breaking three sites at once, and it's what allows a one-line rollback. A range makes docs-drift-check fail on purpose — a pin that doesn't pin isn't a pin.
--ignore-scripts recommended. This package has no install script and never will; the flag is for the whole tree, because this runs in the buildCommand of public sites. Same reason the package has a single dependency (minisearch, which the search engine needs) and zero devDependencies: minimal surface in the build.
What the consumer already has and this package doesn't declare: mcp-handler and zod, which lib/mcp/mcp-handler.mjs imports. They're repo dependencies, on purpose: the repo decides which MCP framework version it deploys with, and the package doesn't impose one. All three repos have them today.
After npm i, the consuming repo is left with three lines of glue:
// 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"The && isn't cosmetic: it's what aborts the deploy when the guard exits with 1. Don't change it to ;.
Related MCP server: Markdown RAG MCP
What it exports
Import | What it is |
| search engine: |
|
|
|
|
| constant-time token comparison (uses |
| the |
|
|
|
|
|
|
| the reference |
bin | the leak guard, for the |
bin | the drift-check, for the consumer's CI |
The two contracts
Both carry schemaVersion, and both readers throw if the emitter declares a newer version than they know how to read — or if it doesn't declare one at all. No silent degradation: a wrong index that answers badly is worse than one that doesn't answer.
config ↔ platform:
docs.config.json, with schema published atschema/docs.config.schema.jsonand validator inlib/config.mjs(own, dependency-free:ajvdoesn't enter the build of a public site). The design of each field, with the measured evidence, is indocs/unificacion/diseno-eje.md; the three current configs translated, inmapeo-configs.md.index ↔ engine: emitted by each repo's
tools/build.mjsand read bylib/mcp/indice.mjs. It's specified indocs/unificacion/contrato-indice.md.
The axis, in a table
The corpus declares one axis as an object: { tipo, default?, valores[] }.
| corpus | param in the tools |
| wildcard (articles outside the axis) |
| oba-docs |
| picks the | yes ( |
| adhoc-docs |
| structured ambiguity (doesn't declare | no |
| odumbo-docs | (not exposed) | — | — |
The leer() rule is one and has no if per axis type: it only picks when the config declared who to pick. What changes behavior is the presence of eje.default, not the type — and there's a test that proves it by putting a default on a corpus with a project axis.
Running the tests
npm install && npm test # 227 casosbloques needs a content repo (it runs its real tools/build.mjs over the incident fixtures) and is skipped with a reason if there isn't one:
DOCS_REPO=~/repositorios/oba-docs node --test tests/bloques.test.mjsThe HTTP handler slice of mcp.test.mjs (16 cases) is also skipped with a reason if the checkout doesn't have mcp-handler/zod, which are consumer dependencies and not this package's. With both installed, mcp gives 57. A case without its capability is explicitly skipped; it doesn't run degraded.
For jjs — open decisions
What this assembly does not solve on its own. The first three are from
diseno-eje.md §7 and commit the contract; the rest came out of the four
analyses and are still alive after unifying.
1. A single axis per corpus: is the ceiling accepted?
schemaVersion: 1 supports one axis per config, and today that's enough for the three repos. The day a corpus needs project × version at the same time, the schema can't express it and the way out is a schemaVersion: 2 with ejes: [...] (plural).
Design recommendation: accept the ceiling explicitly and let a real need reopen it with evidence (same criterion as the bump alarm in Stage B). It's your call because it commits the major.
2. metadata.types: per-corpus vocabulary or a single Adhoc one?
Today only adhoc-docs has types, and its 6 values look a lot like a knowledge-management standard (concepto, referencia, procedimiento, troubleshooting, guia, indice). If the vocabulary is Adhoc's, it doesn't go in each repo's config: it goes in the package, and the config only says whether it's required. It's a content governance decision, not a schema one; until it's settled, the schema leaves it as a per-corpus list (compatible with both outcomes).
3. The leak guard opt-out in adhoc-docs: will you sign it?
The schema requires declaring deploy.guardDeFuga, so silent omission is no longer possible. Two outcomes remain, both defensible: {"activo": false, "motivo": "…"} (that repo has no public build: its gate is unconditional, and the guard protects against leaks to the public build), or the guard goes in anyway, as a seatbelt. The motivo currently in mapeo-configs.md literally says "PENDING SIGNATURE (jjs)".
And there's a technical part that isn't fixed by copying the file (QUESTION 1 from analisis-04-seguridad.md): adhoc-docs has no :::interno blocks, doesn't emit site/generated.json with audience, and has no deploy.proyectos map. With the guard active as-is, its build fails from the start with "site/generated.json doesn't exist". The strict path is for it to emit those two things.
4. The audience list is still duplicated, and the drift-check still doesn't compare it
docs.config.json → audiences and middleware.js → AUDIENCIAS have to match, and there's no way to avoid the duplication: the edge doesn't read from the filesystem. It's exactly the kind of silent drift where the fork started. A CI case that compares them is missing (today's docs-drift-check measures the pin, not that coherence).
5. Three things to check in the repos before tagging
DOCS_AUDIENCEin all three environments of each Vercel project (Production, Preview, and Development) before the merge that adopts the package. With fail-closed, a project without the variable returns 503. It's the safe direction, but it isn't free.--esperadain the currentbuildCommands: the guard now rejects it running on Vercel. If any buildCommand passes it today, that deploy starts failing. It couldn't be verified from the snapshots.The MCP GET returns 503 if the deployment doesn't declare a servable audience. It's an observable change for the consumer: Claude Code's preflight receives 503 instead of the banner when the deployment is misconfigured.
6. Measured debt this package can't close
The preprocessor's fail-closed emits and then fails. With a misspelled directive (
::: interno),build.mjswritessite/docs/**with the internal line inside and then exits with 1. Today it doesn't leak because thebuildCommandchains with&&: the protection is in the operator, not in the program. It's declared astodointests/bloques.test.mjs, and the unification ofbuild.mjsfixes it — which did not make it into this stage.tests/bloques.test.mjswrites to<repo>/site/because in oba and odumbo the build output is hardcoded. After running the suite you have to regenerate withnpm run gen.Limits of the guard's lexical approach: numbers and strings shorter than 5 characters never get a probe (a key
4821, an acronym), images are not scanned, and a leak insideapplyBlocksdoesn't generate a probe. It's in the guard's header; I repeat it here because it's the part that can be confused with coverage.serverInfo.versionis still hardcoded to'1.0.0'in the handler. It should come from thepackage.jsonof the pinned package, so an MCP client can report which version of the platform it talked to. It wasn't changed: that would be inventing behavior.The wildcard is a property of the axis
tipo, not of the corpus. A corpus with aprojectaxis cannot have a cross-cutting document (eje: nullstays invisible to any filter). If it's ever needed, the strict output is that the index contract prohibits it while the wildcard is off, so the contradiction fails at build time and not at runtime.The spec says "vitest" as the test convention for Stage A, and none of the three repos uses vitest: the real convention —and this package's— is native
node:test. It's worth fixing that line before someone installs vitest to comply with it.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
No tool schema history has been recorded yet.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
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.203MIT
- AlicenseNot gradedqualityDmaintenanceProvides semantic search over markdown documentation using RAG, allowing natural language queries and integration with MCP clients.1MIT
- 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"
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ingadhoc/doc-platform'
If you have feedback or need assistance with the MCP directory API, please join our Discord server