Servidor MCP (Model Context Protocol) que expone una base de conocimiento corporativa unificada — ADRs, perfiles de proyecto, patrones de adopción, skills y un grafo de WikiLinks — para cualquier cliente LLM (Claude, Gemini, Cursor, etc.) operando dentro de Grupo Deacero, con thread-safety y autorización por scope.
- ¿Qué es Keystone?
- Características
- Inicio rápido
- Configuración
- Herramientas MCP expuestas
- Estructura del repositorio
- Desarrollo
- Calidad y gates
- Documentación adicional
Grupo Deacero opera más de 200 empresas con documentación fragmentada entre wikis, repos, drives y conocimiento tácito. Keystone centraliza ese conocimiento en una Knowledge Database (KDB) indexada por búsqueda híbrida (BM25 + embeddings) y la expone como un servidor MCP estándar. Cualquier agente o IDE compatible con MCP puede consultarla, contribuir nuevas páginas y resolver decisiones de arquitectura sin recrawlear fuentes dispersas.
El proyecto fue inicializado con Cornerstone (template cookiecutter-mcp v0.13.0), comparte gobernanza con el ADR organizacional ORG-ADR-0001, y está optimizado para correr como contenedor Docker o lanzarse directo por stdio desde un cliente MCP.
- Búsqueda híbrida (RRF) — combina BM25 (
rank_bm25) y similitud coseno sobre embeddingstext-embedding-004de Gemini con Reciprocal Rank Fusion (k=60). - Grafo de conocimiento in-memory — extracción de
[[WikiLinks]]y BFS confidence-weighted (src/keystone/domain/graph.py, ADR-0004). - Thread-safe —
SearchEngineprotegido porthreading.RLockcon patrón snapshot-then-release (ADR-0006). Las operaciones de scoring son lock-free. - Autorización por scope —
team,projectyorgvalidadas server-side viactx.request_context.meta["user" / "project"](ADR-0006). Identidad no forjable por el caller. - Almacenamiento Obsidian-compatible — páginas en Markdown con frontmatter YAML, validadas contra el modelo Pydantic
WikiPage(extra='forbid'). - Arquitectura hexagonal enforced con
import-linter(capasdomain → ports → adapters, independenciainbound/outbound). - 6 herramientas MCP: búsqueda híbrida, contexto de proyecto, ADRs organizacionales, patrones por fingerprint, navegación de grafo y ingesta.
- Gobernanza multi-agente — hooks de sesión para Claude Code y Gemini CLI, swarm file-based vía tmux, y skills en
.agents/sincronizadas desdedeagentic/cornerstone-agents. - Quality gates: cobertura 100 % (
pytest-cov),ruff,mypy,import-linter, gates personalizados (ADR, GitOps, SemVer, red-team de MCP). En CI (GitHub Actions): gate red-team (tools/red_team_mcp.py). Los demás gates (pytest,ruff,mypy,import-linter) corren localmente / pre-commit — pendiente integrarlos a CI.
- Python >= 3.11
- Docker y Docker Compose (opcional, pero recomendado)
- Una
GEMINI_API_KEYválida para embeddings (sin ella, la búsqueda semántica devuelve un vector constante y solo BM25 aporta señal).
git clone https://github.com/deagentic/keystone.git
cd keystone
# Entorno virtual
python -m venv .venv
source .venv/bin/activate
# Instalación editable
pip install -e ".[dev]"
# Hooks
pre-commit installConfigura tu .env en la raíz del repo:
GEMINI_API_KEY=tu_clave_aqui
KDB_DATA_DIR=./wiki
AGENTIC_TELEMETRY_URL=Nota de seguridad: nunca commitees
.envni claves..gitignoreya cubre.env, pero confírmalo antes del primer push.
docker compose up --buildEl servicio expone la imagen en python:3.11-slim, monta un volumen persistente store_data en /data/store, y arranca python -m keystone.server. Nota: el servidor actualmente corre por stdio; el puerto 8000 declarado en Dockerfile/compose.yml está reservado para un transporte HTTP futuro (ver Known issues).
Para Claude Desktop, agrega a claude_desktop_config.json:
{
"mcpServers": {
"keystone": {
"command": "python",
"args": ["-m", "keystone.server"],
"cwd": "/ruta/absoluta/al/repo",
"env": {
"KDB_DATA_DIR": "/ruta/absoluta/al/repo/wiki",
"GEMINI_API_KEY": "..."
}
}
}
}Variables de entorno reconocidas:
| Variable | Default | Propósito |
|---|---|---|
KDB_DATA_DIR |
./wiki |
Directorio raíz donde Keystone carga y escribe páginas *.md. |
GEMINI_API_KEY |
— | Habilita embeddings reales con text-embedding-004. |
AGENTIC_TELEMETRY_URL |
vacío | Endpoint opcional para telemetría del cliente Cornerstone. Vacío = no-op. |
AGENTIC_TELEMETRY_DEBUG |
0 |
Logs de debug del cliente de telemetría. |
CORNERSTONE_ADR_PATH |
docs/adr |
Path que el gate adr_gate.py valida en cada Edit/Write. |
Las seis tools viven en src/keystone/adapters/inbound/mcp.py y delegan a _*_logic puras (testables sin FastMCP). El helper _is_authorized(page, user, user_project) centraliza la autorización por scope (ADR-0006).
| Tool | Firma | Descripción |
|---|---|---|
kdb_search |
query: str, domain: Optional[str] = None, ctx: Optional[Context] = None |
Búsqueda híbrida BM25 + semántica con RRF. Devuelve top 5 con id, title, confidence, repo, excerpt. Filtra por scope. |
kdb_get_project_context |
project: str, ctx: Optional[Context] = None |
Agrega páginas por source_project y retorna stack, dominios y lista de id/title. Filtra por scope. |
kdb_get_org_adr |
adr_id: str |
Recupera un ADR organizacional (página con scope=="org" y "architecture" in domain). No recibe ctx — cualquier caller puede leerlo, pero el resultado ya está acotado a páginas org/architecture. |
kdb_list_patterns |
fingerprint: Dict, ctx: Optional[Context] = None |
Devuelve AdoptionPatterns cuya etiqueta tags=["pattern"] coincide con el stack del fingerprint. Filtra por scope. |
kdb_get_related |
page_id: str, depth: int = 1, tags: Optional[List[str]] = None, ctx: Optional[Context] = None |
BFS confidence-weighted sobre el grafo de WikiLinks. depth ∈ [1,3], cap de 10 resultados. Excluye deprecated, prioriza verified. Poda nodos no autorizados antes del traversal. |
kdb_ingest |
metadata: Dict, content: str |
Valida contra WikiPage (estricto), rechaza identificadores prohibidos, auto-genera id (kb-<uuid8>) y last_updated, escribe atómicamente y refresca el índice BM25 en memoria. |
ctxes inyectado por el transport MCP (no lo suministra el caller). Transportauseryprojectpara autorización scope-based — ver ADR-0006.
Reglas de autorización (ADR-0006): páginas
scope="team"solo se devuelven cuandouser == page.contributed_by; páginasscope="project"solo cuandouser_project == page.project;scope="org"siempre visible. (WikiPage.scopeaceptaorg | project | team.)
Los modelos Pydantic (
WikiPage,ADR,AdoptionPattern,ProjectProfile) están ensrc/keystone/domain/wiki.pyy rechazan campos no declarados.
keystone/
├── src/keystone/ # Paquete runtime (hexagonal)
│ ├── server.py # Composition root (stdio)
│ ├── adapters/
│ │ ├── inbound/mcp.py # FastMCP tools (@mcp.tool) + _is_authorized
│ │ ├── fs_adapter.py # I/O atómico + path-traversal guard
│ │ └── gemini_embed_adapter.py
│ ├── domain/
│ │ ├── search.py # SearchEngine (BM25 + RRF, RLock)
│ │ ├── graph.py # KnowledgeGraph + BFS confidence-weighted
│ │ └── wiki.py # Modelos Pydantic
│ └── ports/ # (placeholder, ver Known issues)
├── tests/
│ ├── features/*.feature # Escenarios BDD (Gherkin)
│ ├── step_defs/*.py # Glue pytest-bdd
│ ├── integration/, unit/
│ └── test_telemetry.py
├── docs/adr/ # ADRs locales + org/
├── wiki/ # Seed KDB + taxonomy.md
├── tools/ # Gates y discovery toolbox
├── .agents/ # Skills sincronizadas
├── .claude/, .gemini/ # Hooks por IDE
├── .swarm/ # Coordinación multi-agente (tmux)
├── .telemetry/ # Cliente OTel + envelope
├── .mcp.json # Configuración de MCP externo
├── pyproject.toml # Deps + tooling
├── Dockerfile, docker-compose.yml
└── README.md, AGENTS.md, CLAUDE.md, GEMINI.md
- Branching:
feat/<feature>,fix/<bug>(verCONTRIBUTING.md). - Commits: Conventional Commits (
feat:,fix:,docs:,test:). - Linting:
ruff check . && ruff format .(line length 100, target py311). - Tipos:
mypy src/(vía pre-commit). - Arquitectura:
lint-importsantes de PR (3 contratos: layers, independence, forbidden).
pytest # Unit + BDD vía pytest-bdd
pytest --cov-fail-under=100 # Gate de cobertura
behave # Si tienes step-defs en formato behave puro- Crea el ADR en
docs/adr/ADR-XXXX-titulo.mdsiguiendo MADR. - El gate
tools/adr_gate.pylo valida en cadaEdit/Write(hook PreToolUse, 60 s timeout). - Los ADRs organizacionales (
docs/adr/org/) son read-only — sincronizados desdedeagentic/cornerstone-kdb(ORG-ADR-0001).
| Gate | Herramienta | Cuándo corre |
|---|---|---|
| Lint | ruff check, ruff format |
pre-commit + CI |
| Tipos | mypy src/ |
pre-commit |
| Arquitectura hexagonal | import-linter |
pre-commit + manual |
| Cobertura ≥ 100 % (branch) | pytest-cov |
pytest |
| BDD | pytest-bdd |
pytest |
| ADR Gate | tools/adr_gate.py |
Hook PreToolUse de Claude Code |
| GitOps Gate | tools/gitops_gate.py |
Hook PreToolUse (Bash) |
| Red-team MCP | tools/red_team_mcp.py |
Manual / CI (obligatorio per AGENTS.md) |
| SemVer | tools/enforce_semver.py |
Pre-tag |
- Transporte:
Dockerfileexpone8000peroserver.pycorre porstdio. Decidir si se agrega un transporte HTTP/SSE o se quita la exposición de puerto. ports/vacío: el contratodomain → ports → adaptersestá declarado enimport-linterperoports/__init__.pyno define Protocols todavía.outbound/vacío:FSAdapteryGeminiEmbedAdapterviven enadapters/(un nivel arriba) en vez deadapters/outbound/.- Embeddings en ingest:
SearchEngine.add_or_update_pagereconstruye BM25 pero no recomputa el embedding de la página recién ingerida. Los resultados semánticos quedan rezagados hasta el próximo cold-start. - Grafo reconstruido por request:
_get_related_logicreconstruye elKnowledgeGraphen cada llamada —src/keystone/adapters/inbound/mcp.pytiene unTODO(perf)para cachear. - Transport auth no integration-tested:
_get_user_from_context/_get_project_from_contexttienen cobertura unitaria completa, pero falta un test end-to-end que levante un FastMCP real conctx.request_context.metacontrolado (ADR-0006). sonar-project.propertiestodavía contiene placeholders sin renderizar ({cookiecutter.project_slug},__PYTHON_VERSION__).docs/adr/index.mdlista ADR-0004 comoproposedaunque el ADR ya estáaccepted— desincronización a corregir.- Artefactos commiteados:
.coverage 2,stdout.log,stderr.log,logs/— candidatos a limpiar.
- docs/architecture.md — vista de arquitectura nivel arquitecto con diagramas Mermaid (C4, dominio, concurrencia, flujos MCP).
- docs/Keystone-Arquitectura-Ejecutiva.docx — resumen ejecutivo para stakeholders de Grupo Deacero.
- AGENTS.md — el "Supreme Mandate" del proyecto: capacidades del agente, gates obligatorios, taxonomía de skills.
- CLAUDE.md / GEMINI.md — instrucciones específicas por IDE-agent.
- docs/adr/ — ADRs locales y organizacionales.
- wiki/taxonomy.md — dominios del KDB (ot-ics, infra, security, backend, finance, hr, architecture; frameworks IEC 62443 / ISO 27001 / políticas internas).
Licencia y propiedad: Grupo Deacero. Repo público para fomentar colaboración con socios y proveedores. No publicar credenciales, secretos ni datos operativos sensibles.