🔏 fix: Enforce MCP Server Authorization on Agent Tool Persistence#12250
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes an authorization gap in the agent create/update/duplicate controller paths where arbitrary MCP tool identifiers could be persisted without verifying the user is allowed to reference the embedded MCP server name, potentially granting unintended MCP “consumeOnly” access via stored mcpServerNames.
Changes:
- Introduces
filterAuthorizedTools()to validate tool identifiers, including MCP tool strings againstgetMCPServersRegistry().getAllServerConfigs(userId). - Applies the filtering on agent create, update, and duplicate flows before persisting tools.
- Adds MCP registry access to the agents controller.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Agent creation and update accepted arbitrary MCP tool strings without verifying the user has access to the referenced MCP servers. This allowed a user to embed unauthorized server names in tool identifiers (e.g. "anything_mcp_<victimServer>"), causing mcpServerNames to be stored on the agent and granting consumeOnly access via hasAccessViaAgent(). Adds filterAuthorizedTools() that checks MCP tool strings against the user's accessible server configs (via getAllServerConfigs) before persisting. Applied to create, update, and duplicate agent paths.
Addresses review findings on the MCP agent tool authorization fix: - Wrap getMCPServersRegistry() in try/catch so uninitialized registry gracefully filters all MCP tools instead of causing a 500 (DoS risk) - Guard revertAgentVersionHandler: filter unauthorized MCP tools after reverting to a previous version snapshot - Preserve existing MCP tools on collaborative updates: only validate newly added tools, preventing silent stripping of tools the editing user lacks direct access to - Add audit logging (logger.warn) when MCP tools are rejected - Refactor to single-pass lazy-fetch (registry queried only on first MCP tool encountered) - Export filterAuthorizedTools for direct unit testing - Add 18 tests covering: authorized/unauthorized/mixed tools, registry unavailable fallback, create/update/duplicate/revert handler paths, collaborative update preservation, and mcpServerNames persistence
…ertions - N1: Add duplicateAgentHandler integration test verifying unauthorized MCP tools are stripped from the cloned agent and mcpServerNames are correctly persisted in the database - N2: Replace all hardcoded '_mcp_' delimiter literals with Constants.mcp_delimiter to prevent silent false-positive tests if the delimiter value ever changes - N3: Add DB state assertion to the revert-with-strip test confirming persisted tools match the response after unauthorized tools are removed
d408771 to
73b9178
Compare
Reject MCP tool keys with multiple delimiters to prevent authorization/execution mismatch when `.pop()` vs `split[1]` extract different server names from the same key.
When the MCP registry is uninitialized (e.g. server restart), existing tools already persisted on the agent are preserved instead of silently stripped. New MCP tools are still rejected when the registry cannot verify them. Applies to duplicate and revert handlers via existingTools param; update handler already preserves existing tools via its diff logic.
jcbartle
pushed a commit
to jcbartle/LibreChat
that referenced
this pull request
May 11, 2026
…nny-avila#12250) * 🛡️ fix: Validate MCP tool authorization on agent create/update Agent creation and update accepted arbitrary MCP tool strings without verifying the user has access to the referenced MCP servers. This allowed a user to embed unauthorized server names in tool identifiers (e.g. "anything_mcp_<victimServer>"), causing mcpServerNames to be stored on the agent and granting consumeOnly access via hasAccessViaAgent(). Adds filterAuthorizedTools() that checks MCP tool strings against the user's accessible server configs (via getAllServerConfigs) before persisting. Applied to create, update, and duplicate agent paths. * 🛡️ fix: Harden MCP tool authorization and add test coverage Addresses review findings on the MCP agent tool authorization fix: - Wrap getMCPServersRegistry() in try/catch so uninitialized registry gracefully filters all MCP tools instead of causing a 500 (DoS risk) - Guard revertAgentVersionHandler: filter unauthorized MCP tools after reverting to a previous version snapshot - Preserve existing MCP tools on collaborative updates: only validate newly added tools, preventing silent stripping of tools the editing user lacks direct access to - Add audit logging (logger.warn) when MCP tools are rejected - Refactor to single-pass lazy-fetch (registry queried only on first MCP tool encountered) - Export filterAuthorizedTools for direct unit testing - Add 18 tests covering: authorized/unauthorized/mixed tools, registry unavailable fallback, create/update/duplicate/revert handler paths, collaborative update preservation, and mcpServerNames persistence * test: Add duplicate handler test, use Constants.mcp_delimiter, DB assertions - N1: Add duplicateAgentHandler integration test verifying unauthorized MCP tools are stripped from the cloned agent and mcpServerNames are correctly persisted in the database - N2: Replace all hardcoded '_mcp_' delimiter literals with Constants.mcp_delimiter to prevent silent false-positive tests if the delimiter value ever changes - N3: Add DB state assertion to the revert-with-strip test confirming persisted tools match the response after unauthorized tools are removed * fix: Enforce exact 2-segment format for MCP tool keys Reject MCP tool keys with multiple delimiters to prevent authorization/execution mismatch when `.pop()` vs `split[1]` extract different server names from the same key. * fix: Preserve existing MCP tools when registry is unavailable When the MCP registry is uninitialized (e.g. server restart), existing tools already persisted on the agent are preserved instead of silently stripped. New MCP tools are still rejected when the registry cannot verify them. Applies to duplicate and revert handlers via existingTools param; update handler already preserves existing tools via its diff logic.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes an authorization bypass where agent create, update, duplicate, and revert paths accepted arbitrary MCP tool strings without verifying the requesting user's access to the referenced MCP servers. A crafted identifier like
anything_mcp_<targetServer>would persisttargetServerinto the agent'smcpServerNamesarray, grantingconsumeOnlyaccess viahasAccessViaAgent()to a server the user was never authorized to use.filterAuthorizedTools()that validates each MCP tool string against the user's accessible server configs viagetMCPServersRegistry().getAllServerConfigs(userId)before persisting; non-MCP tools continue to be validated against the global tool cache andsystemToolsas before.undefinedas the unfetched sentinel.updateAgentHandlerby diffing incoming tools againstexistingAgent.tools— only newly added MCP tools are validated, preserving tools already on the agent that a collaborating editor may not have direct access to.revertAgentVersionHandler: filters unauthorized MCP tools from the restored version snapshot and re-persists viaupdateAgentwhen tools are stripped, ensuring the response reflects the committed DB state.logger.warnfor every rejected MCP tool (including tool name, server name, and user ID) and on registry unavailability, establishing an audit trail for adversarial input patterns.filterAuthorizedToolsfrommodule.exportsfor direct unit testing without handler overhead.Change Type
Testing
19 new tests in
api/server/controllers/agents/filterAuthorizedTools.spec.jsusingMongoMemoryServerwith the realAgentmodel and schema — no mocks for DB logic.Unit tests for
filterAuthorizedTools(9 cases):{}, returns only non-MCP tools, no 500userIdforwarded togetAllServerConfigsHandler integration tests (10 cases across 4 paths):
createAgentHandler: unauthorized MCP tools stripped from response and DB; no 500 on uninitialized registry;mcpServerNamespersisted only for authorized serversupdateAgentHandler: existing MCP tools preserved when editor lacks direct access; newly added unauthorized tools stripped; newly added authorized tools accepted; registry not queried when no new MCP tools presentduplicateAgentHandler: unauthorized MCP tools stripped from cloned agent response and DB;mcpServerNameson duplicate contains only authorized serversrevertAgentVersionHandler: unauthorized tools stripped after revert, DB state confirmed viaAgent.findOne; authorized tools survive revertTest Configuration
All 19 new tests pass. All 49 existing
v1.spec.jstests pass.Checklist