Skip to main content
Glama

@ingadhoc/docs-platform

Платформа документации Adhoc: один поисковый движок, одно ядро MCP, один шлюз доступа и один защитный механизм от утечек, которые потребляются пинами из репозиториев контента (oba-docs, odumbo-docs, adhoc-docs).

До этого четыре части жили в форках в трёх репозиториях: один и тот же файл с тремя диалектами, и каждый фикс распространялся вручную — или не распространялся. Измерение находится в docs/unificacion/: lib/mcp/indice.mjs имел 41 различие между тремя копиями, и 17 из них были фиксами, которые были в одном репозитории, а в двух других — нет. Самый дорогой случай: защитный механизм от утечек был байт-идентичным в двух репозиториях и отсутствовал в третьем.

  • ADR 0006 из knowledge-management — один репозиторий на корпус контента, а платформа как отдельный пакет: контент и движок имеют разные жизненные циклы и разных владельцев.

  • ADR 0007 из knowledge-management — шлюз и защитный механизм от утечек принадлежат платформе, а не каждому сайту: защита, которую каждый репозиторий реализует заново, — это защита, которой у какого-то репозитория нет.

  • Этап A спецификации arquitectura-plataforma-docs: этот пакет с двумя версионированными контрактами и drift-check, который делает видимым отставание пина.

Как потребляется

npm i --ignore-scripts github:ingadhoc/doc-platform#v0.1.0

Точный пин, всегда по тегу. Никаких ^, никаких main, никаких веток: пин — это то, что предотвращает поломку трёх сайтов одновременно из-за фикса платформы, и это то, что позволяет откат одной строкой. Диапазон намеренно вызывает сбой docs-drift-check — пин, который не пинует, не является пином.

--ignore-scripts рекомендуется. У этого пакета нет ни одного install-скрипта, и не будет; флаг предназначен для всего дерева, потому что это выполняется в buildCommand публичных сайтов. По той же причине у пакета одна единственная зависимость (minisearch, которая нужна поисковому движку) и ноль devDependencies: минимальная поверхность в сборке.

Что у потребителя уже есть и этот пакет не объявляет: mcp-handler и zod, которые импортирует lib/mcp/mcp-handler.mjs. Это зависимости репозитория, намеренно: репозиторий решает, с какой версией MCP-фреймворка разворачиваться, и пакет не навязывает свою. Все три репозитория имеют их сегодня.

После npm i у репозитория-потребителя остаются три строки клея:

// 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"

&& — не косметика: это то, что прерывает деплой, когда защитный механизм выходит с кодом 1. Не меняй его на ;.

Related MCP server: docrag

Что экспортирует

Импорт

Что это

@ingadhoc/docs-platform/indice

поисковый движок: buscar(), leer(), mapa() по индексу, который выдаёт сборка. Единственный, кто использует minisearch

@ingadhoc/docs-platform/mcp-handler

crearMcp({config, indice, crearIssue}): tools, их схемы по осям, Bearer и транспорт

@ingadhoc/docs-platform/gate

decidir(request, env, {audiencias}) / crearGate(config): решение middleware на edge

@ingadhoc/docs-platform/auth

сравнение токенов за постоянное время (использует node:crypto: только на стороне функции)

@ingadhoc/docs-platform/tokens

грамматика DOCS_MCP_TOKENS, один раз, общая между edge и функцией

@ingadhoc/docs-platform/feedback

crearFeedback(config): tool, которая открывает issue в docs-feedback

@ingadhoc/docs-platform/config

cargarConfig() / validarConfig(): валидатор docs.config.json

@ingadhoc/docs-platform/guard-fuga

correrGuard(), если хочешь вызывать его из своей сборки вместо бинарника

@ingadhoc/docs-platform/middleware

эталонный middleware.js (тот, что находится в корне потребителя)

bin docs-guard-fuga

защитный механизм от утечек, для buildCommand

bin docs-drift-check

drift-check, для CI потребителя

Два контракта

Оба несут schemaVersion, и оба читателя выбрасывают ошибку, если эмитент объявляет более новую версию, чем они умеют читать — или если не объявляет её вообще. Никакой тихой деградации: неправильно отвечающий индекс хуже, чем тот, который не отвечает.

  1. config ↔ платформа: docs.config.json, со схемой, опубликованной в schema/docs.config.schema.json, и валидатором в lib/config.mjs (собственный, без зависимостей: ajv не попадает в сборку публичного сайта). Дизайн каждого поля с измеренными доказательствами находится в docs/unificacion/diseno-eje.md; три текущих конфига в переводе — в mapeo-configs.md.

  2. индекс ↔ движок: его выдаёт tools/build.mjs каждого репозитория, а читает lib/mcp/indice.mjs. Он описан в docs/unificacion/contrato-indice.md.

Ось в таблице

Корпус объявляет одну ось как объект: { tipo, default?, valores[] }.

eje.tipo

корпус

параметр в tools

leer() без значения

подстановочный знак (статьи вне оси)

version

oba-docs

version

выбирает default и сообщает об этом (elegidoPor)

да (relacion/ применяется ко всем)

project

adhoc-docs

project

структурная неоднозначность (не объявляет default)

нет

none

odumbo-docs

(не раскрывается)

—

—

Правило leer() одно и не содержит if по типу оси: оно выбирает только тогда, когда конфиг объявил, кого выбирать. Поведение меняет наличие eje.default, а не тип — и есть тест, который это проверяет, добавляя default корпусу с осью project.

Запуск тестов

npm install && npm test        # 227 casos

bloques требует репозиторий контента (он по-настоящему запускает его tools/build.mjs на фикстурах инцидентов) и пропускается с причиной, если его нет:

DOCS_REPO=~/repositorios/oba-docs node --test tests/bloques.test.mjs

Полоса HTTP-обработчика из mcp.test.mjs (16 случаев) также пропускается с причиной, если в checkout нет mcp-handler/zod, которые являются зависимостями потребителя, а не этого пакета. С обеими установленными mcp даёт 57. Случай без своей capability пропускается явно; он не выполняется в деградированном режиме.


Для jjs — открытые решения

Что этот ансамбль не решает сам. Первые три — из diseno-eje.md §7 и затрагивают контракт; остальные вышли из четырёх анализов и остаются живыми после унификации.

1. Одна ось на корпус: принимается ли потолок?

schemaVersion: 1 допускает одну ось на конфиг, и сегодня этого достаточно для трёх репозиториев. В день, когда корпусу понадобится project × version одновременно, схема этого не выражает, и выход — schemaVersion: 2 с ejes: [...] (множественное число). Рекомендация дизайна: явно принять потолок и позволить реальной потребности переоткрыть его с доказательствами (тот же критерий, что и сигнализация о бампах на Этапе B). Это твоё решение, потому что оно затрагивает major.

2. metadata.types: словарь по корпусу или единый для Adhoc?

Сегодня только у adhoc-docs есть types, и его 6 значений очень похожи на стандарт из knowledge-management (concepto, referencia, procedimiento, troubleshooting, guia, indice). Если словарь принадлежит Adhoc, он не идёт в конфиг каждого репозитория: он идёт в пакет, а конфиг только говорит, требует ли он его. Это решение по управлению контентом, а не по схеме; пока оно не принято, схема оставляет его как список по корпусу (совместимо с обоими выходами).

3. Отказ от защитного механизма от утечек в adhoc-docs: подписываешь?

Схема обязывает объявлять deploy.guardDeFuga, так что тихое опущение больше невозможно. Остаются два выхода, оба защитимы: {"activo": false, "motivo": "…"} (у этого репозитория нет публичной сборки: его шлюз безусловный, а защитный механизм защищает от утечки в публичную сборку), или защитный механизм входит всё равно, как ремень. motivo, который сегодня находится в mapeo-configs.md, буквально гласит "PENDIENTE DE FIRMA (jjs)".

И есть техническая часть, которая не исправляется копированием файла (ВОПРОС 1 из analisis-04-seguridad.md): у adhoc-docs нет блоков :::interno, он не выдаёт site/generated.json с аудиторией и у него нет карты deploy.proyectos. С активным защитным механизмом как есть его сборка падает с самого начала из-за "no existe site/generated.json". Строгий вариант — чтобы он выдавал эти две вещи.

4. Список аудиторий остаётся дублированным, и drift-check пока его не сравнивает

docs.config.json → audiences и middleware.js → AUDIENCIAS должны совпадать, и нет способа избежать дублирования: edge не читает из файловой системы. Это ровно тот тип тихого дрейфа, с которого начался форк. Не хватает случая в CI, который бы их сравнивал (сегодняшний docs-drift-check измеряет пин, а не эту согласованность).

5. Три вещи, которые нужно проверить в репозиториях перед тегированием

  • DOCS_AUDIENCE во всех трёх окружениях каждого проекта Vercel (Production, Preview и Development) до merge, который принимает пакет. При fail-closed проект без переменной возвращает 503. Это безопасное направление, но не бесплатное.

  • --esperada в текущих buildCommand: теперь защитный механизм отклоняет его при работе в Vercel. Если какой-то buildCommand передаёт его сегодня, этот деплой начнёт падать. Не удалось проверить по снимкам.

  • GET MCP возвращает 503, если деплой не объявляет обслуживаемую аудиторию. Это наблюдаемое изменение для потребителя: предварительная проверка Claude Code получает 503 вместо плаката, когда деплой неправильно сконфигурирован.

6. Измеренный долг, который этот пакет не может закрыть

  • Fail-closed препроцессора выводит и затем завершается с ошибкой. С неправильно написанной директивой (::: interno), build.mjs записывает site/docs/** с внутренней строкой внутри и затем завершается с кодом 1. Сегодня утечки нет, потому что buildCommand объединяется через &&: защита находится в операторе, а не в программе. Оно объявлено как todo в tests/bloques.test.mjs, и это исправляет унификация build.mjs — которая не вошла в этот этап.

  • tests/bloques.test.mjs записывает в <repo>/site/ потому что в oba и odumbo выходные данные сборки захардкожены. После запуска набора тестов нужно перегенерировать с помощью npm run gen.

  • Ограничения лексического подхода guard: числа и строки короче 5 символов никогда не имеют зонда (ключ 4821, аббревиатура), изображения не сканируются, и утечка внутри applyBlocks не генерирует зонд. Это указано в заголовке guard; я повторяю это здесь, потому что эту часть можно спутать с покрытием.

  • serverInfo.version по-прежнему захардкожена как '1.0.0' в обработчике. Она должна браться из package.json закреплённого пакета, чтобы клиент MCP мог сообщить, с какой версией платформы он взаимодействовал. Это не изменено: это было бы выдумыванием поведения.

  • Подстановочный знак является свойством tipo оси, а не корпуса. Корпус с осью project не может иметь поперечный документ (eje: null остаётся невидимым для любого фильтра). Если когда-нибудь это понадобится, строгий выход — чтобы контракт индекса запрещал это, пока подстановочный знак выключен, чтобы противоречие возникало на этапе сборки, а не в рантайме.

  • В спецификации сказано "vitest" как соглашение о тестах Этапа A, и ни один из трёх репозиториев не использует vitest: реальное соглашение — и соглашение этого пакета — это нативный node:test. Стоит исправить эту строку, прежде чем кто-то установит vitest, чтобы ей соответствовать.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides RAG (Retrieval Augmented Generation) access to technical documentation through MCP, enabling LLMs to search and retrieve relevant documentation on-demand.
    4
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables searching, reading, and navigating MkDocs documentation sites through MCP tools for keyword, semantic, or hybrid search, document browsing, and project metadata.
    1
    BSD 2-Clause "Simplified"