Skip to content

๐ŸŽš๏ธ feat: Let Chat Users Switch a Saved Agent's Tools - #16519

Open
TomasPalsson wants to merge 33 commits into
LibreChat-AI:devfrom
TomasPalsson:feat/agent-tool-user-toggles
Open

TomasPalsson wants to merge 33 commits into
LibreChat-AI:devfrom
TomasPalsson:feat/agent-tool-user-toggles

Conversation

@TomasPalsson

Copy link
Copy Markdown
Contributor

Pull Request

Summary

Today a saved agent's tools are all-or-nothing: whatever the creator attaches (web search, code execution, file search, MCP servers) runs on every message, and a chat user has no way to turn one off or opt into one. This PR lets a creator mark each built-in tool (web search, code, file search) and each MCP server as locked (today's behavior, the default), switchable, starts on, or switchable, starts off. Chat users see the switchable ones in the chat input of a saved-agent chat, flip them per chat, and the server only ever removes tools the creator made switchable โ€” a request can never add a tool the agent does not have or enable a locked one. Agents with no switchable tools, ephemeral chats and plain-endpoint chats behave exactly as before.

Related to #8096

How it works

The setting is stored per agent as tool_options[<tool key>].user_toggle: 'on' | 'off' (absent = locked), validated by the agent create/update schema. MCP servers use the existing sys__server__sys_mcp_<configured name> key. Two pure helpers in librechat-data-provider own the rules: getAgentToolSwitches(agent) returns the switchable set with creator defaults, and applyAgentToolSwitches(agent, requested) resolves the request against those defaults and returns the filtered tool list โ€” it only drops switchable tools that resolve to off.

sequenceDiagram
  participant B as Agent builder
  participant C as Chat input (BadgeRow)
  participant S as Server (loadAgent)
  B->>B: tool_options[key].user_toggle = on/off
  C->>C: getAgentToolSwitches(agent) โ†’ show switches, seed per-chat state from defaults
  C->>S: ephemeralAgent { web_search, execute_code, file_search, mcp[] }
  S->>S: primary agent only: applyAgentToolSwitches(agent, ephemeralAgent)
  S->>S: MCP instructions follow the filtered tools, not the request list
Loading
  • Server: packages/api/src/agents/load.ts filters the primary agent only (the memory agent and other agents loaded in the same request are untouched); api/server/services/Endpoints/agents/build.js only passes req.body.ephemeralAgent through. context.ts builds MCP instructions from the filtered tools when the agent has server switches, so a switched-off server's instructions are never injected and a paused/resumed turn rebuilds the same tool set.
  • GET /api/agents/:id for view-only users now also returns tools and a tool_options trimmed to user_toggle only, so shared chat users get their switches.
  • Client: builder setting in UserToggleSelect (built-in and MCP sections); chat switches in BadgeRow / BadgeRowContext / ToolsDropdown; useApplyAgentToolSwitches seeds a new chat from the creator's defaults, keeps the user's choices through the first message and reloads, and clears the previous agent's switches when the agent changes. A saved agent's switchable MCP servers are offered even when chatMenu: false hides them from the general menu.
  • MCP server names are matched by configured name everywhere (builder key, server filter, chat), including names that normalize (spaces) or contain _mcp_.

Type of change

  • Feature
  • Documentation

Testing

  1. Builder: set web search to "switchable, starts off" and an MCP server to "switchable, starts on"; save and reopen โ€” the setting is kept.
  2. New chat with that agent: both switches show with those defaults; the MCP server is selected.
  3. Turn web search on โ†’ it is used. Turn the MCP server off, send a message, reload โ†’ it stays off. New chat โ†’ back to the defaults.
  4. A hand-made request enabling a locked tool, or a tool the agent lacks, loads nothing extra.

Tested environments/configuration: local build on macOS, MongoDB 7 in Docker, a streamable-http MCP server with one tool, Chrome. Manually verified steps 1โ€“3 in the running app.

Automated tests:

  • packages/data-provider: npx jest agentToolOptions (20) + tsc --noEmit
  • packages/api: npx jest src/agents/__tests__/load.spec.ts src/agents/validation.spec.ts src/agents/context.spec.ts (125) + tsc --noEmit
  • api: npx jest server/controllers/agents/v1.spec.js (150)
  • client: npx jest SidePanel/Agents Chat/Input utils/__tests__ hooks/Agents hooks/MCP Providers useToolToggle timestamps (162 suites, 2648) + tsc --noEmit
  • npm run static-checks:full -- --against <base> and npm run -w @librechat/api openapi:check / openapi:test pass.

Risk / compatibility

  • No migration: the new field lives in the existing tool_options mixed field; agents without user_toggle are unchanged. The OpenAPI spec is regenerated for user_toggle.
  • View-only GET /api/agents/:id now includes tools (already exposed by the list endpoint) and only the user_toggle part of tool_options.
  • Per-chat choices live in browser storage like the existing plain-chat switches, so they share the same 2-day cleanup: a chat not opened for more than two days returns to the creator's defaults.
  • Known edge cases left for follow-up: a switchable server whose name ends with a locked server's _mcp_<name> suffix (e.g. bar and foo_mcp_bar) can resolve that locked server's tools as its own; a switchable server hidden by chatMenu: false can be pruned if the agent loads after the MCP server list. OpenAI-compatible and Responses API agent endpoints do not apply switches (no switch state there), matching today's behavior. Artifacts, memory and skills are not switchable in this PR.
  • Existing failures in packages/api/src/agents/hooks/reaper.spec.ts and executor.spec.ts (process-group signal escalation) reproduce locally without this change and are untouched here. The repository-wide eslint . --fix reports pre-existing errors in unrelated files; the branch-scoped static checks are clean.
  • Two tests were changed on purpose: a load.spec.ts assertion that required the request's mcp list to be deleted now asserts the request is left untouched, and a catalog.spec.ts case that only asserted the removed, unread userProvidedAuth flag was deleted along with that flag.

Checklist

  • I reviewed my own changes
  • Relevant tests have been added or updated
  • Existing relevant tests pass
  • The change does not introduce new warnings or errors
  • User-facing or complex behavior is documented where necessary
  • Required dependency changes have been merged/published
  • Required documentation PR: N/A

Saved agents can mark a built-in tool or a whole MCP server as switchable
by the chat user, with an on/off starting state. Adds the option type, the
helpers that read the switchable set and apply a chat's switch state (only
ever removing tools), and validation of the field on agent save.
Copilot AI balanced review requested due to automatic review settings September 29, 2026 18:32
@TomasPalsson
TomasPalsson marked this pull request as ready for review September 29, 2026 18:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@codegraph-librechat codegraph-librechat Bot added the ๐Ÿ—บ๏ธ Agent Chat UI codegraph: the taxonomy area this belongs to (classifier, confidence โ‰ฅ 0.9) label Oct 1, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

๐Ÿ—บ๏ธ Agent Chat UI codegraph: the taxonomy area this belongs to (classifier, confidence โ‰ฅ 0.9)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants