Files
fn_registry/dev/issues/completed/0064-registry-mcp-server.md

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
meta
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 en issue/0064-registry-mcp-server, merge --no-ff a master.
  • Reutiliza registry/ package (ya carga registry.db con FTS5) y cmd/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 en registry_api del VPS.
  • Cada agente reinventa: sqlite3 registry.db "SELECT...", parseo manual, ejecucion via fn run shellando. Caro en tokens, error-prone, no compone.
  • fn CLI 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 (mcp paquete 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 que cmd/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_run y 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 (--stdio default, --http :PORT), arranque server, wiring tools.
  • server.go (NEW) — registro de tools en mcp-go, dispatcher.
  • tools/search.go (NEW) — fn_search, query a functions_fts + types_fts.
  • tools/show.go (NEW) — fn_show, lee fila completa + formatea markdown.
  • tools/code.go (NEW) — fn_code, devuelve code columna como string.
  • tools/list_domains.go (NEW) — fn_list_domains, agregados por (domain, kind, purity, lang).
  • tools/uses.go (NEW) — fn_uses, parsea uses_functions/uses_types JSON + busca consumidores reverso.
  • tools/doctor.go (NEW) — fn_doctor, llama a fn doctor --json como subprocess (mas barato que reimplementar).
  • tools/run.go (NEW pero detras de flag --enable-run por seguridad) — wrap de fn run via 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 consume registry/ package directamente.
  • .gitignore ya excluye apps/*/ excepto app.md. La app vive en su sub-repo Gitea (regla apps_tbd.md + apps_own_repo).

Pure / impure split

  • Pure (testeable sin BD):
    • format.RenderFunctionMarkdown(fn registry.Function) string
    • format.RenderTypeMarkdown(t registry.Type) string
    • tools.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_core para tipos de error.
  • http_json_response_go_infra, http_parse_body_go_infra si transport HTTP.
  • random_hex_id_go_core para 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 a fn-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_run ejecuta 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 tener gcc.
  • Tamaño de respuesta: fn_show puede devolver funciones de varios KB de codigo. Limitar a ~50 KB y truncar con aviso.
  • FTS5 quoting: la regla CLAUDE.md documenta gotchas (description:single-page rompe). 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 detecta SQLITE_CORRUPT o 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 en 0.0.0.0.
  • Drift entre PCs: registry.db local puede estar desactualizado vs registry_api. Documentar que el server consume el local y sugerir fn sync al usuario antes.
  • Logs ensucian stdio: Go SDK usa stdout para JSON-RPC. Cualquier fmt.Println rompe el protocolo. Forzar slog a stderr.
  • fn_run en 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-server desde master actualizado.
  • Confirmar las 8 decisiones de Fase 1 con el usuario.
  • Sub-repo Gitea dataforge/registry_mcp creado (Fase 2.2).
  • mark3labs/mcp-go validado (revisar version/maturity en Fase 1).

Criterios de aceptacion

  • claude desde cualquier directorio con MCP registry_mcp registrado puede llamar fn_search("slice") y obtener resultados sin shellar sqlite3.
  • 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.md con uses_functions actualizado.
  • README documenta instalacion para Claude Code y Claude Desktop.
  • fn doctor uses-functions no regresiona.