feat: declare dynamically_loaded_tools for official models and add per-server MCP deferred disclosure - #3667
Conversation
🦋 Changeset detectedLatest commit: 9a6a9e3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 279e72ed39
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| // catalogModelToCapability reads it; stripping it here would silently | ||
| // disable tool-select for catalog-imported aliases. | ||
| "dynamically_loaded_tools", | ||
| "dynamic_tools", |
There was a problem hiding this comment.
Preserve the models.dev capability field
The models.dev catalog schema and compatible mirrors expose this as dynamically_loaded_tools; replacing that wire key with the internal dynamic_tools name causes stripModel() to discard the capability from fetched catalogs. Consequently, catalog-imported models that previously advertised support are emitted without the bit and tool selection remains disabled; retain the wire key here and translate it while normalizing the catalog.
Useful? React with 👍 / 👎.
| dynamic_tools: | ||
| declared.has('dynamic_tools') || | ||
| detected.dynamic_tools === true, |
There was a problem hiding this comment.
Continue recognizing the existing config capability
For users whose existing config.toml model aliases declare dynamically_loaded_tools, this resolver now silently ignores that declaration, so enabling the tool-select flag no longer activates progressive disclosure after upgrading. Since this is being released as a patch rather than a confirmed major change, accept both spellings during resolution and normalize to dynamic_tools internally.
AGENTS.md reference: AGENTS.md:L64-L64
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e274157fc7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| }); | ||
| const disposable = this._register(this.registry.register(tool, { source: 'mcp' })); | ||
| const disposable = this._register( | ||
| this.registry.register(tool, { source: 'mcp', disclosure: 'deferred' }), |
There was a problem hiding this comment.
Honor deferred:false for MCP authentication tools
When a remote MCP server configured with deferred: false initially enters needs-auth while the tool-select gate is active, this branch still marks its synthetic authentication tool as deferred. AgentToolSelectService consequently hides that tool from the top-level tool list until it is selected dynamically, so the server's explicit inline-disclosure opt-out is not honored during authentication; propagate the server's configured disclosure setting here as the connected-server path does.
Useful? React with 👍 / 👎.
f791d9a to
154056b
Compare
…icial models endpoint
…less deferred is enabled
154056b to
9a6a9e3
Compare
…eeps a whole server eager Port from MoonshotAI/kimi-code#3667: kimi-code lets one MCP server opt into deferred disclosure while its siblings stay inline. Hermes defers every MCP tool by default, so the useful granularity is the inverse: pin one small, every-turn server into tools[] while tool-heavy servers keep deferring. Builds on NousResearch#110714 (tools.tool_search.eager, per tool name): an eager entry may now also name a toolset (`mcp-<server>`), and `mcp_servers.<name>.defer: false` folds into that set at config load. Only an explicit boolean False pins. Before: with tool search on, `mcp_servers.notes.defer: false` had no effect — tools[] = bridge only. After: notes' tools stay visible next to the bridge; the sibling server still defers (E2E on a temp HERMES_HOME). (cherry picked from commit af654fb)
Related Issue
Internal feature work (no linked issue).
Problem
The official models endpoint advertises dynamic tool loading (
supports_dynamic_tools), but the CLI never parsed or declared that capability, so the experimentalselect_toolson-demand loading path could never turn on for official models. Separately, every MCP server's tools always occupied the immutable top-leveltools[]list — there was no way to keep a tool-heavy server's schemas out of the prompt unless the model loaded them on demand.What changed
supports_dynamic_toolsfrom the official models endpoint and map it to thedynamically_loaded_toolsmodel capability, so official models declare dynamic tool loading automatically.mcp.jsonserver entries acceptdeferred(all transports, shared schema across config loading, configStore, management CRUD, and plugin manifests). With the experimentaltool-selectflag and a model declaringdynamically_loaded_tools, a server markeddeferred: truekeeps its tools out of the top-level list and the model loads them viaselect_tools. Servers default to inline disclosure — tools are always exposed unlessdeferred: trueis set explicitly; the synthetic authenticate tool of a needs-auth server follows the same field.deferredfield in the MCP config contract used by session creation options.select_toolsregardless of the profile tool allowlist (still vetoable viadisallowedTools), aligning withtoolPolicyService.isToolActiveForDisclosure.capabilitiesgainsdynamically_loaded_tools, andKIMI_CODE_EXPERIMENTAL_TOOL_SELECTis documented.Checklist
/approve).gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.