ContextForge MCP Server
Enables AI agents to efficiently retrieve relevant code context via PageRank-optimized subgraphs and automate spec generation, implementation planning, and pre-commit validation.
README
ContextForge
Pipeline determinista que convierte cualquier repo en un índice consultable por agentes IA. 94.4 % menos tokens por sesión, 17.9× compresión, ÷28× costo SDD con prompt caching — sin LLM en el pipeline, sin lock-in de agente.
214 600 tokens (todo el repo) → 11 988 tokens (lo que importa a la tarea)
El problema que resuelve
Cada vez que un agente IA (Claude Code, Cursor, OpenCode) abría el repo tenía que:
- Leer TODO para entender qué existe
- Recordar correr
pnpm forge contextmanualmente antes de cada tarea - Adivinar qué archivos tocar sin contexto del grafo
- No había gate pre-commit — specs y guardrails se saltaban fácil
Ahora todo eso es automático vía MCP + hooks.
Cómo encaja cada pieza
Dev / Agente (Claude Code · Cursor · OpenCode) escribe tarea
│
▼
[Hook UserPromptSubmit] ← automático, sin hacer nada
→ MCP: select_agent_context(task)
→ Agente recibe: skills relevantes + domains tocados
│
▼
Agente llama forge_context(task) ← MCP tool, sin pnpm, sin disco
→ ~20 archivos rankeados por PageRank + BFS
→ sabe exactamente qué leer, no el repo entero
│
▼
Agente llama forge_spec("mi-feature") ← MCP tool
→ escribe openspec/changes/mi-feature/
├── proposal.md ← motivación y alcance
├── design.md ← decisiones técnicas
├── tasks.md ← lista de tareas
├── specs/ ← Requirements + Scenarios (Given/When/Then)
├── graph.subset.json ← subgrafo FROZEN de los archivos relevantes
├── context.md ← mapa de lectura para el agente
└── agent-manifest.json ← skills filtradas SOLO para este change
│
▼
Agente/Dev llena proposal.md + specs/*.md
openspec validate mi-feature ← verifica formato Requirement + Scenario
│
▼
Agente llama forge_implement("mi-feature") ← MCP tool
→ .contextforge/implement-plan.json
allowedFiles: [solo los del context-pack]
maxLocDelta: estimado del pack
│
▼
Agente implementa código
│
▼
Agente intenta git commit
│
▼
[Hook PreToolUse] ← automático, bloquea si hay violaciones
→ node .claude/hooks/forge-pre-commit.mjs
→ forge implement --check valida git diff vs guardrails
→ archivo fuera de allowedFiles → exit 2 → COMMIT BLOQUEADO
→ Agente recibe lista de violaciones y corrige antes de reintentar
│
▼
Commit pasa → merge
│
▼
Agente corre openspec archive mi-feature
│
▼
[Hook PostToolUse(openspec)] ← automático, en background
→ forge_rebuild_graph async
→ grafo fresco para la próxima tarea
ContextForge no reemplaza OpenSpec — lo alimenta
OpenSpec es el framework de specs. Claude Code / Cursor / OpenCode son los agentes IA que lo usan. ContextForge le da a los agentes el contexto correcto para que OpenSpec reciba datos reales del código, no inventados.
| OpenSpec hace | ContextForge aporta |
|---|---|
Valida formato Requirement: + Scenario: |
El contexto correcto para escribir la spec |
Archiva y mueve specs a openspec/specs/ |
El subgrafo frozen que dice qué archivos aplican |
openspec validate chequea Given/When/Then |
forge_check chequea que el código respete los guardrails |
| Historial de specs por feature | forge_status muestra qué changes tienen subgrafo activo |
Antes vs ahora
Sin hooks ni MCP — el dev recordaba (o no) cada paso:
pnpm forge scan
pnpm forge graph
pnpm forge context "mi tarea"
pnpm forge spec mi-feature
# llenar docs...
pnpm forge implement mi-feature
# codear...
pnpm forge implement --check # se olvidaba frecuentemente
git commit
openspec archive mi-feature
pnpm forge scan && pnpm forge graph # se olvidaba siempre
Ahora — los hooks y MCP tools hacen el trabajo:
# Agente recibe contexto automático al escribir el prompt (hook)
# Llama forge_context → forge_spec → forge_implement via MCP (sin pnpm)
# Dev/agente llena las specs → openspec validate confirma formato
# Agente codea
git commit # → hook bloquea si hay violaciones, las explica
openspec archive mi-feature # → hook reconstruye grafo en background
pnpm forge manual solo para bootstrap (forge init) o forzar reconstrucción (forge graph --force).
Instalación
npm — público, sin token:
# CLI (uso diario)
pnpm add -g @anai-raia-alex/contextforge-cli # global
# o
pnpm add -D @anai-raia-alex/contextforge-cli # por proyecto
# MCP server (para agentes vía Node)
pnpm add -D @anai-raia-alex/contextforge-mcp
# OpenSpec CLI (recomendado, activa el modo handoff)
npm i -g @fission-ai/openspec
Docker — sin instalar Node, multi-arch (amd64+arm64):
docker pull ghcr.io/alejandro-cedeno-10/contextforge-mcp:latest
Desde fuente (desarrollo):
git clone https://github.com/alejandro-cedeno-10/contextforge-cli.git
cd contextforge-cli
pnpm install && pnpm build
Quick start (5 comandos para empezar)
# 1. Inicializar (corre 'openspec init' si está en PATH; instala instrucciones del agente)
pnpm forge init
# 2. Indexar el repo (incremental; cache por hash)
pnpm forge scan
pnpm forge graph
# 3. Por cada tarea
pnpm forge context "fix race en tokenLedger writer"
# 4. SDD opcional (con OpenSpec)
pnpm forge spec mi-feature-id # spec-input + handoff/fallback
openspec validate mi-feature-id
# 5. Implementar con guardrails
pnpm forge implement mi-feature-id
# (trabajas con el agente)
pnpm forge implement --check # gate pre-commit
Las tres capas
Capa 1 — Grafos para ahorrar tokens
En lugar de "leer todo el repo", rankeamos los archivos por relevancia a tu tarea con el grafo de dependencias real del código + Personalized PageRank.
Pipeline:
pnpm forge scan # Indexa con BLAKE3 (incremental)
pnpm forge graph # Grafo file + symbol con tree-sitter
pnpm forge context "<tarea>" # PageRank + BFS + budget
Algoritmo:
1. resolve_seeds(task) → archivos semilla por keywords
2. personalized_pagerank() → ranking de relevancia (alpha=0.85, 50 iter)
3. bfs_expand(depth=2) → captura deps directas + transitivas
4. score_nodes() → pagerank × (1/bfs_dist) × edge_multiplier
5. greedy_pack(budget=12000) → llena hasta 12k tokens (full → excerpt → summary)
Edge multipliers: tests=1.2 · defines=1.0 · calls=1.0 · imports=0.8 · references=0.6
Ahorro medido en este repo:
| Sin ContextForge | Con context-pack |
|---|---|
| 214 600 tokens | 11 988 tokens |
| ~$0.64 / sesión (Claude Sonnet 4.6) | ~$0.036 / sesión |
| 128 archivos enviados | 50 archivos seleccionados |
Compresión: 17.9× · Ahorro: 94.4 %
Capa 2 — SDD apoyando a OpenSpec (no compitiendo con él)
v0.3.0+ —
forge specya no genera el spec final. Genera la entrada (spec-input.json+spec-prompt.md) para que OpenSpec o un agente IA lo escriban con todo el contexto correcto.
Tres roles separados, cada uno hace lo suyo:
┌─────────────────────┐ ┌─────────────────────┐ ┌────────────────┐
│ ContextForge │ │ OpenSpec │ │ Agente IA │
├─────────────────────┤ ├─────────────────────┤ ├────────────────┤
ROL │ Selección de │ │ Estructura + │ │ Redacción │
│ contexto │ │ validación │ │ │
├─────────────────────┤ ├─────────────────────┤ ├────────────────┤
TÉC. │ PageRank + BFS + │ │ Schemas RFC 2119 │ │ LLM │
│ budget tokens │ │ Given/When/Then │ │ │
├─────────────────────┤ ├─────────────────────┤ ├────────────────┤
INPUT │ scan + graph + │ │ spec-input.json │ │ spec-prompt.md│
│ task │ │ │ │ + context-pack│
├─────────────────────┤ ├─────────────────────┤ ├────────────────┤
OUTPUT │ context-pack + │ │ proposal/design/ │ │ .md llenos │
│ spec-input + prompt│ │ tasks/spec.md │ │ + commits │
└─────────────────────┘ └─────────────────────┘ └────────────────┘
Pipeline SDD completo:
pnpm forge context "<tarea>" # context-pack + agent-manifest
pnpm forge spec mi-feature-id # spec-input + handoff/fallback
openspec list # ver changes activos
openspec validate mi-feature-id # validación oficial
pnpm forge implement mi-feature-id # plan con guardrails
# (trabajas con el agente)
pnpm forge implement --check # gate pre-commit
openspec archive mi-feature-id -y # al mergear: mueve a specs/
Modo handoff vs modo fallback:
| Modo | Cuándo | Qué pasa |
|---|---|---|
| Handoff (default si OpenSpec CLI está) | openspec --version resuelve |
forge spec ejecuta openspec new change + emite .contextforge/spec-prompt.md con instrucciones canónicas + contexto del grafo. El dev pega ese prompt en su agente |
Fallback (sin OpenSpec CLI o --no-openspec) |
CLI ausente, CI sin instalación | ContextForge emite el scaffold con formato moderno (### Requirement: + #### Scenario:). Cuando se instale el CLI después, openspec validate pasa sin retoque |
Mismo contrato (spec-input.json), dos formas de consumirlo. Portable Mac/Linux/Windows.
Forma de cada Requirement (RFC 2119 + Given/When/Then):
### Requirement: System produces fix-token-race within budget
The system MUST implement the change `fix-token-race` while respecting
`guardrails.allowedFiles` and not exceeding `guardrails.maxLocDelta`.
#### Scenario: change is implemented within scope
- **Given** a valid `.contextforge/context-pack.json` for the task
- **When** the developer runs `pnpm forge implement fix-token-race`
- **Then** `pnpm forge implement --check` exits with code 0
Guardrails de implementación (derivados del grafo):
forge implement produce implement-plan.json cuyos campos vienen del context-pack:
allowedFiles[]← exactamente los archivos del packforbiddenPaths[]— nunca tocarmaxLocDelta— derivado del tamaño del packmaxFilesChanged—allowedFiles.length + 2
forge implement --check valida tu git diff antes del commit. Si te saliste del scope, te lo dice exactamente y el commit no pasa.
Capa 3 — Solo cargar las skills/rules que aplican a la tarea
forge context ya emite 5 artefactos sin paso extra:
.contextforge/context-pack.json ← archivos relevantes
.contextforge/token-ledger.json ← métricas
.contextforge/agent-manifest.json ← NEUTRAL (validado por schema)
.claude/agent-manifest.md ← renderer Claude Code
.cursor/rules/contextforge-active.mdc ← renderer Cursor (Auto-Attached)
.contextforge/manifests/opencode-readme.md ← renderer OpenCode
Reglas de matching (priorizadas):
alwaysApply: true→ siempre incluida.- frontmatter
domains: [..]∩ dominios tocados → match domain. - nombre
contextforge-domain-<slug>del dominio tocado → match explicit (legacyctx-<slug>aún soportado). - nada matchea → skipped (con razón).
Frontmatter mal formado nunca crashea: cae en skipped con frontmatter parse error.
Activación por agente (sin pasos manuales después de setup):
| Agente | Cómo se carga | Setup |
|---|---|---|
| Claude Code | Hook UserPromptSubmit regenera el manifest en cada prompt |
Pegar snippet en .claude/settings.json (ver docs/integrations/claude-code-hook.md) |
| OpenCode | El agente llama tool MCP select_agent_context({task}) al inicio. Live, sin disco |
opencode.json ya configurado |
| Cursor | Rule Auto-Attached con globs: por dominio. Se activa al abrir un archivo del dominio |
Sin setup; forge context regenera la rule |
El grafo en detalle
Esta sección explica qué genera forge graph, cómo lo genera, cómo lo visualiza, y cómo el agente lo consume. Si vienes de Understand-Anything, vas a reconocer la idea: convertir un repo en un mapa navegable. La diferencia es que aquí el pipeline es 100 % determinista (sin LLM en el loop, sin coste por correrlo), cacheable por archivo, y los outputs son JSON portables que cualquier agente puede consumir sin la app.
Pipeline de generación (qué pasa cuando corres forge graph)
┌──────────────┐ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ scan.json │ → │ per-file │ → │ graph passes│ → │ graph.json │
│ (BLAKE3) │ │ cache hit? │ │ (4 etapas) │ │ + cache.json│
└──────────────┘ └─────────────┘ └──────────────┘ └──────────────┘
│ │ │
│ │ ├─ Pass 0: file nodes
│ │ ├─ Pass 1: parse code/test (paralelo, n=cpus)
│ │ ├─ Pass 2: defines + imports + tests
│ │ ├─ Pass 3: extends / implements
│ │ ├─ Pass 3.5 (--with-calls): calls
│ │ └─ Pass 4: folder + contains
│ │
│ └─ Si `file.hash` coincide con cache, reusa el fragment parseado
│ y solo recomputa las aristas cross-file (imports/extends/calls).
│
└─ Hash global del scan: si no cambió, `forge graph` ni siquiera arranca (skip).
Output canónico — .contextforge/graph.json cumple docs/schemas/graph.schema.json:
{
"schemaVersion": "0.2.0",
"project": { "name": "contextforge-cli", "root": "." },
"generatedAt": "2026-05-08T...Z",
"scanRef": { "path": ".contextforge/scan.json", "scanHash": "..." },
"parser": { "engine": "heuristic" },
"stats": {
"nodesByType": { "file": 287, "folder": 117, "symbol": 3104 },
"edgesByType": { "imports": 71, "defines": 2256, "contains": 376 }
},
"nodes": [
/* ordenados por id, JSON byte-estable */
],
"edges": [
/* ordenadas por (from,to,type) */
]
}
Tipos de nodo y arista que produce
| Nodo | Cuándo aparece |
|---|---|
file |
Uno por cada archivo del scan (code/test/doc/config/asset) |
symbol |
Por cada función/clase/interface/type/var/enum exportada o interna |
folder |
Sintético, derivado de los paths (anidación completa) |
package |
Por cada import externo único (react, node:path, @anthropic-ai/sdk, …) |
| Arista | De qué | Cuándo se emite |
|---|---|---|
defines |
file → symbol |
Siempre que el archivo contiene un símbolo |
imports |
file → file o file → package |
Relativos · alias workspace (pnpm) · alias tsconfig.paths · externos |
tests |
file:test → file:impl |
Convención *.test.ts ↔ *.ts |
extends |
symbol → symbol |
Clase/interface extiende un símbolo del mismo file o de un import |
implements |
symbol → symbol |
class A implements B resuelto |
contains |
folder → folder y folder → file |
Estructura de carpetas |
calls |
file → symbol |
Solo con --with-calls (heurística regex, ruido aceptado) |
references |
file → symbol |
Solo con --with-refs (PascalCase, fuera de imports/defines) |
Lenguajes parseables hoy (heurística regex, no tree-sitter WASM): TypeScript, TSX, JavaScript, JSX, Python, Go, Rust, Java. El resto se incluye como file pero sin símbolos.
Incrementalidad y reproducibilidad
pnpm forge graph # 1ª vez en este repo: 112 archivos parseados
# editas un archivo
pnpm forge scan # actualiza hashes
pnpm forge graph # → "cache: 111 reutilizados, 1 reparseados"
pnpm forge graph --force # ignora ambos caches; reconstruye todo
pnpm forge graph --with-calls # añade aristas calls (heurística, opt-in)
pnpm forge graph --with-refs # añade aristas references PascalCase (opt-in)
# resolución de imports
# 1. workspace pnpm (packages/<name>)
# 2. tsconfig.paths (compilerOptions.paths con baseUrl)
# 3. relativos (./foo, ../bar)
# 4. externos → nodo type=package (react, node:fs, @scope/lib)
# export a otros formatos
pnpm forge graph --export=dot > graph.dot
pnpm forge graph --export=graphml > graph.graphml
# Render con Graphviz: dot -Tsvg graph.dot -o graph.svg
# O abrir graph.graphml directamente en Gephi.
# enriquecimiento opcional con LLM (Anthropic)
ANTHROPIC_API_KEY=sk-... pnpm forge graph --enrich
# Agrega summary / tags / complexity a los símbolos exportados.
# El default sigue siendo determinista; --enrich es la única excepción opt-in.
- Cache global por hash del scan: si
scan.jsonno cambió,forge graphno arranca. - Cache por archivo (
.contextforge/graph.cache.json): solo se reparsean los archivos cuyo hash BLAKE3 cambió. - Output byte-idéntico entre dos corridas (excluyendo
generatedAt): los nodos y aristas se ordenan al final porid/(from,to,type), así quegit diffdel JSON es revisable y el prompt cache de Claude no se invalida innecesariamente.
Visualización: forge viz
pnpm forge graph
pnpm forge viz # genera .contextforge/graph.html
# abre el archivo en cualquier navegador, no necesita servidor ni Node
El HTML viewer es standalone (un solo archivo, Cytoscape vía CDN) y trae:
┌──────────────────────────────────────────────────────────────────────┐
│ ContextForge [Grafo] [Dominios] 287 nodos · 2703 edges · 50 pack│
├──────────────┬───────────────────────────────────────────────────────┤
│ Estadísticas │ │
│ 287 archivos │ ●─────● │
│ 3104 símbolos│ ╱ ╲ ● ← archivo en pack │
│ 2703 edges │ ●─────●───● ◇ ← símbolo exportado │
│ 50 en pack │ ╲ ╱ ◇ ← dashed = interno │
│ │ ●─● ▢ ← carpeta │
│ Tour ◀ ▶ ✕ │ │
│ 3 / 50 │ │
│ │ │
│ Leyenda nodos │ ┌──────────────┐ │
│ ● Código 128 │ │ src/ │ ← agrupación por dominio │
│ ● Test 24 │ │ ┌────┐ ┌──┐ │ (toggle "Agrupar") │
│ ● Doc 17 │ │ │a.ts│ │b │ │ │
│ ▢ Carpeta 117│ │ └────┘ └──┘ │ │
│ ◇ Interno 218│ └──────────────┘ │
│ ● En pack 50 │ │
│ │ │
│ Leyenda edges │ │
│ ─ imports │ │
│ ─ defines │ │
│ ─ tests │ │
│ ─ extends │ │
│ ─ contains │ │
│ │ │
│ Filtros │ │
│ [Solo pack] [+Sím] [+Carpetas] [Agrupar] │
│ [⊕ Centrar] [⟳ Layout] │
│ │ │
│ 🔍 Buscar... │ │
│ │ │
│ Nodo selec. │ │
│ src/main.ts │ │
│ [code][ts] │ │
│ Define 4 sím │ │
│ → importa: 3 │ │
│ ← usado por:5│ │
└───────────────┴────────────────────────────────────────────────────────┘
Capacidades del viewer:
- Dos vistas: grafo file/symbol/folder + vista de dominios (agregación por carpeta top-level, edges = imports cruzados entre dominios).
- Tour por context-pack:
▶recorre uno por uno los archivos seleccionados por PageRank para tu tarea, centrándose y mostrando vecinos. - Filtros incrementales: solo pack (default), añadir símbolos, añadir carpetas, agrupar por dominio.
- Estilos diferenciados para
exported:false(símbolos internos en dashed) y nodosfolder. - Búsqueda por nombre o ruta con resaltado.
- Panel de nodo: kind, lang, contadores de edges entrantes/salientes, lista navegable de vecinos.
Cómo lo usa el agente
El agente nunca lee el repo a ciegas. Lee artefactos derivados, en este orden:
1. .contextforge/agent-context.md ← qué hay disponible y en qué orden leerlo
2. .contextforge/context-pack.json ← solo los archivos relevantes a la tarea
3. .contextforge/agent-manifest.json ← qué skills/rules aplican (con razón)
4. .contextforge/graph.json ← solo si necesita ver vecinos / cross-deps
El grafo no se inyecta entero al prompt. El agente lo consulta puntualmente:
| Pregunta del agente | Cómo responde el grafo |
|---|---|
"¿Qué archivos tocan a Foo?" |
Edges defines/imports entrantes del símbolo |
| "¿Quién testea esto?" | Edges tests entrantes del file |
"¿De qué hereda Bar?" |
Edge extends saliente del símbolo |
| "¿En qué carpeta vive este archivo?" | Edge contains entrante del file |
| "Dame el subgrafo de mi tarea" | Lo entrega forge context ya filtrado (PageRank + BFS + budget) |
| "Que nada se salga del scope" | implement-plan.json::allowedFiles[] derivado del pack |
Vía MCP — los agentes que hablen MCP pueden llamar tools sin tocar disco:
{ "tool": "forge_neighbors", "arguments": { "path": "src/foo.ts" } }
// → { incoming: [...], outgoing: [...], symbols: [...] }
{ "tool": "forge_context", "arguments": { "task": "fix race in writer" } }
// → { files: [...], packTokens: 11988, tokenLedger: {...} }
Vs. Understand-Anything (qué tomamos, qué no)
| Aspecto | Understand-Anything | ContextForge |
|---|---|---|
| Pipeline | Tree-sitter + LLM agents (híbrido) | Regex/tree-sitter, 0 LLM en el pipeline |
| Lenguajes soportados | 10+ con WASM | 8 con heurística (TS/JS/Py/Go/Rust/Java/TSX/JSX) |
| Schema de nodo | 21 tipos (con dominio + knowledge) | 4 tipos canónicos (file/symbol/folder/package) |
| Schema de arista | 35 tipos | 8 tipos (los que un agente usa de verdad) |
| Enriquecimiento (summary) | LLM agents enriquecen cada nodo | Determinista: contadores derivados del grafo |
| Coste de regenerar | API key + tiempo + ruido | CPU-only · cache por archivo · idempotente |
| Visualización | Dashboard rico, tours guiados | HTML standalone con tour + dominios + filtros |
| Persistencia | knowledge-graph.json + embeddings | graph.json + graph.cache.json (BLAKE3) |
| Roadmap LLM enrich | Es la base | Opt-in futuro (--enrich), nunca el default |
La filosofía: el grafo de ContextForge no intenta entender el código, intenta darte un índice rápido y barato. La inteligencia semántica vive en el agente, no en el pipeline. Por eso es reproducible y por eso el output sirve de input al prompt cache de Claude (descuento del 90 % en iteraciones 2+ del SDD).
Cómo el grafo le da superpoderes a OpenSpec
El bloque clave de v0.3.0+. Sin grafo, OpenSpec hace su trabajo bien pero el agente trabaja a ciegas. Con grafo, el spec se vuelve trazable, validable y barato.
1. El spec-input.json deriva del grafo, no del repo.
| Campo | Lo aporta el grafo |
|---|---|
affectedFiles |
El context-pack ya filtró por PageRank → spec apunta a archivos reales |
crossDomainDeps |
El grafo de imports → design.md menciona dependencias correctas |
domain |
Inferencia por mayoría de paths del pack |
evidence |
Referencias trazables a los artefactos JSON validados |
purpose |
Inferencia determinista del nombre del archivo |
2. El agente recibe el spec-prompt, NO el repo.
forge spec genera .contextforge/spec-prompt.md con 4 secciones:
1. Contexto del repo (de ContextForge)
- Tarea, dominio inferido, archivos afectados, cross-domain deps, evidencia
2. Instrucciones canónicas de OpenSpec
- Lo que devuelve `openspec instructions proposal --change <id> --json`
3. Restricciones para tu salida
- Token budget, allowed files, formato moderno
4. Output esperado
- Editar openspec/changes/<id>/{proposal,design,tasks,specs}.md
El agente no abre el repo. Solo lee este prompt + el context-pack si necesita más detalle.
3. El multiplicador escondido — prompt caching.
Claude descuenta 90 % del precio de tokens cacheados. ContextForge mete un boost porque su output es determinista:
- Mismo
forge context→ mismocontext-pack.jsonbyte-a-byte. - Mismo
forge spec→ mismospec-prompt.mdbyte-a-byte.
Entre las iteraciones 2, 3, N del SDD, el agente cachea todo el prompt. Solo paga full por el delta.
4. Ahorro doble medido.
Por iteración SDD:
Sin ContextForge 217 600 tokens $0.65
Con ContextForge 18 988 tokens $0.057 (-91%)
3 iteraciones de un feature normal:
Sin caching 3 × $0.65 = $1.96
Con caching activo $0.07 (÷28×)
5. Trazabilidad auditable.
Cada path en design.md apunta a un archivo del context-pack.json. Cada commit puede verificarse con forge implement --check contra allowedFiles[] derivado del pack.
6. Subgrafo congelado dentro del change (v0.3.7+).
forge spec <id> ahora también escribe openspec/changes/<id>/graph.subset.json — un subgrafo limitado al context-pack del change + 1 hop por aristas. Eso significa:
- Las skills/prompts del agente que leen
openspec/changes/<id>/tienen el grafo del momento exacto en que se autorizó el spec, sin necesidad de reabrir.contextforge/graph.json(que pudo haber mutado). design.mdincluye una sección "Context graph (subset)" con stats y la referencia al adjunto.- Vía MCP:
forge_change_subgraph({ change_id: "mi-feature" })lo devuelve directo, ideal para que un agente lo cargue al inicio de una sesión de implementación.
openspec/changes/mi-feature/
├── proposal.md
├── design.md ← incluye tabla con stats del subset
├── tasks.md
├── specs/<domain>/spec.md
├── graph.subset.json ← subgrafo self-contained · validado por JSON Schema
└── graph.subset.html ← viewer interactivo standalone (Cytoscape, sin servidor)
Trazabilidad real: el subgrafo es byte-stable (orden determinista), validado por JSON Schema (docs/schemas/graph-subset.schema.json), y queda commiteado con el resto del change. Si seis meses después el grafo global cambió, puedes comparar este subset con el subgrafo equivalente de hoy y ver el drift.
Tabla — qué aporta cada herramienta:
| Aporte | ContextForge | OpenSpec | Agente IA |
|---|---|---|---|
| Ranking de archivos por relevancia | ✓ | ||
| Grafo de dependencias | ✓ | ||
| Token budget + selección por presupuesto | ✓ | ||
| Trazabilidad (cada archivo viene del pack) | ✓ | ||
| Schemas estables (Requirement+Scenario) | ✓ | ||
Validación estructural (openspec validate) |
✓ | ||
| Templates canónicos por artefacto | ✓ | ||
Instalación de instrucciones por agente (openspec init) |
✓ | ||
| Redacción de prosa | ✓ | ||
| Decisiones de diseño contextual | ✓ | ||
| Determinismo + cacheabilidad | ✓ | ✓ |
Documentación generada para agentes (forge init)
forge init genera .contextforge/agent-context.md — un archivo derivado que cualquier agente puede leer al iniciar sesión y entender:
- Qué artefactos hay disponibles y en qué orden leerlos.
- Cómo consumirlos (no leer el repo a ciegas).
- La receta SDD completa (ContextForge ⊕ OpenSpec).
- Política para agentes (no specs a mano, no bullets, gate pre-commit).
- Tabla de comandos clave por intención.
Adicionalmente, forge init detecta si openspec CLI está en PATH y, si el directorio openspec/ no existe, ejecuta:
openspec init . --tools=claude,cursor,opencode --force
Eso instala las instrucciones canónicas de OpenSpec para los 3 agentes (las que aparecen en .claude/, .cursor/rules/, opencode.json). Idempotente: si el directorio openspec/ ya existe, salta.
Comandos completos
| Comando | Qué hace | LLM |
|---|---|---|
forge init |
.contextforge/ + agent-context.md + openspec init (si está) |
No |
forge scan |
Indexa con BLAKE3 (incremental) | No |
forge graph [--force] [--with-calls] [--with-refs] [--enrich] [--export=<dot|graphml>] |
Grafo file/symbol/folder/package con cache por archivo (BLAKE3) | No (¹) |
forge context "<tarea>" [--no-manifest] [--force] |
PageRank + BFS + budget · auto-emite manifest | No |
forge spec <id> [--no-openspec] |
spec-input.json + handoff/fallback OpenSpec | No |
forge implement <id> |
Plan con guardrails derivados del pack | No |
forge implement --check |
Valida diff vs guardrails | No |
forge skills [--force] |
Auto-genera skills por dominio (contextforge-domain-<slug>.md) |
No |
forge manifest [--agents=...] [--force] |
Re-genera manifest sin re-rankear | No |
forge sync [--since X] [--rebuild] |
Reporta delta desde un ref de git | No |
forge impact |
Health check de artefactos + cobertura | No |
forge viz |
Visualización HTML interactiva del grafo | No |
forge docs [--force] |
Scaffold Diátaxis (tutorials/how-to/reference/explanation/adr/architecture) | No |
Política: ningún comando llama a un LLM por defecto. Todo es derivable, reproducible, auditable.
(¹) forge graph --enrich es la única excepción opt-in — agrega summary/tags/complexity a símbolos exportados vía Anthropic API. Requiere ANTHROPIC_API_KEY. Sin la flag, el pipeline sigue 100 % determinista.
Servidor MCP
packages/mcp expone 10 tools consumibles por cualquier cliente MCP (Claude Code, OpenCode, etc.).
| Tool | Propósito |
|---|---|
forge_status |
Estado de los artefactos (frescura, conteos, savings) |
forge_domain_map |
Mapa de dominios + dependencias cruzadas |
forge_neighbors |
Vecinos directos de un archivo en el grafo |
forge_context |
Selección PageRank para una tarea (con o sin contenido) |
forge_check |
Valida git diff contra guardrails del implement-plan |
forge_change_subgraph |
Subgrafo congelado de un OpenSpec change (openspec/changes/<id>/graph.subset.json) |
forge_change_context |
Mapa de lectura del change (openspec/changes/<id>/context.md) |
forge_archive_change |
Cierra un change: openspec archive + rebuild graph padre + refresh subgrafos |
select_agent_context |
Runtime: computa agent-manifest en memoria para una tarea (cache mtime) |
get_agent_manifest |
Offline: lee .contextforge/agent-manifest.json precomputado |
Wiring (Docker, recomendado):
{
"mcpServers": {
"contextforge": {
"command": "docker",
"args": [
"run",
"--rm",
"-i",
"-v",
"${PWD}:/project",
"-e",
"PROJECT_ROOT=/project",
"ghcr.io/alejandro-cedeno-10/contextforge-mcp:latest"
]
}
}
}
Wiring (npm):
{
"mcpServers": {
"contextforge": {
"command": "node",
"args": ["./node_modules/@anai-raia-alex/contextforge-mcp/dist/index.js"],
"env": { "PROJECT_ROOT": "." }
}
}
}
Artefactos generados
Todos los JSON validados con JSON Schema 2020-12 antes de escribirse.
.contextforge/
scan.json # archivos con hashes BLAKE3
graph.json # 368 nodos, 267 edges
context-pack.json # archivos seleccionados dentro del presupuesto
token-ledger.json # métricas auditables del ahorro
agent-manifest.json # skills/rules relevantes a la tarea (neutral)
spec-input.json # entrada determinista para OpenSpec / agente
spec-prompt.md # prompt copy-paste (modo handoff)
implement-plan.json # guardrails: allowedFiles, maxLocDelta, etc.
agent-context.md # documentación del repo para agentes IA
graph.html # visualización interactiva (Cytoscape.js)
manifests/opencode-readme.md # instrucciones MCP para OpenCode
.claude/agent-manifest.md # manifest renderer Claude Code
.cursor/rules/contextforge-active.mdc # rule auto-attached por dominios tocados
openspec/changes/<id>/
proposal.md # intent, scope, why, alternatives
design.md # decisiones técnicas con archivos del grafo
tasks.md # T1, T1.1, T2... checklist
specs/<dominio>/spec.md # ## ADDED Requirements + ### Requirement: + #### Scenario:
Schemas
| Artefacto | Schema |
|---|---|
scan.json |
docs/schemas/scan.schema.json |
graph.json |
docs/schemas/graph.schema.json |
context-pack.json |
docs/schemas/context-pack.schema.json |
implement-plan.json |
docs/schemas/implement-plan.schema.json |
token-ledger.json |
docs/schemas/token-ledger.schema.json |
agent-manifest.json |
docs/schemas/agent-manifest.schema.json |
spec-input.json |
docs/schemas/spec-input.schema.json |
Métricas reales (este repo, hoy)
| Métrica | Valor |
|---|---|
| Tests | 270 / 270 verde (26 archivos) |
| Coverage | ≥ 80 % global · módulos manifest/ y spec/ ≥ 95 % |
| Token savings | 94.4 % vs baseline |
| Compresión | 17.9× por sesión · ÷28× SDD con caching |
| Archivos del repo | 128 |
| Archivos en context-pack | 50 |
| Tokens del context-pack | 11 988 |
| Tokens baseline | 214 606 |
| Grafo | 368 nodos, 267 edges |
Comandos forge |
13 |
| Tools MCP | 7 |
Distribución
| Canal | Identificador |
|---|---|
| npmjs.com (público, sin token) | @anai-raia-alex/contextforge-{core,cli,mcp} |
| GitHub Container Registry | ghcr.io/alejandro-cedeno-10/contextforge-mcp:latest (multi-arch amd64+arm64) |
| Repo público | https://github.com/alejandro-cedeno-10/contextforge-cli |
Integración con agentes — matriz
| Agente | Configuración | Selección por tarea | Estado |
|---|---|---|---|
| Claude Code | .claude/skills/ (3 task-oriented + N auto-generadas) · hook UserPromptSubmit |
Hook inyecta manifest live + JSON precomputado | Listo |
| Cursor | .cursor/rules/contextforge.mdc (alwaysApply) + contextforge-active.mdc (Auto-Attached) |
Rule por glob regenerada por forge context (Cursor no tiene hooks reactivos) |
Listo |
| OpenCode | opencode.json con MCP server registrado |
Tool MCP select_agent_context({task}) live |
Listo |
| Codex / otros | Leen .contextforge/*.json directamente |
Lectura directa de agent-manifest.json |
Compatible |
Los .contextforge/*.json son portables. Cualquier agente futuro los puede consumir sin modificación.
Principios
- Determinismo primero: ningún comando llama a un LLM. Reproducible, auditable, cacheable.
- OpenSpec por defecto:
forge specdelega a OpenSpec CLI cuando está, fallback determinista cuando no. - Cache por hash: si
scan.jsonno cambió,forge graphsalta el rebuild. - Presupuesto explícito: cada context-pack tiene
maxInputTokenstrazable entoken-ledger.json. - JSON validado: ningún artefacto se escribe sin pasar JSON Schema.
- Portable: cualquier agente puede consumir
.contextforge/*.jsonsin modificación. - Sin lock-in: si mañana sale otra herramienta SDD, lee los mismos JSON.
Para la exposición — los 5 puntos memorables
- 94 % menos tokens por sesión — verificado con
token-ledger.json, no estimado. - ÷28× costo SDD con prompt caching — porque el output es determinista byte-a-byte.
- No competimos con OpenSpec, lo apoyamos. Tres roles separados (CF / OpenSpec / agente). Si OpenSpec evoluciona, no nos pisamos.
- El agente solo carga lo de tu tarea actual — context-pack + manifest. Las 100 skills viejas no entran al contexto.
- Sin lock-in. Los
.contextforge/*.jsonson neutrales. Si mañana sale otra herramienta, los puede consumir igual.
Estructura del monorepo
packages/
core/ → scanner, parser tree-sitter, graph builder, selector PageRank,
packer, schema validator, skill builder, agent-manifest, spec-input,
spec promptRenderer
cli/ → comandos forge (init, scan, graph, context, spec, implement, manifest,
skills, sync, impact, viz, docs)
mcp/ → servidor MCP con 7 tools
.claude/skills/ → 3 skills task-oriented (contextforge-*) +
N auto-generadas (contextforge-domain-*) +
agent-manifest.md (per-task)
.cursor/rules/ → contextforge.mdc (alwaysApply) +
contextforge-active.mdc (Auto-Attached, regenerada por tarea)
opencode.json → configuración MCP para OpenCode
docs/integrations/ → guías de wiring por agente
docs/explanation/ → contextforge-and-openspec.md (lectura clave)
Tests
pnpm test # todos los tests
pnpm test:coverage # con cobertura
pnpm typecheck # tsc -b --force (project references)
Suite actual: 270/270 tests pasando (26 archivos), coverage global ≥ 80 % · módulos manifest/ y spec/ ≥ 95 %.
Documentación
| Documento | Descripción |
|---|---|
docs/explanation/contextforge-and-openspec.md |
Lectura clave: 3 roles (CF / OpenSpec / agente), ahorro medido, prompt caching ÷28× |
docs/how-to/use-contextforge.md |
Guía paso a paso: instalación, pipeline, integración por agente |
docs/integrations/claude-code-hook.md |
Snippet copy-paste para activar manifest en runtime en Claude Code |
docs/integrations/cursor-rules.md |
Los 3 modos de rules en Cursor + estrategia recomendada |
docs/integrations/opencode-mcp.md |
Ejemplo de select_agent_context desde OpenCode |
docs/token-savings-architecture.md |
Análisis de ahorro de tokens por capas con tabla de costos |
docs/EXAMPLES/end-to-end-flow.md |
Walkthrough completo del pipeline con outputs reales |
docs/IMPLEMENTATION_TASKS.md |
Backlog de sprints con criterios verificables |
docs/CHANGELOG-schemas.md |
Historial de cambios de schemas |
openspec/changes/forge-spec-as-prepare-step/ |
OpenSpec change del re-arquitectura v0.3.0 |
openspec/changes/agent-manifest/ |
OpenSpec change del manifest por tarea |
openspec/changes/auto-domain-skills/ |
OpenSpec change de las skills auto-generadas |
CONTEXTFORGE_SOURCE_OF_TRUTH.md |
Decisiones de diseño, arquitectura, roadmap |
Licencia
MIT
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.