trazabilidad-mcp
MCP server that exposes code tracing capabilities including journey flows, HTTP seams, and findings from indexed projects, allowing AI assistants to query software architecture.
README
Trazabilidad
Motor de trazabilidad de código full-stack: construye un grafo de hechos de tu app (componentes → handlers → costuras HTTP → queries DB) y lo expone por web, MCP y CLI desde una sola fuente de verdad. Local-first, sin telemetría, el código nunca sale de tu máquina.
Estado: v1 funcional. El motor completo está implementado y verificado: extractor ts-morph, matcher de costuras HTTP, persistencia SQLite con migraciones, detección de doble-fuente, y las tres superficies vivas (CLI
trazar, API REST en :8791, web en :5183, servidor MCP) y soporte multi-proyecto: un dueño apunta la herramienta a N repos reales. Las tres leen la MISMA query layer → una sola fuente de verdad.
Onboarding de un comando
Requisitos: Docker + Docker Compose.
git clone <repo> && cd trazabilidad
docker compose -f docker-compose.local.yml up --build -d
- Web: http://127.0.0.1:5183 (shell con first-run)
- API: http://127.0.0.1:8791/health (health honesto)
Ambos servicios se publican solo en 127.0.0.1 — nunca expuestos a la LAN (decisión de seguridad de la spec §5).
Para frenar todo:
docker compose -f docker-compose.local.yml down
Ciclo de desarrollo (hot-reload)
El compose usa bind-mount + --watch, no bakea el código:
apps/apicorre conbun --watch→ al guardar un.ts, el proceso reinicia solo.apps/webcorreviteconwatch.usePolling→ HMR cruza el FS del contenedor en macOS.
Editás en el host, el cambio se ve sin reconstruir la imagen. Solo necesitás
up --build la primera vez (o tras tocar package.json / lockfile, para reinstalar deps).
Si cambiás dependencias: docker compose -f docker-compose.local.yml up --build -d recrea
los contenedores y reinstala dentro de ellos.
Configuración de entorno
Convención de tres archivos en env/:
| Archivo | Tracked | Contenido |
|---|---|---|
env/local.env |
sí | config no-secreta (puertos, NODE_ENV) |
env/local.secret.env.example |
sí | todas las keys de secretos, sin valores |
env/local.secret.env |
no (gitignored) | tus valores reales |
v1 no tiene secretos reales (local-only, sin auth, sin servicios externos). El .example
existe para fijar la convención de cara a v2.
Estructura (monorepo Bun + Turborepo)
packages/
domain/ tipos puros del grafo (sin deps)
core/ FactGraphBuilder + ExtractorPort (HEXAGONAL, no importa ts-morph)
matcher/ SeamMatcher (STRATEGY, confianza descendente)
db/ schema SQL + migraciones
query/ query layer COMPARTIDA (fuente única que leen CLI, API y MCP)
apps/
api/ Hono REST :8791 (bind 127.0.0.1) — /health, /index/status, /journeys, /seams, /findings
web/ React + Vite + TanStack Router :5183 — canvas del grafo con polling de estado
mcp/ servidor MCP stdio (SDK 1.29.0) — trace_flow, list_journeys, get_http_seams, index_status, ...
cli/ `trazar` (index/add/projects/remove/overview/flow/journeys/seams/findings[+suppress/confirm]/serve)
examples/
demo-login/ fixture de test: login React + Hono con plantados verificables
CLI trazar
Todos los comandos van sobre la misma query layer que la web y el MCP:
trazar index <ruta> # indexa un proyecto y construye el grafo de hechos
trazar add <ruta> [--name=X] # registra un proyecto en el registro (lo verás en la web)
trazar projects # lista los proyectos registrados (id, name, ruta, estado)
trazar remove <id> # quita un proyecto del registro
trazar overview [<ruta>] # salud del proyecto (healthScore, capas, findings por severidad)
trazar journeys [<ruta>] # lista los journeys (entry points de UI) detectados
trazar flow <journey> [--mermaid] # camino completo botón→DB de un journey (texto o Mermaid)
trazar seams [<ruta>] [--level=L] # costuras HTTP con confianza y file:line de ambos lados
trazar findings [<ruta>] # findings de doble-fuente con evidencia y estado
trazar findings suppress <key> [--reason=...] # suprime un finding (persiste entre re-index)
trazar findings confirm <key> # confirma un finding
trazar serve # cómo levantar API + web (modo Docker demo o host)
add y index son cosas distintas: add registra la ruta en el catálogo (para verla en la
web/MCP por id), index construye el grafo. index no auto-registra (deja un hint copiable);
add no indexa. Mirá la sección Multi-proyecto más abajo.
Honestidad por diseño: sin index → comando copiable; stale tras editar sin re-indexar → ⚠;
seams no resueltas → declaradas como huecos, nunca ocultadas. Exit codes: 0 ok · 2 uso · 1 error.
El proyecto objetivo de los comandos de lectura sale de: arg explícito > TRAZABILIDAD_PROJECT > cwd.
Instalar trazar en el PATH
El bin del CLI apunta a apps/cli/src/index.ts, que ya tiene shebang #!/usr/bin/env bun.
bun link NO sirve acá: en un monorepo de workspaces solo registra el paquete para enlazarlo
en otro proyecto (bun link @trazabilidad/cli), no instala el binario trazar en el PATH
(verificado: tras bun link, which trazar → not found). Dos formas que sí funcionan:
Opción A — symlink al bin dir de bun (recomendada; ~/.bun/bin ya está en tu PATH si usás bun):
chmod +x apps/cli/src/index.ts
ln -sf "$(pwd)/apps/cli/src/index.ts" ~/.bun/bin/trazar
trazar journeys examples/demo-login # ✓ funciona por el shebang
Opción B — alias/función en tu shell (si no querés tocar ~/.bun/bin):
# en ~/.zshrc o ~/.bashrc (ajustá la ruta absoluta del repo):
trazar() { bun "/ruta/al/repo/trazabilidad/apps/cli/src/index.ts" "$@"; }
Sin instalar nada, el CLI siempre corre con bun apps/cli/src/index.ts <comando> desde la raíz del
repo (lo que usan los ejemplos de este README).
Multi-proyecto
Trazabilidad apunta a N proyectos desde un solo dueño. El catálogo de rutas vive en
~/.trazabilidad/projects.json (config, separado de la .db de cada proyecto). Cada superficie
—web, MCP, CLI— resuelve el proyecto de forma independiente; cuando no se especifica, cae al
default (env TRAZABILIDAD_PROJECT), así el demo dockerizado y los tests no se rompen.
Dos modos de arranque
| Modo | Comando | Qué ve | Para qué |
|---|---|---|---|
| Demo (Docker) | docker compose -f docker-compose.local.yml up --build -d |
solo el fixture montado en /repo |
onboarding de un comando, conocer la herramienta |
| Host (bun) | bun run api:host + bun run web:host |
tus repos reales (cualquier path absoluto del host) | trabajar sobre tu código de verdad |
La API en Docker está encajonada al volumen montado; para apuntar a tus repos reales usá el modo
host (la API corre con bun directo en loopback y ve todo el filesystem). Ambos publican solo
en 127.0.0.1.
Registrar y listar proyectos (CLI)
# 1) registrar una o más rutas reales (idempotente; imprime el id estable)
trazar add ~/code/mi-api # → ✓ registrado "mi-api" — id mi-api-3f9a2c
trazar add ~/code/mi-web --name="Front"
# 2) listar el registro con estado (id, name, ruta, ✓/✗ index, ⚠ si la ruta no existe)
trazar projects
# 3) construir el grafo de cada uno (index ≠ add; corré index aparte)
trazar index ~/code/mi-api
# 4) quitar uno del registro (no borra la .db ni el código)
trazar remove mi-api-3f9a2c
El id es estable y se deriva del path absoluto (slug(basename)-hash6), no del basename:
dos repos api en carpetas distintas obtienen ids distintos, sin colisión.
El registro es CLI-only. No hay
POST /addniPOST /indexen la API: registrar e indexar se hacen por el CLI en el host (decisión v1 — la API es de solo lectura sobre el grafo). La web y el MCP leen el catálogo, pero quien lo escribe estrazar add/trazar remove.
Selector en la web
En modo host (bun run web:host), el sidebar muestra un dropdown que lista GET /projects. El
proyecto activo vive en la URL como segmento (/p/<id>/overview, deep-linkeable). Al elegir otro
proyecto, todas las vistas (overview/journeys/seams/findings/status) se re-scopean a ese proyecto.
Si un proyecto está sin indexar o su ruta ya no existe, aparece con un punto rojo y un estado honesto.
Sin proyectos registrados → estado vacío con CTAs copiables (trazar add / trazar index).
El param project del MCP
Cada tool del MCP acepta un project opcional (el id del registro). Con project=<id> responde
sobre ese proyecto; sin el param, sobre el default (TRAZABILIDAD_PROJECT). La tool
list_projects devuelve el catálogo para que el agente descubra los ids.
// un agente consulta dos proyectos en la MISMA sesión, sin reconectar:
{ "tool": "overview", "arguments": { "project": "mi-api-3f9a2c" } }
{ "tool": "list_journeys", "arguments": { "project": "mi-web-b71e04" } }
{ "tool": "overview", "arguments": {} } // ← default (back-compat)
Las tres superficies pueden operar sobre proyectos distintos a la vez sin interferir: son procesos
independientes que leen .dbs distintas, y cada uno cae al default cuando no se especifica.
Flujo completo con el demo
bun install
# 1) indexar el fixture → 22 nodos, 14 aristas, 3 seams (1 finding de doble-fuente)
bun apps/cli/src/index.ts index examples/demo-login
# 2) ver los journeys de UI detectados (onSubmit, signOut)
bun apps/cli/src/index.ts journeys examples/demo-login
# 3) trazar el journey de login botón→DB (con el puente honesto del extractor)
bun apps/cli/src/index.ts flow onSubmit examples/demo-login
# 4) ver el finding estrella: doble fuente de verdad de `user`
bun apps/cli/src/index.ts findings examples/demo-login
# 5) levantar API + web (consumen la MISMA verdad que el CLI)
TRAZABILIDAD_PROJECT="$(pwd)/examples/demo-login" bun apps/api/src/index.ts # :8791
cd apps/web && bun run dev # :5183
Si editás un .tsx del demo sin re-indexar, todas las superficies (CLI, /index/status, /health,
web) reportan stale con ⚠ — la verdad sigue siendo honesta hasta que vuelvas a trazar index.
Conectar el MCP a Claude Code / Claude Desktop / Cursor
El servidor MCP (apps/mcp) expone el grafo de hechos por stdio (trace_flow, list_journeys,
get_http_seams, index_status, find_data_providers, list_findings, overview y list_projects)
leyendo la MISMA query layer que el CLI y la API. Cada tool acepta un project opcional (el id del
registro) para apuntar a un proyecto puntual; sin ese param usa la env TRAZABILIDAD_PROJECT (back-compat).
Ver la sección Multi-proyecto arriba. El MCP solo LEE — indexá antes con trazar index <ruta>.
El paquete expone el bin trazabilidad-mcp (entry apps/mcp/src/index.ts, con shebang
#!/usr/bin/env bun igual que trazar). El entry es directamente ejecutable; bun link registra
el paquete @trazabilidad/mcp para consumirlo desde otro repo.
Flujo de una pasada: index → connect → query
# 1) INDEX — el MCP solo LEE; primero construí el grafo de hechos.
bun apps/cli/src/index.ts index examples/demo-login
# 2) CONNECT — agregá el server a Claude Code en un comando (un único proyecto por env):
claude mcp add trazabilidad \
-e TRAZABILIDAD_PROJECT="$(pwd)/examples/demo-login" \
-- bun "$(pwd)/apps/mcp/src/index.ts"
# ...o versioná el `.mcp.json` de la raíz del repo (ver abajo) y abrí el repo con Claude Code.
# 3) QUERY — confirmá el boot y pedí la primera consulta:
claude mcp get trazabilidad # Status: ✓ Connected
# Desde el agente: `list_projects` → elegí un `id` → `overview` / `trace_flow onSubmit`.
# Smoke real (boot por stdio + las 8 tools + datos del demo), repetible:
bun apps/mcp/smoke.ts # o: bun run --filter @trazabilidad/mcp smoke
.mcp.json versionable (raíz del repo, project-scoped para Claude Code). Claude Code lanza el
server con cwd = raíz del repo, así que el path RELATIVO resuelve sin hardcodear nada — y NO fija
TRAZABILIDAD_PROJECT: el agente elige proyecto pasando project por tool (descubrilos con
list_projects), diseño stateless:
{
"mcpServers": {
"trazabilidad": {
"type": "stdio",
"command": "bun",
"args": ["apps/mcp/src/index.ts"]
}
}
}
Comprobá la conexión con claude mcp get trazabilidad (debe decir Status: ✓ Connected).
Quitalo con claude mcp remove trazabilidad. Usá -s project para escribirlo en el .mcp.json
versionable del repo, o -s user para que esté disponible en todos tus proyectos.
Path relativo vs absoluto. El
.mcp.jsonde la raíz usa un path relativo porque Claude Code arranca el server con cwd = raíz del repo. Claude Desktop / Cursor no garantizan ese cwd: ahí usá rutas absolutas (y, si querés un proyecto fijo, la envTRAZABILIDAD_PROJECT):{ "mcpServers": { "trazabilidad": { "type": "stdio", "command": "bun", "args": ["/ruta/absoluta/al/repo/apps/mcp/src/index.ts"], "env": { "TRAZABILIDAD_PROJECT": "/ruta/absoluta/al/repo/examples/demo-login" } } } }
El fixture: examples/demo-login
App de login completa (React + Hono) que es el contrato de test del producto. Planta a
propósito tres situaciones que los lotes 2 y 5 deben detectar (marcadas con // FIXTURE:):
- Doble fuente de verdad de
user:Dashboard.tsxlo lee deAuthContext,Profile.tsxlo lee directo delocalStorage. → Findingdouble-source(LOTE 5). - Fetch dinámico (
fetchProfile, URL template en runtime) → seam no-resuelta visible (LOTE 2). - Fetch literal
fetch('/api/auth/login')→ costuraliteralcon el handler Hono (LOTE 2).
Typecheck del fixture:
bun install
bun run --filter '@trazabilidad/example-demo-login' typecheck
Limitaciones conocidas v1
Trazabilidad declara sus huecos en vez de ocultarlos. Lo que v1 todavía NO cubre:
sessionStoragey claves dinámicas no se detectan como fuente de estado. El extractor reconocelocalStoragecon clave literal y el Context explícito; una doble-fuente sobresessionStorage(olocalStoragecon una clave armada en runtime) no genera finding yfind_data_providersdevuelve 0 proveedores. Cobertura ampliable en v2.finding_keyvs renames. La identidad de un finding (para que la supresión persista entre re-index) se deriva del contenido del hecho; si renombrás el símbolo/archivo involucrado, la key cambia y una supresión previa no se reasocia automáticamente al finding "equivalente".- Puente del flujo botón→DB es heurístico. Cuando el extractor no puede unir dos tramos con certeza (p. ej. una URL armada en runtime), declara la costura como no-resuelta (hueco visible) en lugar de inventar una arista — preferimos un hueco honesto a una conexión falsa.
- La API es de SOLO LECTURA sobre el grafo + supresión/confirmación de findings. El indexado se
dispara únicamente por CLI (
trazar index); no hayPOST /indexen v1 (cola + progreso es backlog v2). - DB local single-project, single-writer. La base SQLite usa
journal_mode=DELETE(no WAL): se comparte host↔contenedor por bind-mount sobre VirtioFS, donde el-shmde WAL no es coherente y causaba "disk I/O error" intermitentes y estados divergentes. DELETE serializa escritor↔lectores, aceptable para un dev local; no es una DB pensada para concurrencia multi-cliente. - Sin auth ni multi-tenant. Local-first, bind 127.0.0.1, sin telemetría; multi-usuario/remoto es la visión SaaS de v2, no v1.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.