Catálogo core y chat — el plan que se aprueba antes de escribir código
Encargo: dejar el MCP como la única fuente de capacidades de Brein, borrar lo que no se usa, estandarizar lo que queda, añadir Secuencias como primera capacidad nueva y definir el chat que lo opera. Base:mcp-estandar-y-catalogo.md (la auditoría: 53 herramientas), 01-interfaz-luna.md, 03-lexico.md, 04-harness-y-evals.md (seis benches medidos), apps/mcp/src/tools/tool-registry.ts, apps/api/src/cognitive/tool-registry.ts, apps/api/src/email-campaigns/email-campaign-tools.ts.
Referencias externas: Apollo, Clay, HubSpot, Outreach, Instantly, lemlist, Vercel AI SDK, Claude, Cursor.
Regla de lectura: [REQUISITO] = contrato congelable y evaluable; [SUGERENCIA] = técnica sustituible.
1. Veredicto en cinco líneas
- El MCP es la fuente de verdad; Luna lo consume en proceso y su copia de herramientas se borra. La costura ya existe y nadie la usó:
McpModule.register()exportaToolRegistryyAppModuleya lo importa. 53 → 34 herramientas. - Se borran 22 hoy: las 18 retiradas más
context_share,contact_prepare,membersycontext_delete. No hay borrado desde el chat ni desde el MCP, y las notas mueren: los topics se escriben directo. - Mercado no se toca. Sus 13 se quedan como están; la estandarización a 8 espera al chat. Eso elimina el riesgo más caro del plan: los nombres que las skills públicas ya tienen guardados.
- Secuencias son la primera capacidad nueva: seis herramientas de borrador y previsualización más un preflight de estatutos. Autorizar y activar no es una herramienta — es una decisión que la persona resuelve en sesión.
- El chat es de Brein y de nada más. Rechaza lo de afuera por alcance del cinturón, no por prohibición; muestra cada llamada, cada costo y cada decisión antes de gastar.
2. Qué se elimina ya
Difiere de la auditoría, que contaba 25: ahí las sintetizadas contaban como herramientas y
context_delete estaba en KEEP.
“Broker fuera”, en concreto. DRIFTLESS_BROKER_ENABLED a false por default, fijo en todos los entornos; driftless_broker sale del array de registro y deja de resolverse en tools/call; connectorTools() sale de paginateFor — una línea — y tool-synthesis.ts queda inerte; se borra references/broker.md (9 menciones en 2 archivos, espejadas en .driftless/) y su fila de ruteo; docs/mcp/integrations.mdx se marca no disponible. Conectar proveedores sigue siendo flujo humano.
“Las notas mueren”, en concreto. propose desaparece de context_create (hoy su default es true y manda el topic a Cambios pendientes). El topic nace como conocimiento del workspace. Lo que sustituye a la cola: las seis reglas validadas al escribir — área obligatoria, patrón validado contra el checkout, bloqueo de secretos — y, en el chat, la tarjeta de un toque.
3. El estándar de una herramienta
Estado: ✓ ya está · ≈ parcial · + nuevo.4. El catálogo core objetivo
34 públicas = 21 core + 13 de Mercado sin tocar. El registro tiene 36 definiciones: las dos de sesión (board_read, selection_update) viven ahí con external:false — nunca salen en tools/list, pero comparten tipo, sobre y presupuesto.
Costo: gratis · lectura (cuenta en el techo horario) · créditos.
Empresas y personas — 8
CRM — 2
Cerebro — 4
Cuenta — 2
Secuencias — 6 · nueva
No existen en el MCP. El motor sí (EMAIL_CAMPAIGN_TOOL_CONTRACT, autorizaciones inmutables, dispatcher por Nylas) tras EMAIL_CAMPAIGNS_ENABLED. Se envuelve, no se reescribe. Todas gratis.
No hay
sequence_activate. Nadie en la industria expone envío masivo: el patrón universal es borrador → destinatarios → un solo cuello de botella de activación. El nuestro es la tarjeta revisar_secuencia, con una casilla por paso, resuelta en sesión; assertHumanSession se cumple igual que en el dashboard.
Mercado — 13, sin tocar
market_capabilities, market_search_suppliers · _opportunities · _awards · _permits · _risks, market_get_supplier · _opportunity, market_count_suppliers, market_compare_segments, market_aggregate_awards, market_get_supplier_history, market_screen_risks. Se estandarizan (13→8, con kind y filters cerrado por kind) después de que el chat esté vivo.
Total: 21 core + 13 Mercado = 34 públicas · 36 definiciones.
5. Cómo Luna consume el MCP en proceso
La costura ya existe:McpModule.register() exporta ToolRegistry y AppModule ya lo importa con el adaptador in-process (app.module.ts:334). Sin HTTP ni JSON-RPC.
- Handle compartido. Extraer
apps/api/src/mcp.dynamic.tscon la referencia delDynamicModulee importar esa misma desdeAppModuleyLunaModule(Nest deduplica por identidad de objeto).LunaToolsServiceinyectaToolRegistry. - Lista y llamada.
registry.catalog(): DriftlessTool[]yregistry.call(name, args, ctx), con unToolCallContextarmado desde elPrincipalde la sesión, sinauthorization. - Subconjunto por etapa:
stagesen la definición, no allowlist en Luna[REQUISITO]. HoyLUNA_TOOL_STAGESes una tabla paralela por nombre que hay que tocar por cada herramienta nueva.MAX_VISIBLE_TOOLSse queda en Luna: es regla de la conversación.market_capabilitiesva sin etapas — la cobertura llega compilada en elTurnInput.
context_retrieve, context_get, usage, board_read, market_count_suppliers (5, visibles en todas).
[Resuelto 2026-09-10] El tope de 12 era una heurística de S1 (
MAX_VISIBLE_TOOLS), nunca una decisión del founder; subió a 20 y buscando ve Mercado completo (17 herramientas), con_lista ve las Secuencias (17). Lo que sigue es histórico. compare_segments, screen_risks, get_supplier_history, search_permits/_risks) quedan fuera de buscando, y el banco viejo depende de ellas. Es el costo concreto de aplazar el 13→8: o el tope sube a 14 solo en buscando, o Mercado se estandariza antes (§11).
Luna conserva: luna-manual.ts, luna-input.compiler.ts, luna-criterion.ts, luna-intent.ts (las seis compuertas), el cosechador de refs, stripRefTokens, luna-stage.ts y el cobro.
Luna borra: luna-tools.ts (551 líneas) salvo MEXICAN_STATES, que sube a libs/market-data-contracts 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.
next_actions y el presupuesto contra el validador. Luna hoy quita next_action a propósito; con el sobre estándar lo verán los gates. Regla: next_actions[], warnings[] y hint son instrucción, no material citable — el cosechador los ignora por nombre y el validador los trata como no-citables, una línea junto a REF_FIELDS. cost{} sí es citable: “20 créditos” es una cifra que la narración dirá, y entra al set con el ref de la llamada.
La cifra derivada, el defecto que mata turnos buenos. Bench 6: 5 de 6 defectos y los 2 rechazos fueron promedios calculados bien que no pudieron anclarse porque no venían en ningún payload. [REQUISITO] todo agregado devuelve promedio y total con su ref. [SUGERENCIA] el gate acepta además una cifra derivada por suma, promedio o porcentaje sobre cifras del mismo ref.
6. El chat, comportamiento por comportamiento
Base: assistant-ui con External Store (instalado, 0.15.8) sobre el contrato de eventos de01-interfaz-luna.md §6. El servidor es la única autoridad.
6.1 Llamadas visibles [REQUISITO]. La máquina de estados de tool-part del Vercel AI SDK es el contrato de renderizado; nuestros eventos mapean 1:1.
Implementación:
makeAssistantToolUI por familia — seis componentes, no treinta y cuatro. La etiqueta sale de TOOL_LABELS con el léxico corregido (COUNT_LABEL del bench cambia en lockstep).
6.2 Regenerar. Variantes hermanas, nunca destructivo (Claude, Cursor): el turno se re-emite con el mismo parent_turn_id y un variant nuevo, y assistant-ui lo pinta con su BranchPicker. [REQUISITO] las herramientas de la variante anterior no se re-ejecutan con argumentos idénticos — se reutiliza el resultado por search_id/ref, así que regenerar no cuesta créditos ni lecturas.
6.3 Borrar un turno. No borra nada: el log de la Work Session es la autoridad. Se escribe turn.retracted {turn_id} y la proyección deja de mostrarlo. [REQUISITO] los efectos ya ejecutados no se deshacen, y el evento lo dice con esas palabras.
6.4 Compactar — la escalera. Un peldaño por turno, como máximo.
[REQUISITO] compactar no puede romper el anclaje: el peldaño 1 conserva refs, labels y cifras para que unresolved_refs siga en 0.
6.5 Recordar. Topics editables con aprobación antes de guardar. Luna propone dejar_nota; la tarjeta muestra el cuerpo editable; al tocar, context_create escribe directo. [REQUISITO] la memoria de equipo es Cerebro y nada más. Lo que solo sirve en esta sesión vive en snapshot.facts y muere con ella.
6.6 Rechazar lo que no es de Brein. Por alcance, no por prohibición: el cinturón por etapa define lo que existe, sin un system prompt que enumere prohibiciones. Si el mensaje no cae en ninguna capacidad, el turno termina en limite {tipo, detalle} más una redirección a la acción más cercana: “No puedo abrir tu bandeja de Gmail — ese conector no existe hoy. Sí puedo dejarte la secuencia lista para que la autorices.”
6.7 Interrupción de dos niveles. Detener (Esc): el bucle corta en el siguiente límite de paso; las herramientas en vuelo terminan — no se cancelan a medias, no se pierde lo cobrado — y el turno cierra como cierre parcial con lo anclado. Dirigir sin detener: el mensaje entra como steering al mismo turno. [REQUISITO] un mensaje durante una decisión pendiente es steering, no una resolución: resolver la tarjeta exige tocarla.
6.8 Tarjetas: cotizar → tarjeta → resolver. Toda tarjeta nace de una cotización real, nunca de una estimación del modelo, y lleva qué, cuánto cuesta, cuánto queda, cuándo vence (10 min) y qué pasa si aceptas o no. Aprobación por lote: la fatiga de aprobación mide ~93 % de sellos automáticos cuando cada acción pide un clic, así que una tarjeta por lote — “Revelar 10 contactos · 20 créditos · te quedan 140” es UNA tarjeta con diez casillas. Compuertas duras solo para gasto e irreversible; el resto es nivel 1 con vista previa o nivel 0 con deshacer. El modelo nunca ve quote_token, idempotency_key ni correlation_id.
7. Memoria y compresión (S2)
8. Los nuevos briefs del bench
Reemplazan el set actual, que mide Mercado. Cubren el flujo core: Empresas → enrichment → lista → CRM → secuencia → Cerebro. Distribución esperada: 8cierre, 6 proponer_decision, 3 responder, 3 limite.
Este set mide por primera vez los evals 1, 2 y 8 de
01-interfaz-luna.md §9. Los ocho medidores actuales se conservan y se añade uno: intento 1 vs intento 2 — bench 6 mostró que la reparación puede cambiar un cierre con tres anclas por un párrafo vacío que pasa todas las reglas.
9. Orden de ejecución y tamaño
≈19 días de un ingeniero con Claude (A 2 · B 3 · C 3 · D 2,5 · E 3,5 · F 5). A→B→C secuenciales; D en paralelo a C; E necesita B+C+D; F necesita E. −3 230 / +4 000 líneas, ~93 archivos.
Lo que NO se toca: las 13 de Mercado (esquemas, cursores sellados, cobro, la cabecera
X-Driftless-Model-Exposure: abstracted), el motor de secuencias y su ledger de autorizaciones inmutables, CommercialUsageExecutionService, el CLI (habla HTTP contra la API: borrar del MCP no puede romperlo), la Work Session en base de datos y el gateway de modelos.
10. Riesgos y mitigaciones
11. Decisiones que quedan para el founder
El tope de herramientas visibles enResuelto el 2026-09-10: el tope era mío, no del founder; subió a 20.buscando- Escribir a Cerebro desde el MCP, fuera del chat: ¿directo (lo que dice este doc), o con tarjeta también para agentes externos? Borra la única cola de revisión que existe.
- Guardar al CRM: ¿nivel 1 con tarjeta, o nivel 0 con deshacer? Este doc lo deja en nivel 1 porque toca el sistema de registro del equipo; nivel 0 es más rápido y defendible.
people_revealen el catálogo público: hoy un agente externo puede gastar créditos del workspace conconfirm:truey unquote_tokenvivo. ¿Se queda (los clientes ya la usan) o sale y vive solo como resolutor del chat?- El nombre “Luna”: en producto es el modelo gestionado; en código
gpt-5.6-lunaes el fallback caro y el primario esdeepseek-v4-flash. Pendiente desde01-interfaz-luna.md.
