fix(infra): gradle_run detecta android-sdk — issue 0076 #2
@@ -79,3 +79,4 @@
|
||||
| [0061](0061-notify-telegram-integration.md) | Integrar `notify_telegram` en deploy_server + bucle reactivo | pendiente | media | integration | 0054 |
|
||||
| [0062](0062-deprecate-unused-core-functions.md) | Politica de deprecacion para funciones del registry sin consumidores | pendiente | baja | research | — |
|
||||
| [0063](0063-kanban-stickers.md) | kanban: sistema de stickers (emojis) sobre cards | pendiente | media | feature | — |
|
||||
| [0064](completed/0064-registry-mcp-server.md) | registry_mcp: servidor MCP que expone registry.db a Claude | completado | alta | feature | — |
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
# 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:
|
||||
|
||||
```bash
|
||||
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)
|
||||
|
||||
```jsonc
|
||||
// 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.
|
||||
Reference in New Issue
Block a user