ADR-0010: graph-service como MCP centralizado
- Status: Accepted
- Date: 2026-03-26
- Deciders: DeAcero Agentic Team
Context
El pipeline de arqueología requiere un grafo de dependencias del sistema legacy para:
- El
software-archeologist— mapear quién llama a quién, qué tablas lee/escribe cada SP - El
characterization-tester— detectar side effects no reversibles (edges WRITES hacia colas de email, tablas de integración externa, SAP) para clasificar escenarios como UNTESTABLE - Futuros agentes (
cornerstone-builder,bdd-writer) — entender el impacto de cambios
El artefacto que produce este grafo es graph_builder — un pipeline Python de 6 fases que
procesa los fuentes SQL de SQL Server y produce:
graph.json— nodos (SPs, tablas, jobs) con edges (CALLS, READS, WRITES, DELETE, MERGE, RUNS_ON_SCHEDULE, USES_LINKED_SERVER, DEPENDS_ON)tables.json— catálogo de tablas con metadata (esquema, servidor)cross_server_refs.json— referencias a servidores vinculadosignored.md— artefactos ignorados con razón
Problema: modelo actual (implícito)
En el modelo actual, cada proyecto que necesita el grafo debe:
1. Clonar o copiar graph_builder localmente
2. Ejecutarlo contra los fuentes del sistema legacy (potencialmente 13M líneas)
3. Mantener su propia copia de graph.json — posiblemente desactualizada
4. Asumir el costo de cómputo completo en cada proyecto
Esto es exactamente el problema que SQUIT resolvió para la búsqueda semántica SQL: construir una vez, servir a todos vía MCP.
Decision
Crear graph-service como un servidor MCP centralizado, análogo al patrón SQUIT:
[graph_builder pipeline] → pre-computed graph store
↓
[graph-service MCP server]
↓
┌───────────────────────────┼───────────────────────┐
↓ ↓ ↓
software-archeologist characterization-tester otros agentes
Responsabilidades del graph-service
Construcción (offline, centralizada):
- Acepta un directorio de fuentes SQL o un snapshot del repositorio legacy
- Ejecuta el pipeline graph_builder (6 fases) y almacena los grafos resultantes
- Versiona los grafos por fecha de snapshot o hash del fuente
- Soporta reconstrucción incremental por servidor/base de datos
Servicio (online, vía MCP):
Expone las siguientes herramientas MCP:
| Herramienta | Descripción |
|---|---|
get_callers(sp_name) |
Retorna todos los SPs que llaman a sp_name |
get_callees(sp_name) |
Retorna todos los SPs que sp_name llama |
get_reads(sp_name) |
Tablas que sp_name lee (SELECT, JOIN) |
get_writes(sp_name) |
Tablas que sp_name escribe (INSERT, UPDATE, DELETE, MERGE) |
get_side_effects(sp_name) |
Edges hacia sistemas externos: email queues, linked servers, SAP |
get_entry_points(db_name) |
SPs sin callers — puntos de entrada a la lógica de negocio |
get_cross_server_refs(sp_name) |
Referencias a servidores vinculados desde sp_name |
get_schedule(job_name) |
Schedules de SQL Agent jobs asociados a un SP |
query_graph(cypher_or_sql) |
Query abierto contra el grafo (para exploración avanzada) |
Degradación graceful
Si graph-service MCP no está disponible:
- Los agentes leen graph.json local si existe en el proyecto
- Si tampoco existe graph.json, el software-archeologist ejecuta graph_builder localmente
y guarda el output en output/graph/
- El characterization-tester marca como UNTESTABLE todos los escenarios donde no puede
determinar side effects (protocolo conservador)
Implementación técnica
graph-service/
├── server.py ← FastMCP server (expone las herramientas MCP)
├── graph_builder/ ← el pipeline existente (graph_builder.zip) integrado como librería
│ ├── main.py
│ ├── catalog.py
│ ├── sql_parser.py
│ ├── enrichment.py
│ └── output_writer.py
├── store/
│ └── graphs/ ← grafos pre-computados por proyecto/snapshot
└── README.md
El servidor se despliega como contenedor Docker en infraestructura compartida de DeAcero.
La dirección del servidor se configura en el proyecto generado vía .env:
GRAPH_SERVICE_URL=http://graph-service.internal:8080
Flujo en el pipeline
[SQL Server source files]
↓
graph_builder (run once, by infra team)
↓
graph-service store
↓ (MCP tools)
software-archeologist ──→ "qué tablas escribe sp_generar_factura?"
characterization-tester ──→ "¿tiene side effects no reversibles sp_enviar_notificacion?"
bdd-writer ──→ "cuáles son los entry points de [FACTURACION]?"
Consecuencias
Positivo
- Grafo construido una sola vez para toda la organización — no por proyecto
- Todos los agentes acceden a la misma versión del grafo (consistencia)
- El
characterization-testerpuede clasificar UNTESTABLE con certeza en lugar de asumir - Reduce el tiempo de bootstrap de nuevos proyectos de arqueología
- Centraliza el costo computacional del análisis de 5.7M objetos SQL
Negativo
- Requiere infraestructura adicional (contenedor, almacenamiento persistente)
- El grafo puede quedar desactualizado si los fuentes legacy cambian sin re-procesamiento
- Agrega una dependencia de red — proyectos offline no pueden usarlo directamente
(mitigado por la degradación graceful a
graph.jsonlocal)
Dependencias
graph_builder(existente, engraph_builder.zip) — integrado como librería interna- FastMCP (Python) — para el servidor MCP
- Docker — para el deployment centralizado
characterization-tester(ADR-0012) — consumidor primario deget_side_effectssoftware-archeologistskill — consumidor deget_callers,get_entry_points
Alternativas consideradas
Bundlear graph_builder en cada proyecto: Viable para proyectos aislados pero crea N copias
desincronizadas del grafo. A medida que el sistema legacy evoluciona, mantener N grafos actualizados
es inviable. Descartado.
Usar Neo4j como store del grafo: Ofrece queries Cypher más expresivos pero agrega una dependencia operacional significativa. Para el volumen actual (5.7M objetos, ~306 instancias SQL Server) un grafo en memoria o SQLite es suficiente. Puede revisarse si el volumen crece.
Graph-as-a-file (graph.json en el repo): Funciona bien para proyectos pequeños y como fallback. No escala a múltiples proyectos con grafos compartidos ni permite consultas ad-hoc. Se mantiene como fallback, no como solución principal.