El MCP como fuente única de capacidades — estándar y catálogo
Actualización 2026-09-11 (PR #698): la familia de Empresas pasó a persona-primero.Decisión. El MCP es la fuente de verdad de toda capacidad de Brein. Luna consume las mismas herramientas en proceso. Lo que no se usa se borra. El MCP se estandariza y se simplifica. Broker/integraciones quedan fuera de esta versión. Base auditada (solo lectura):contact_discover,contact_select,contacts_acquired,contact_quoteycontact_unlockse retiraron; su lugar lo ocupanpeople_search,people_quote,people_acquired,people_reveal,people_fileypeople_status. Las filas decontact_*de este doc describen el estado previo a esa retirada. El detalle vigente vive enluna-chat/05-catalogo-core-y-chat.mdyproveedores-una-puerta.md.
apps/mcp/src/**, apps/api/src/{app.module.ts, market-data/market-data-mcp.adapter.ts, cognitive/, luna/}, docs/mcp/, skills/, .driftless/, apps/cli/src, scripts/harness/mcp-e2e.mjs, libs/analytics.
0. Seis hallazgos que corrigen el encargo
No existe evidencia de volumen: se decide con evidencia de referencia. Arreglar la telemetría es una línea, va en Fase A y no recupera historia. Los docs:
overview.mdx lista bien las 18 retiradas, pero documenta como vivas context_comment y note_add y nombra tres inexistentes (driftless_agent_stats, driftless_project, driftless_notion_*); al revés, company_save, contact_select y contact_prepare no aparecen en ningún doc de usuario ni skill.
1. Inventario con evidencia de uso
CLI = fila enCAPABILITY_MATRIX (paridad de capacidad, no llamada). Skill = skills/driftless/. Brein = las 4 skills públicas brein-*. Har. = mcp-e2e.mjs.
Cerebro — 20
Mercado — 13 (las 13 referenciadas por las skills públicas)
CRM, Empresas y contactos, Cuenta — 20
“Broker fuera”, en concreto: (a) el default de
DRIFTLESS_BROKER_ENABLED se invierte a false y se fija así en todos los entornos; (b) driftless_broker sale del array de registro — el handler puede quedarse en el archivo, pero deja de resolverse en tools/call; (c) connectorTools() deja de invocarse desde paginateFor (una línea) y tool-synthesis.ts queda inerte; (d) se borran references/broker.md (9 menciones en 2 archivos, espejadas en .driftless/) y su fila de la tabla de ruteo; (e) docs/mcp/integrations.mdx se marca no disponible. Conectar proveedores sigue siendo flujo humano.
Cuentas. KEEP 20 · RENAME 6 · MERGE 8→2 · DELETE 25 → catálogo objetivo: 24 públicas.
2. El estándar de una herramienta
3. El catálogo objetivo — 24 herramientas
La regla de fusión, del banco 3 de Luna.market_analyze {op, filters} fracasó porque firstMissingRequiredArg (cognitive/registry-tools.ts:143) solo lee required del nivel raíz: un obligatorio dentro de filters era una frase, nunca una compuerta, y 5 de 8 fallos de intención fueron el modelo sin llamar la herramienta. Regla: fusiona solo herramientas sin obligatorios propios distintos. Las 5 búsquedas no tienen ninguno (solo kind); los 2 get comparten record_ref. Las 4 de análisis sí los tienen y se quedan separadas.
Por qué cada conteo: Mercado son las cinco preguntas comerciales reales (¿quién hay?, ¿cuántos?, ¿cuánto?, ¿quién es este?, ¿es riesgoso?) más comparar y el contrato de capacidades. Cerebro es leer y escribir: la gobernanza es acto humano. CRM es leer y mover: configurar el pipeline es humano. Empresas/contactos son los siete pasos del ciclo gobernado, cada uno con su chequeo de derechos — fusionarlos borraría el punto de control. Cuenta son los dos hechos que un agente necesita antes de prometer algo.
4. Cómo Luna consume el MCP en proceso
La costura ya existe y nadie la usó.McpModule.register() exporta ToolRegistry, y AppModule ya lo importa con el adaptador in-process (app.module.ts:334). No hace falta HTTP ni JSON-RPC.
- Handle compartido. Extraer
apps/api/src/mcp.dynamic.tsconexport const MCP = McpModule.register({useExisting: MarketDataMcpAdapter, imports:[MarketDataModule]})e importar esa misma referencia desdeAppModuleyLunaModule(Nest deduplica unDynamicModulepor identidad de objeto).LunaToolsServiceinyectaToolRegistry. - Lista y llamada.
registry.catalog(): DriftlessTool[](Luna conserva sutoRuntimeToolDef) yregistry.call(name, args, ctx)con unToolCallContextarmado desde elPrincipalde la sesión — el mismo que arma el controller, sinauthorizationporque no hay salto de red. - Subconjunto por etapa — recomendación:
stagesen el catálogo, no allowlist en Luna. HoyLUNA_TOOL_STAGESes una tabla paralela por nombre que hay que tocar por cada herramienta nueva; el propio archivo lo admite. Añadirstages?: SessionStage[]aDriftlessTooly quetoolsForStagefiltre por el campo.MAX_VISIBLE_TOOLS = 12se queda en Luna: es regla de la conversación. De las 24, Luna verá 5 ensin_criterioy 11 después;market_capabilitiesse marca sin etapas a propósito, porque la cobertura ya le llega compilada en elTurnInput.
luna-manual.ts, luna-input.compiler.ts y luna-criterion.ts (los dos compiladores), luna-intent.ts (las compuertas parse/schema/refs/pregunta/lexico/cifra), el cosechador de refs y stripRefTokens, luna-stage.ts y el cobro.
Luna borra: luna-tools.ts (551 líneas) salvo MEXICAN_STATES, que sube a contratos con su guardia; y de luna-tools.service.ts (862) las ~600 de despacho, mapeo acción→ruta, recorte por kind y proyección. anchorRow, capOutput y toLunaToolError se mudan al MCP como envelope, presupuesto y error estándar — no se duplican.
next_action y presupuesto contra el validador. Luna hoy quita next_action a propósito (luna-tools.service.ts:7); con el envelope estándar lo verán los gates de cifras y refs. next_action es instrucción, no material citable: el cosechador lo ignora por nombre y el validador lo trata como no-citable, igual que a hint — una línea junto a REF_FIELDS. El tope del MCP (50 KiB) es ~11× el de Luna (4 500 caracteres) y recorta distinto; el outputBudget por herramienta los reconcilia con min(outputBudget, tope) y el recorte por filas, conservando la excepción de las páginas de mercado, que se rechazan en vez de trocearse.
cognitive/tool-registry.ts se adopta, no se retira. Es el único tipo que ya declara efecto, política, costo, idempotencia, presupuesto y ejemplos, y ya trae un spec de contrato (mcp-mapping.contract.spec.ts) que lee el registro vivo del MCP y verifica la derivación. Mover tool-registry.ts + tool-policy.ts + tool-citations.ts a libs/agent-tools y que apps/mcp importe DriftlessTool: eso convierte “el MCP es la fuente de verdad” en algo que defiende el compilador.
5. Orden de migración y tamaño
Total −3 460 / +1 880 líneas, ~59 archivos. 12-14 días de un ingeniero con Claude (A 1,5 · B 3,5 · C 2 · D 1,5 · E 4, más un día de margen en E). A-D son independientes y se mezclan una por una; E necesita A-D dentro.
