Skip to content

feat(tools): expose MCP tools through the @tools catalog#1129

Merged
diillson merged 2 commits into
mainfrom
feat/tools-catalog-mcp
Jul 3, 2026
Merged

feat(tools): expose MCP tools through the @tools catalog#1129
diillson merged 2 commits into
mainfrom
feat/tools-catalog-mcp

Conversation

@diillson

@diillson diillson commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Problema

O catálogo deferred de tools (@tools meta-tool) deixou as tools MCP no escuro no caminho de dispatch textual:

  • @tools describe/list respondiam unknown tool para qualquer nome mcp_* — o adapter só consultava o plugin registry.
  • O system prompt listava só nome+descrição e instruía o modelo a invocar às cegas para o sistema devolver o schema. Esse echo só dispara quando a tool declara parâmetros required — uma chamada vazia a uma tool só com parâmetros opcionais executa a tool de verdade como efeito colateral da "descoberta".
  • Em providers sem native tool calling (ex.: StackSpot), esse era o único caminho — a IA não tinha como aprender os parâmetros de uma tool MCP com segurança.

Solução

O catálogo agora cobre MCP de ponta a ponta:

  • mcp.Manager.VisibleTools — snapshots das tools descobertas honrando EnabledTools/DisabledTools (mesmo filtro de GetTools/GetToolsSummary), ordenados por nome para catálogo estável e cache-friendly entre turnos.
  • @tools describe mcp_<tool> — bloco completo: servidor, descrição, JSON Schema de parâmetros e forma de invocação. Aliases resolvem (@mcp_x, nome bare, case-insensitive) já que modelos derrubam prefixos rotineiramente; builtin com mesmo nome bare continua vencendo. Tool sem parâmetros instrui explicitamente args='{}'.
  • @tools list — seção MCP com entradas de uma linha no mesmo formato [MCP:server] que o system prompt já usa; contagem agrega builtins+MCP.
  • Prompt MCP — descoberta de schema roteada por @tools describe em vez de invocação cega; o echo de schema em chamada sem required params permanece como fallback.
  • Erro de tool desconhecida agora lista os nomes mcp_* disponíveis junto com os builtins.

Testes

  • TestManager_VisibleTools_FilterAndOrder — filtro de visibilidade + ordenação estável (ordem de iteração de map não vaza pro prompt).
  • TestToolCatalogAdapterDescribeMCP — describe com schema completo, aliases, tool schemaless, erro listando nomes MCP, e sessão sem MCP manager (sem panic).
  • TestToolCatalogAdapterListMCP — seção MCP no index, contagem agregada, ausência da seção sem MCP.
  • TestBuildMCPToolsSection — pina a nova instrução @tools describe no prompt.

go build ./..., go test ./... e golangci-lint run limpos.

The deferred tool catalog left MCP tools in the dark on the text-dispatch
path: the system prompt listed only name and description and told the model
to invoke blind so the system would echo the schema back. That echo only
fires when the tool declares required parameters — a schemaless call to a
tool with only optional parameters EXECUTES it as a side effect of
discovery — and `@tools describe`/`list` answered "unknown tool" for any
`mcp_*` name.

Now the catalog covers MCP end to end:

- `mcp.Manager.VisibleTools` returns visibility-filtered snapshots
  honoring `EnabledTools`/`DisabledTools`, sorted by name so the rendered
  catalog stays stable and cache-friendly across turns.
- `@tools describe mcp_<tool>` renders the full block: server, description,
  parameter JSON Schema and the invocation form. Prefix aliases resolve
  too, since models drop them routinely, and a builtin with the same bare
  name still wins.
- `@tools list` appends an MCP section with one-line index entries in the
  same `[MCP:server]` format the system prompt already uses.
- The MCP prompt section now routes schema discovery through
  `@tools describe` instead of blind invocation; the schema echo on a
  missing-required-params call stays as fallback.

Tests pin the visibility filter and ordering, the describe/list/alias/
error surfaces with and without an MCP manager wired, and the new prompt
instruction.
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Quality Gate

Result: ✅ all floors passed

Floor Status Result Δ vs main Budget
1 · Build & Static go build / vet / fmt / lint
2 · Coverage 48.3% (bootstrap) 0 ≥ baseline
3 · Patch coverage 90.8% (req ≥ 60%) ≥ 60%
4 · AI smells diff scanned
5 · Scope budget 8 files / 323 LOC (code 323 + tooling 0) warn 800·25
6 · E2E go test -race ./e2e/... ≤ 15min
7 · Commit lint conventional commits
8 · Cyclo (new code) 4 file(s) under threshold ≤ 30
9 · Secrets scan gitleaks
10 · i18n parity missing 0, unknown 0
11 · CRD drift drifted: 0
12 · License headers 0 missing
13 · API breaking 0 incompatible
14 · Binary size chatcli 90.7MB · operator 52.3MB 100MB each
15 · Provider parity 14 providers · 0 violations

Config: .github/quality-gate.yml. Workflow: .github/workflows/quality-gate.yml.

Provenance now reads at a glance: entries cluster under one header per
server, ordered by server name, while the per-line tag stays for models
that quote a single line out of context.
@diillson diillson merged commit 816245b into main Jul 3, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant