Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
14 KiB
id, title, status, type, domain, scope, priority, depends, blocks, related, created, updated, tags
| id | title | status | type | domain | scope | priority | depends | blocks | related | created | updated | tags | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0064 | registry_mcp: servidor MCP que expone registry.db a Claude | completado | feature |
|
multi-app | alta | 2026-05-17 | 2026-05-17 |
0064 — registry_mcp: servidor MCP que expone registry.db a Claude
APP Metadata
| Campo | Valor |
|---|---|
| ID | 0064 |
| Estado | pendiente |
| Prioridad | alta |
| Tipo | feature — apps/registry_mcp/ |
Dependencias
- Ninguna directa. Aplica TBD obligatorio (
.claude/rules/apps_tbd.md): trabajar enissue/0064-registry-mcp-server, merge--no-ffa master. - Reutiliza
registry/package (ya carga registry.db con FTS5) ycmd/fn/run.go(dispatcher por lenguaje).
Objetivo
Convertir registry.db en herramienta de primera clase para Claude (Code, Desktop, otros clientes MCP) via servidor MCP. Tras este issue, cualquier sesion Claude con MCP registrado puede llamar fn_search, fn_show, fn_code, fn_run, etc., sin abrir bash ni sqlite3.
Contexto
Estado actual:
- registry.db tiene 1090 funciones, 166 tipos, FTS5 sobre
code/description/params_schema/notes/documentation. 23 MB. Local en raiz del repo + replica enregistry_apidel VPS. - Cada agente reinventa:
sqlite3 registry.db "SELECT...", parseo manual, ejecucion viafn runshellando. Caro en tokens, error-prone, no compone. fnCLI ya implementa todo (search/show/run/doctor) — falta exponerlo como protocolo MCP estable.
MCP (Model Context Protocol):
- Anthropic spec, transport stdio (default Claude Code/Desktop) o HTTP+SSE.
- SDKs: Python (
mcppaquete oficial), TypeScript, Go (mark3labs/mcp-go). - Tools = funciones JSON-schema-tipadas que el LLM ve y puede invocar.
Decision por defecto (confirmar en Fase 1):
- Lenguaje: Go. Reutiliza
registry/(mismo paquete quecmd/fn), un solo binario, despliegue trivial. SDK:github.com/mark3labs/mcp-go. - Transport: stdio primero (caso 90% Claude Code/Desktop). HTTP+SSE como flag opcional (
--http :7733) para clientes remotos. - Read-only en v1.
fn_runy mutacion de proposals quedan para fase 2 (issue separado), tras decidir politica de confirmacion.
Decisiones de diseno (a confirmar antes de implementar)
| Decision | Default recomendado | Alternativas |
|---|---|---|
| Lenguaje | Go (mark3labs/mcp-go) |
Python (mcp oficial, mas comun) |
| Transport v1 | stdio | HTTP+SSE, ambos |
| Tools v1 | search, show, code, list_domains, uses, doctor (read-only) | Incluir fn_run desde el dia 1 con confirmation prompt |
| Auth HTTP | REGISTRY_API_TOKEN (basicAuth) |
Sin auth (solo loopback) |
| Origen datos | registry.db local (FN_REGISTRY_ROOT) | Fallback a registry_api HTTP si no hay clone |
| Logging | stdout estructurado (slog) en stderr para no romper stdio | operations.db con executions de cada tool call |
Salida fn_show |
Markdown render-ready (frontmatter + code fenced) | JSON crudo |
| Limites | search: 50 resultados default, paginable | Sin limite |
Confirmar con usuario antes de Fase 2. Si elige defaults, proceder.
Arquitectura
Archivos afectados
App nueva (apps/registry_mcp/):
main.go(NEW) — flag parsing (--stdiodefault,--http :PORT), arranque server, wiring tools.server.go(NEW) — registro de tools enmcp-go, dispatcher.tools/search.go(NEW) —fn_search, query afunctions_fts+types_fts.tools/show.go(NEW) —fn_show, lee fila completa + formatea markdown.tools/code.go(NEW) —fn_code, devuelvecodecolumna como string.tools/list_domains.go(NEW) —fn_list_domains, agregados por(domain, kind, purity, lang).tools/uses.go(NEW) —fn_uses, parseauses_functions/uses_typesJSON + busca consumidores reverso.tools/doctor.go(NEW) —fn_doctor, llama afn doctor --jsoncomo subprocess (mas barato que reimplementar).tools/run.go(NEW pero detras de flag--enable-runpor seguridad) — wrap defn runvia subprocess.db.go(NEW) — abre registry.db en read-only WAL, ping, helpers de query.format.go(NEW) — formateo markdown comun (frontmatter + code blocks).app.md(NEW) — frontmatter de la app,framework: "mcp",tags: [service, mcp].README.md(NEW) — instalacion, ejemplos de queries.CMakeLists.txt— N/A (es Go).Makefile(NEW, opcional) —make build,make install-claude-code,make install-claude-desktop.migrations/001_init.sql(NEW, opcional) — operations.db propio para logs de queries (fase 2).
fn_registry root:
cmd/fn/— sin cambios. La app es independiente y consumeregistry/package directamente..gitignoreya excluyeapps/*/exceptoapp.md. La app vive en su sub-repo Gitea (reglaapps_tbd.md+apps_own_repo).
Pure / impure split
- Pure (testeable sin BD):
format.RenderFunctionMarkdown(fn registry.Function) stringformat.RenderTypeMarkdown(t registry.Type) stringtools.parseSearchFilters(args map[string]any) SearchFilters
- Impure (bordes):
db.Open(path),db.Search,db.Show— SQLite I/O.tools/run.go,tools/doctor.go— subprocess.- server stdio/HTTP — I/O.
Funciones del registry a reutilizar (registry-first)
Auditar antes de escribir:
sqlite3 registry.db "SELECT id FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'name:mcp OR description:mcp OR description:\"model context protocol\"');"
sqlite3 registry.db "SELECT id FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'name:sqlite OR name:fts5');"
Reutilizables casi seguro:
sqlite_open_go_infra(WAL + foreign keys + ping).error_go_corepara tipos de error.http_json_response_go_infra,http_parse_body_go_infrasi transport HTTP.random_hex_id_go_corepara correlation IDs en logs.
Si falta:
mcp_tool_register_go_infra(registra tool con schema en mcp-go) — si surge patron repetido en N tools, delegar afn-constructor.markdown_function_card_go_infra(formatea funcion del registry como markdown card) — candidato si encaja con regla "dos artefactos lo usaran".
Schema MCP de cada tool (resumen)
// fn_search
{
"query": "string (FTS5 expression o texto libre)",
"kind": "function|pipeline|component|type? (optional)",
"lang": "go|py|bash|ts|cpp? (optional)",
"domain": "string? (optional)",
"purity": "pure|impure? (optional)",
"limit": "int? (default 50)"
}
// returns: { "results": [{ "id","name","kind","lang","domain","purity","signature","description" }] }
// fn_show
{ "id": "string" }
// returns: { "id","markdown" }
// fn_code
{ "id": "string" }
// returns: { "id","lang","code" }
// fn_list_domains
{}
// returns: { "domains": [{ "domain","functions","types","pure","impure","by_lang":{...} }] }
// fn_uses
{ "id": "string" }
// returns: { "uses_functions":[], "uses_types":[], "consumed_by":[] }
// fn_doctor
{ "subcommand": "artefacts|services|sync|uses-functions|unused? (optional, default all)" }
// returns: { "report": <objeto del fn doctor --json> }
Tareas
Fase 1 — confirmar diseno
1.1 Confirmar las 8 decisiones de la tabla con el usuario. Si elige defaults, seguir.
1.2 Auditar registry: ejecutar las dos queries FTS5 de "Funciones del registry a reutilizar" y documentar reutilizables aqui.
1.3 Decidir si v1 incluye fn_run (con confirmation) o solo read-only.
Fase 2 — bootstrap app
2.1 Crear apps/registry_mcp/ con app.md (frontmatter completo: framework: "mcp", tags: [service, mcp], dir_path, repo_url placeholder), README.md esqueleto.
2.2 Inicializar sub-repo Gitea (dataforge/registry_mcp) via gitea_create_repo_bash_infra + ensure_repo_synced_bash_infra. Branch master.
2.3 go mod init y añadir github.com/mark3labs/mcp-go.
2.4 Wire minimo: main.go con stdio server vacio + 1 tool ping que devuelve pong. Probar con claude MCP.
Fase 3 — tools read-only
3.1 db.go: abrir registry.db read-only, helpers de query. Reusar sqlite_open_go_infra si compatible.
3.2 tools/list_domains.go (mas simple, valida wiring con datos reales).
3.3 tools/search.go: FTS5 con escapado seguro de tokens (gotcha de la regla de quoting). Filtros opcionales.
3.4 tools/show.go + format.go: markdown card.
3.5 tools/code.go: solo code column.
3.6 tools/uses.go: parse JSON uses_functions/uses_types + reverse lookup (SELECT id FROM functions WHERE uses_functions LIKE '%"<id>"%').
3.7 tools/doctor.go: subprocess fn doctor <sub> --json. Devolver el JSON parseado tal cual.
Fase 4 — transport HTTP opcional
4.1 Flag --http :7733 en main.go.
4.2 Auth basicAuth via REGISTRY_API_TOKEN (header Authorization: Bearer ... o user:pass).
4.3 Bind por defecto a 127.0.0.1 salvo flag --bind 0.0.0.0 explicito.
Fase 5 — tests
5.1 db_test.go: abrir registry.db de fixture (mini, ~5 funciones), verificar search/show.
5.2 tools/search_test.go: filtros, escapado FTS5, paginacion.
5.3 tools/show_test.go: markdown bien formado (snapshot).
5.4 tools/uses_test.go: dependencias directas + reverse.
5.5 Integration test: arrancar server stdio en goroutine, mandar tools/list JSON-RPC, validar schema de cada tool.
5.6 fn doctor uses-functions no debe regresionar.
Fase 6 — instalacion + docs
6.1 Makefile: build (CGO_ENABLED=1 -tags fts5), install-claude-code (escribe .mcp.json en cwd), install-claude-desktop (linea en ~/.config/claude/mcp.json o equivalente macOS).
6.2 README.md: como registrar el server, ejemplos de queries Claude haria ("busca funciones puras de finance", "muestra el codigo de filter_slice_go_core", "que funciones usan sqlite_open_go_infra").
6.3 Probar end-to-end desde una sesion Claude Code real: fn_search("slice"), fn_show("filter_slice_go_core").
Fase 7 — cleanup + index
7.1 Si se reutilizo o creo funcion del registry → declararla en apps/registry_mcp/app.md (uses_functions).
7.2 Una linea en CHANGELOG.md.
7.3 fn index y verificar fn show registry_mcp_app.
7.4 Crear sub-issue 0064b si se decide implementar fn_run y mutacion de proposals (fuera de scope de este issue).
Ejemplo de uso
Sesion Claude Code, MCP registry_mcp registrado:
Usuario: "necesito una funcion Go pura que filtre slices"
Claude (internamente):
→ fn_search({ query: "filter slice", lang: "go", purity: "pure" })
← { results: [
{ id: "filter_slice_go_core", signature: "func FilterSlice[T any](xs []T, pred func(T) bool) []T", description: "..." },
...
] }
→ fn_show({ id: "filter_slice_go_core" })
← { markdown: "# filter_slice_go_core\n\n```go\nfunc FilterSlice...\n```\n..." }
Claude (al usuario): "existe `filter_slice_go_core`. Aqui el codigo: ..."
Usuario: "diagnostico del sistema"
Claude:
→ fn_doctor({})
← { report: { artefacts: [...], services: [...], sync_drift: [], unused: [...] } }
Claude: "5 services activos, 2 con puerto sin escuchar (...), 12 funciones huerfanas en `core`."
Decisiones de diseno (rationale)
- Go vs Python: Go reusa el codigo existente sin duplicar (mismo
registry/package, mismos tipos). Python tendria SDK MCP mas maduro pero obligaria a reimplementar parsers. Coste-beneficio favorece Go. - stdio default: Claude Code lo usa nativamente. Cero config de red/firewall/puertos. HTTP es para casos remoto.
- Read-only v1:
fn_runejecuta codigo arbitrario en host del usuario. Confirmation flow (prompt antes de exec) merece su propio issue, no urge en v1. - Subprocess para
fn doctor: reimplementar todos los checks duplicaria logica. Subprocess es ~50ms y ya devuelve JSON. KISS. - markdown en
fn_show: el LLM consume markdown nativamente. JSON crudo le obligaria a re-renderizar. Una sola conversion en el server.
Riesgos
- CGO + sqlite-fts5 en build: el binario debe compilar con
-tags fts5. Documentar en Makefile + README. Si el VPS hostea version HTTP, debe tenergcc. - Tamaño de respuesta:
fn_showpuede devolver funciones de varios KB de codigo. Limitar a ~50 KB y truncar con aviso. - FTS5 quoting: la regla
CLAUDE.mddocumenta gotchas (description:single-pagerompe). El server debe sanitizar input del LLM antes de pasar a FTS5. Test explicito de tokens con guiones, puntos, comillas. - Race con
fn index: si registry.db se regenera mientras el server tiene WAL abierto, podria ver schema viejo. Mitigacion: el server reabre conexion si detectaSQLITE_CORRUPTo si mtime de la BD cambia. - Auth HTTP: si se expone a 0.0.0.0 sin token, cualquier red local lee el registry. Default
127.0.0.1+ token obligatorio en0.0.0.0. - Drift entre PCs: registry.db local puede estar desactualizado vs
registry_api. Documentar que el server consume el local y sugerirfn syncal usuario antes. - Logs ensucian stdio: Go SDK usa stdout para JSON-RPC. Cualquier
fmt.Printlnrompe el protocolo. Forzarsloga stderr. fn_runen fase 2: ejecutar codigo arbitrario sin confirmation expone al usuario. Diseñar prompt-before-exec antes de habilitarlo.
Prerequisitos
- TBD: rama
issue/0064-registry-mcp-serverdesdemasteractualizado. - Confirmar las 8 decisiones de Fase 1 con el usuario.
- Sub-repo Gitea
dataforge/registry_mcpcreado (Fase 2.2). mark3labs/mcp-govalidado (revisar version/maturity en Fase 1).
Criterios de aceptacion
claudedesde cualquier directorio con MCPregistry_mcpregistrado puede llamarfn_search("slice")y obtener resultados sin shellarsqlite3.fn_show(id)devuelve markdown renderizable que Claude muestra inline.- Latencia <100ms para search en BD local (FTS5 ya es rapido; verificar).
- Tests pasan: unit (parsers, format) + integration (stdio JSON-RPC end-to-end).
app.mdconuses_functionsactualizado.- README documenta instalacion para Claude Code y Claude Desktop.
fn doctor uses-functionsno regresiona.