Skip to content

feat(agent): deferred MCP tool schema disclosure (Tool Search) - #2443

Merged
topcheer merged 3 commits into
mainfrom
mcp-tool-search
Sep 16, 2026
Merged

topcheer merged 3 commits into
mainfrom
mcp-tool-search

Conversation

@topcheer

Copy link
Copy Markdown
Owner

What

Implements Tool Search (deferred tool loading) for ggcode, porting Anthropic's Advanced Tool Use capability (beta advanced-tool-use-2025-11-20, docs) to the agent runtime.

When the tool registry carries >= 20 MCP tools (mcp__server__tool naming, e.g. many connected MCP servers), their JSON schemas are hidden behind a single tool_search meta-tool:

  1. Requests send core (built-in) tools + tool_search only — MCP schema tokens are no longer paid on every call.
  2. The model searches by keyword; matched schemas are returned in the tool result and activated (included in subsequent requests).
  3. Calling a deferred MCP tool by name auto-activates its schema (history carry-over safety).

Design constraints honored

  • Strictly monotonic activation — schemas are only ever added mid-run, never removed. This respects the documented failure mode of the previously-reverted dynamic tool pruning (comment at activeToolDefs in agent.go): mid-run list shrinkage destabilized models and misfired on CJK contexts.
  • Built-in tools never deferred — file/edit/search/run stay fully available from turn one; the discovery round-trip applies only to optional MCP integrations.
  • Agent-side meta-tooltool_search is not registry-backed: sub-agent clones, Registry.ToDefinitions() and permission flows stay untouched; activation state is per-agent and mutex-guarded for parallel tool execution.
  • Per-turn SetToolDefinitionOverhead now reflects the reduced list, so compaction accounting stays accurate.
  • Opt-out: GGCODE_TOOL_SEARCH=off (also auto-disables below 20 MCP tools to keep zero-round-trip behavior on small setups).

Files

  • internal/agent/tool_search.go — state, scoring search, meta-tool definition, executor (~260 lines)
  • internal/agent/agent.go — struct field, NewAgent init, run-start init, per-turn activeDefs
  • internal/agent/agent_tool.go — meta-tool dispatch + by-name auto-activation
  • internal/agent/tool_search_test.go — 7 tests: threshold, defer/activate, re-search no-match, auto-activate, env opt-out, deterministic order, concurrent activation (-race)

Verification

  • go build -tags goolm ./... ✅, go vet -tags goolm ./internal/agent ✅, gofmt clean ✅
  • go test -tags goolm -race -run ToolSearch ./internal/agent
  • go test -tags goolm -p 1 -parallel 1 ./internal/agent ✅ (full package)

Research

  • Anthropic Tool Search Tool: https://code.claude.com/docs/en/advanced-tool-use (tool search tool section)
  • Motivating data point: Claude Code + App Store Connect MCP session showed ~191 tool schemas dominating per-request context; deferred disclosure moves that cost to on-demand retrieval.

Co-Authored-By: ggcode noreply@ggcode.dev

topcheer pushed a commit that referenced this pull request Sep 16, 2026
…ting)

CodeQL flagged 6 critical alerts on PR #2443: the new tool_search
dispatch/auto-activate paths feed JSON-derived tool names into the
error-tracking pipeline, opening a new taint path to the '%s'
single-quote interpolation in error_cascade.go's guidance strings.

The strings are message text (not SQL/shell), so the exploitability is
theoretical, but %q is the correct fix: Go-quoted output is escape-safe
and the quoting pattern CodeQL keys on disappears. Args unchanged,
message quoting changes from 'key' to "key".

Co-Authored-By: ggcode <noreply@ggcode.dev>

@topcheer topcheer left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复审通过 ✅(6 PR 排程最后一环)

  • 单调激活语义正确:activated 只增不减——尊重 dynamic tool pruning 回退教训(agent.go activeToolDefs 注释的失败模式:mid-run 缩表 destabilize+CJK 误发),是本设计最重要的约束且被显式遵守
  • autoActivate 挂点精确:unknown-tool 检查之后、执行之前——deferred 工具 registry 里真实存在(不触发 unknown error),激活保证后续请求与历史引用一致(history carry-over 安全)
  • 边界完备:built-in 永不 deferred(file/edit/run 首轮全量)/20 MCP 门槛下自动关闭(小配置零往返)/GGCODE_TOOL_SEARCH=off 显式退出/compaction 的 SetToolDefinitionOverhead 同步缩减列表(记账准确)
  • 并发安全:toolSearchState 全 mu 守卫(并行工具执行下激活竞态防护)
  • error_cascade %q 顺带修复:即 CodeQL 污点根因(格式串注入面消除)——修复方向正确且是 main 既有 sink,纳入本 PR 合理
  • search 语义:AND-token 匹配+确定性序(sort.Strings)+limit 钳制+空 query 目录浏览;agent-side meta-tool 不进 registry(sub-agent clone/权限流零扰动)
  • 10 测试含 fakeMCPTool 集成

CI 全绿(CodeQL 复绿=污点消除验证)。可合并。

Junjun Zhang and others added 3 commits September 17, 2026 03:15
…-tool

Implements Tool Search (Anthropic Advanced Tool Use, advanced-tool-use-2025-11-20)
for ggcode: when the registry carries >=20 MCP tools (mcp__server__tool),
their schemas are hidden behind a single agent-side tool_search meta-tool.
The model searches on demand; matched schemas are returned in the tool
result and activated, so subsequent requests carry core tools + meta-tool +
activated schemas only.

Design constraints honored:
- Activation is strictly monotonic (schemas only added mid-run, never
  removed) — the failure mode that got dynamic tool pruning reverted.
- Built-in tools are never deferred; file/edit/search/run stay fully
  available from the first turn.
- Meta-tool handled agent-side (not registry-backed) so sub-agent clones
  and Registry.ToDefinitions stay untouched.
- Calling a deferred MCP tool by name auto-activates its schema (history
  carry-over safety); state is mutex-guarded for parallel tool execution.
- Per-turn SetToolDefinitionOverhead now reflects the reduced list.
- Opt-out: GGCODE_TOOL_SEARCH=off.

Research: https://docs.anth.com/en/docs/agents-and-tools/tool-use/tool-search-tool
(mirrored at code.claude.com/docs/en/advanced-tool-use)

Co-Authored-By: ggcode <noreply@ggcode.dev>

Co-Authored-By: ggcode <noreply@ggcode.dev>
Covers the integration points that unit tests on toolSearchState miss:
- executeTool dispatches the tool_search meta-tool agent-side (no registry
  lookup) and a successful search feeds the next request's active list
- by-name calls to deferred MCP tools execute normally AND auto-activate
  their schema (history carry-over consistency)
- below-threshold agents send the full registry list with no meta-tool

Co-Authored-By: ggcode <noreply@ggcode.dev>

Co-Authored-By: ggcode <noreply@ggcode.dev>
…ting)

CodeQL flagged 6 critical alerts on PR #2443: the new tool_search
dispatch/auto-activate paths feed JSON-derived tool names into the
error-tracking pipeline, opening a new taint path to the '%s'
single-quote interpolation in error_cascade.go's guidance strings.

The strings are message text (not SQL/shell), so the exploitability is
theoretical, but %q is the correct fix: Go-quoted output is escape-safe
and the quoting pattern CodeQL keys on disappears. Args unchanged,
message quoting changes from 'key' to "key".

Co-Authored-By: ggcode <noreply@ggcode.dev>
@topcheer
topcheer merged commit f7412a8 into main Sep 16, 2026
9 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