@botmesh/mcp es un servidor MCP (Model Context Protocol) por stdio. Le da a un agente de código (o a un editor) las piezas deterministas del proyecto para escribir, validar, compilar, dibujar, lintear, migrar y comparar un documento de flujo sin arrancar un bot: sin teléfono, sin credenciales, sin red.
Cada herramienta es una función de sus argumentos. El servidor no tiene dependencias de terceros (el transporte JSON-RPC está implementado en el paquete), nunca evalúa código y nunca escribe en disco.
Arrancarlo
Los clientes MCP lo arrancan connpx; no hay que instalar nada antes:
Registrarlo en tu cliente
Claude Code:.mcp.json:
.cursor/mcp.json) y Claude Desktop (claude_desktop_config.json) usan la misma forma mcpServers. VS Code (.vscode/mcp.json):
npx directamente necesita "command": "cmd", "args": ["/c", "npx", "-y", "@botmesh/mcp"].
Herramientas de documentación
--docs <dir> (o BOTMESH_DOCS_DIR; el nombre anterior, BOTWA_DOCS_DIR, se sigue leyendo) apunta a un árbol Markdown local, como la carpeta docs/ de un clon del repositorio; las guías no van dentro del paquete. Sin él las dos herramientas de docs no se anuncian en tools/list, así que un cliente nunca ve una herramienta que no puede funcionar.
Desde el repositorio
.vscode/mcp.json: ejecuta pnpm build una vez y arranca el servidor botmesh desde la vista MCP.
Por programa
createMcpServer, serveStream, runStdio, las revisiones del protocolo, la identidad del servidor y EXAMPLE_FLOW_DOCUMENT) sigue semver; el framing JSON-RPC, el catálogo de herramientas y la búsqueda en la documentación viven en @botmesh/mcp/internal, sin garantía semver.
Herramientas
flow_analyze ejecuta las mismas describeFlows() y lintFlows() que usan bot.describe() y bot.lint(), y flow_* envuelve @botmesh/flow-schema, así que el servidor no puede contradecir al runtime ni al gate de despliegue.
«Válido» o «inválido» es un resultado, no un error
Un documento inválido es una llamada correcta cuyo payload dicevalid: false con los problemas:
isError: true queda reservado para la petición (argumentos ausentes o mal tipados, una versión que no se puede migrar, una ruta fuera del directorio de docs), así que un agente distingue «mi documento necesita trabajo» de «mi llamada estaba mal».
Los argumentos se comprueban contra el inputSchema que anuncia tools/list antes de ejecutar la herramienta: una clave desconocida, una obligatoria que falta, un valor fuera de un enum o de un rango (format, direction, target hasta la versión actual del documento, limit de 1 a 25) o un valor anidado a más de 64 niveles falla con una frase que lo nombra, por ejemplo invalid arguments for flow_validate: unknown argument "doc" (accepted: "document"). Los textos de error nunca llevan rutas absolutas: una página que no existe contesta page not found: guide/x.md.
Protocolo
initializeresponde concapabilities: { tools: { listChanged: false } }, la versión del paquete y un bloqueinstructionscon las reglas caras de equivocar (la API v1 no existe, los documentos son datos, un handler no debe hacerawaitde la conversación que inicia, los ids son opacos).- Revisiones soportadas:
2025-06-18,2025-03-26,2024-11-05. A un cliente que pide otra se le contesta2025-06-18y él decide qué hacer. - Cada herramienta lleva
annotations(readOnlyHint: true,destructiveHint: false,idempotentHint: true,openWorldHint: false), así que un cliente puede ejecutarlas sin pedir confirmación. ping,tools/listytools/call; cualquier otra cosa (resources/*,prompts/*,logging/setLevel) contesta-32601, porque esas capabilities no se anuncian.- Las notificaciones nunca se contestan (JSON-RPC lo prohíbe), incluida
notifications/initialized. - Una respuesta enviada por el cliente (un mensaje con
resultoerrory sinmethod) se ignora; nunca se contesta. - Un mensaje JSON por línea. Una línea malformada contesta
-32700, una línea más larga quemaxMessageLength(16 Mi caracteres por defecto,DEFAULT_MAX_MESSAGE_LENGTH) contesta-32600sin acumularse en memoria, y la sesión sigue. Un array JSON es un batch (revisión2025-03-26): se contesta con un array de respuestas, sin las notificaciones. - Por stdout solo salen mensajes del protocolo; los diagnósticos van a stderr. Si el cliente cierra la tubería, el servidor deja de escribir y termina cuando acaba stdin, sin un
EPIPEsin manejar. Un directorio--docsque no existe se avisa por stderr al arrancar.
Búsqueda en la documentación
Con--docs, docs_search indexa cada .md del directorio salvo la salida generada (api/, dist/, .vitepress/) y las notas de diseño internas (superpowers/), y las parte en secciones por heading (un # dentro de un bloque de código no es un heading).
El ranking es determinista y deliberadamente simple: términos sin acentos ni mayúsculas, frecuencia inversa de documento para que un identificador raro (appendLog) gane a una palabra que aparece en todas las páginas (durabilidad), presencia en vez de conteos brutos, doble peso si el término está en el heading, bonus por cobertura y por frase exacta. Las páginas en inglés y en español se indexan juntas, así que una consulta alcanza a las dos.
docs_page solo lee archivos .md y rechaza cualquier ruta que se salga del directorio de docs.
Tests
pnpm --filter @botmesh/mcp test cubre el framing (mensajes partidos entre chunks, \r\n, una última línea sin salto, chunks en UTF-8, batches, líneas demasiado largas, un fuzz con semilla de chunks crudos), la comprobación de argumentos, cada herramienta con entrada válida e inválida, los 14 tipos de paso, la resolución de subflows, la migración de un documento sin versión, una búsqueda bilingüe sobre un árbol temporal (incluido el rechazo de ../) y la sincronización de la versión con package.json.