dynamic mcp: wire conversation workflows#775
Conversation
🤖 LLM Evaluation ResultsOpenAI
❌ Failed EvaluationsShow 7 failuresOPENAI1. TestReactEval/[openai]_react_cat_message
2. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json
3. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json
4. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json
5. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json
6. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json
7. TestDirectMessageConversations/[openai]_bot_dm_tool_introspection
Anthropic
❌ Failed EvaluationsShow 8 failuresANTHROPIC1. TestReactEval/[anthropic]_react_cat_message
2. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json
3. TestChannelSummarization/[anthropic]_channel_summarization_developers_webapp_channel
4. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json
5. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json
6. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json
7. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json
8. TestDirectMessageConversations/[anthropic]_bot_dm_tool_introspection
This comment was automatically generated by the eval CI pipeline. |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
📝 WalkthroughWalkthroughThis PR adds optional agent MCPDynamicToolLoading, preloads and validates embedded MCP tools (including bare-name aliasing and ServerOrigin checks), centralizes single-build LLM context construction with late conversation-ID binding, refactors tool-resolution and policy lookups, and expands tests and prompt text for dynamic MCP search/load workflows. ChangesDynamic MCP Tool Loading Feature
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (2)
api/api_channel_analysis_test.go (1)
158-194: 💤 Low valueConsider closing the response body to prevent resource leaks.
At line 186,
resp := recorder.Result()returns an*http.Responsewhose body should be closed. While this rarely causes issues in tests, it's good hygiene to close it.🧹 Suggested fix
recorder := httptest.NewRecorder() e.api.ServeHTTP(&plugin.Context{}, recorder, request) resp := recorder.Result() + defer resp.Body.Close() require.Equal(t, http.StatusOK, resp.StatusCode)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@api/api_channel_analysis_test.go` around lines 158 - 194, In TestHandleChannelAnalysisPreloadsRequiredMCPTools, the http.Response returned by recorder.Result() (resp) is never closed; after calling resp := recorder.Result() add a resp.Body.Close() (or immediately defer resp.Body.Close()) to avoid leaking the response body; locate the resp variable in TestHandleChannelAnalysisPreloadsRequiredMCPTools and insert the close right after obtaining resp.channels/channels.go (1)
117-144: 💤 Low valueRe-aliasing tool name alters the returned tool's identity.
At line 142,
tool.Name = namemutates the copy to replace the namespaced name (e.g.,mattermost__read_channel) with the bare name (read_channel). This is intentional per the docstring inapi/api_channel.go(lines 190-195) explaining that preload re-aliases tools. However, this implicit mutation could surprise callers who expect the original namespaced name to be preserved.Consider adding a brief inline comment to clarify this intentional re-aliasing, e.g.,
// Return tool with bare name for scoped binding.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@channels/channels.go` around lines 117 - 144, In requiredEmbeddedToolByExactOrBareName, callers may be surprised by the intentional re-aliasing of the tool name; add a brief inline comment immediately before the statement that sets tool.Name = name to explain this behavior (e.g., "// Return tool with bare name for scoped binding" or similar) so readers understand the mutation is deliberate and matches the preload re-aliasing behavior referenced in api/api_channel.go.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@api/api_agents_test.go`:
- Around line 246-320: Combine the three separate tests
TestCreateAgentMCPDynamicToolLoadingDefaultsTrueWhenOmitted,
TestCreateAgentMCPDynamicToolLoadingPersistsExplicitFalse, and
TestCreateAgentMCPDynamicToolLoadingPersistsExplicitTrue into a single
table-driven test that iterates cases for "omitted", "explicit true", and
"explicit false" and runs each as a subtest; keep the shared setup calls
(setupAgentTestEnvironment, mockLicensed, mocks for
HasPermissionTo/CreateBot/LogError) and for each case supply the request body
(nil with deletion for omitted, or map with "mcpDynamicToolLoading":
true/false), perform doRequest, decode into llm.BotConfig and assert both the
response value and stored value in
e.agentStore.agents[agent.ID].MCPDynamicToolLoading accordingly; also refactor
the similar tests in the later block referenced (lines ~895-1020) the same way
so all MCP dynamic-tool-loading variants are covered by a single table-driven
subtest loop.
In `@api/api_mcp_test.go`:
- Around line 98-212: Combine the three separate tests
TestHandleGetUserMCPToolsReturnsBareNamesForNamespacedRuntimeTools,
TestHandleGetUserMCPToolsBareAllowlistUICompatibility, and
TestHandleGetUserMCPToolsReturnsUpstreamDescriptionNotOverride into a single
table-driven test (e.g. TestHandleGetUserMCPTools_TableDriven) that iterates
over cases with fields for server config, mock tool (use
mockMCPClientManager.tools), expected tool.Name, Enabled, Policy and
Description; inside the loop call SetupTestEnvironment once per subtest, set
e.config.mcpConfig and e.api.mcpClientManager, call getUserMCPToolsResponse and
assert the expected values using t.Run for each case, keeping existing unique
symbols (getUserMCPToolsResponse, mockMCPClientManager, mcp.ServerConfig,
mcp.ToolConfig) so behavior from the three original tests is preserved as table
entries.
In `@conversations/bot_channel_tool_filter_test.go`:
- Around line 247-452: Combine the repeated "ChannelFollowUpStrict" tests into a
single table-driven subtest that iterates cases (e.g., failures on GetPost,
GetUser, DM-no-filter, ActivateAI true) instead of duplicating logic; for each
case provide inputs/fixtures (origin, config, builder tools, mmClient
expectations, rootPost/user, toolPolicyChecker) and assertions, then run the
same flow that calls channelFollowUpMCPToolFilterContextOptions,
buildChannelFollowUpStrictContext, and uses channelFollowUpSearchToolNames to
assert presence/absence of "jira__safe_tool" and "jira__ask_tool"; retain and
reuse helper symbols like newChannelFollowUpTestBuilder,
channelFollowUpTestMCPTool, buildChannelFollowUpStrictContext, and
channelFollowUpMCPToolFilterContextOptions to construct each case and move
per-case mmClient setups into the table entries' setup functions to keep tests
concise and table-driven.
In `@conversations/dynamic_mcp_workflow_test.go`:
- Around line 78-250: Combine TestDynamicMCPStrictSearchLoadCallHappyPath and
TestDynamicMCPLoadedToolStillRequiresApprovalWhenPolicyAsks into a single
table-driven test that iterates over cases differing by toolPolicyChecker (e.g.,
origin -> "get_issue" {policy: mcp.ToolPolicyAutoRunInDM, enabled:true} vs
mcp.ToolPolicyAsk) and expected outcomes (resolverCalls >0 vs 0,
foundPendingBusinessTool true/false, number of lm.requests, turns length,
loadedStore rows assertions). Reuse the shared harness setup
(loadedStateConversationStore(), loadedStateStore{}, jiraTool,
newChannelFollowUpTestBuilder, builder.SetLoadedMCPToolStore(loadedStore), lm :=
&dynamicWorkflowLLM{}, bot := loadedStateBot(lm), llmContext :=
builder.BuildLLMContextUserRequest(...), and c := &Conversations{...}) inside
the loop, and assert per-case expectations for resolverCalls, tool approval
events, lm.requests indices (check
lm.requests[2].Context.Tools.GetTool("jira__get_issue")), and persisted
loadedStore rows; leave TestDynamicMCPMetaToolsBypassApproval as a separate test
unchanged.
In `@prompts/standard_personality_without_locale_test.go`:
- Around line 184-252: The four tests
TestStandardPersonalityIncludesDynamicToolWorkflowWhenEnabled,
TestStandardPersonalityIncludesDynamicToolWorkflowWithDisabledToolsInfo,
TestStandardPersonalityOmitsDynamicToolWorkflowWhenFlagOff, and
TestStandardPersonalityOmitsDynamicToolWorkflowWithoutMetaTools should be
combined into a single table-driven test that iterates subtests via t.Run;
create a slice of cases with a name, an llm.Context (varying Tools:
dynamicMetaToolStore() vs llm.NewNoTools(), MCPDynamicToolLoading true/false,
and DisabledToolsInfo when needed), and expected positive/negative string
assertions, then for each case call renderStandardPersonalityWithoutLocale and
run assert.Contains/assert.NotContains according to the case expectations; keep
references to renderStandardPersonalityWithoutLocale, dynamicMetaToolStore,
llm.NewNoTools, DisabledToolsInfo, and MCPDynamicToolLoading when constructing
the cases so the logic is identical to the original tests.
---
Nitpick comments:
In `@api/api_channel_analysis_test.go`:
- Around line 158-194: In TestHandleChannelAnalysisPreloadsRequiredMCPTools, the
http.Response returned by recorder.Result() (resp) is never closed; after
calling resp := recorder.Result() add a resp.Body.Close() (or immediately defer
resp.Body.Close()) to avoid leaking the response body; locate the resp variable
in TestHandleChannelAnalysisPreloadsRequiredMCPTools and insert the close right
after obtaining resp.
In `@channels/channels.go`:
- Around line 117-144: In requiredEmbeddedToolByExactOrBareName, callers may be
surprised by the intentional re-aliasing of the tool name; add a brief inline
comment immediately before the statement that sets tool.Name = name to explain
this behavior (e.g., "// Return tool with bare name for scoped binding" or
similar) so readers understand the mutation is deliberate and matches the
preload re-aliasing behavior referenced in api/api_channel.go.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: f68c9585-15d4-4ad1-a5e3-c509b9947d0b
📒 Files selected for processing (22)
api/api_agents.goapi/api_agents_test.goapi/api_channel.goapi/api_channel_analysis_test.goapi/api_config_test.goapi/api_llm_bridge.goapi/api_mcp_test.gochannels/analysis_conversation_test.gochannels/channels.goconversations/bot_channel_tool_filter.goconversations/bot_channel_tool_filter_test.goconversations/conversations.goconversations/dynamic_mcp_workflow_test.goconversations/handle_messages.goconversations/loaded_state_flow_test.goconversations/regeneration.goconversations/single_build_test.goconversations/tool_approval.goconversations/tool_approval_internal_test.goconversations/tool_policy_test.goprompts/standard_personality_without_locale.tmplprompts/standard_personality_without_locale_test.go
e882aa7 to
80e5ad8
Compare
95f8795 to
b365666
Compare
80e5ad8 to
c383e63
Compare
b365666 to
a8e402c
Compare
c383e63 to
1c509aa
Compare
a8e402c to
461b81d
Compare
Co-authored-by: Cursor <cursoragent@cursor.com>
461b81d to
df9713a
Compare
Co-authored-by: Cursor <cursoragent@cursor.com>
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
conversations/handle_messages.go (1)
438-458:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRe-prune disabled MCP servers after
AttachConversationID.
buildConversationContextWithToolsstripsDisabledMCPServerOriginsbefore the conversation exists, butAttachConversationIDrestores loaded tools afterwards. In a DM or group DM, that can put a previously loaded tool from a user-disabled server back intollmContext.Tools, so existing conversations ignore the user's MCP preference.Proposed fix
c.contextBuilder.AttachConversationID(llmContext, bot, convResult.ConversationID) + removePreFilteredMCPServersFromVisibleStore(llmContext) // Anchor this run's trace to the user turn ID. Link to the previous user🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@conversations/handle_messages.go` around lines 438 - 458, The context is rebuilt with buildConversationContextWithTools which prunes DisabledMCPServerOrigins too early and AttachConversationID can re-add tools; after calling c.contextBuilder.AttachConversationID(llmContext, bot, convResult.ConversationID) re-apply the pruning of DisabledMCPServerOrigins (or call the existing helper that removes user-disabled MCP servers from llmContext.Tools) so DM/group DM flows do not reintroduce tools the user disabled; update the sequence around CreateOrGetDMConversation / AttachConversationID to prune llmContext.Tools immediately after AttachConversationID (ensuring ensureDMWebSearchTracking still runs on the final pruned context).
🧹 Nitpick comments (2)
api/api_channel_analysis_test.go (1)
196-212: ⚡ Quick winConvert this two-scenario test to table-driven subtests.
This function exercises two distinct cases (non-embedded origin missing, then embedded origin present) but keeps them in one linear test body. Please convert to a
tests := []struct{...}+t.Run(...)pattern for consistency with repo test rules.♻️ Suggested refactor
func TestChannelAnalysisToolAvailabilityRequiresEmbeddedOrigin(t *testing.T) { - tools := llm.NewToolStore(nil, false) - tools.AddTools([]llm.Tool{ - {Name: "read_channel", ServerOrigin: "https://remote.example.com"}, - {Name: "get_channel_info", ServerOrigin: mcp.EmbeddedClientKey}, - }) - - _, missing := channelAnalysisToolAvailability(tools) - require.ElementsMatch(t, []string{"read_channel"}, missing) - - tools.AddTools([]llm.Tool{ - {Name: "read_channel", ServerOrigin: mcp.EmbeddedClientKey}, - }) - - _, missing = channelAnalysisToolAvailability(tools) - require.Empty(t, missing) + tests := []struct { + name string + tools []llm.Tool + wantMissing []string + }{ + { + name: "marks non-embedded required tool as missing", + tools: []llm.Tool{ + {Name: "read_channel", ServerOrigin: "https://remote.example.com"}, + {Name: "get_channel_info", ServerOrigin: mcp.EmbeddedClientKey}, + }, + wantMissing: []string{"read_channel"}, + }, + { + name: "accepts embedded required tools", + tools: []llm.Tool{ + {Name: "read_channel", ServerOrigin: mcp.EmbeddedClientKey}, + {Name: "get_channel_info", ServerOrigin: mcp.EmbeddedClientKey}, + }, + wantMissing: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + store := llm.NewToolStore(nil, false) + store.AddTools(tt.tools) + _, missing := channelAnalysisToolAvailability(store) + require.ElementsMatch(t, tt.wantMissing, missing) + }) + } }As per coding guidelines:
**/*_test.go: Go tests must be table-driven when there is more than one test case.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@api/api_channel_analysis_test.go` around lines 196 - 212, Replace the linear TestChannelAnalysisToolAvailabilityRequiresEmbeddedOrigin with a table-driven test: create a tests := []struct{name string, initialTools []llm.Tool, addTools []llm.Tool, expectedMissing []string}{...} containing the two scenarios (first: initialTools includes read_channel with ServerOrigin "https://remote.example.com" and get_channel_info with mcp.EmbeddedClientKey expecting missing ["read_channel"]; second: initialTools includes both tools with EmbeddedClientKey expecting missing []), then iterate tests with t.Run(tc.name, func(t *testing.T){ tools := llm.NewToolStore(nil, false); tools.AddTools(tc.initialTools); if len(tc.addTools)>0 { tools.AddTools(tc.addTools) }; _, missing := channelAnalysisToolAvailability(tools); use require.ElementsMatch/require.Empty as appropriate }), making sure each subtest creates a fresh ToolStore to avoid state leakage.conversations/bot_channel_tool_filter_test.go (1)
206-208: Zero-argllm.NewToolStore()is safe (equivalent tollm.NewToolStore(nil, false)).
llm.NewToolStoreisfunc NewToolStore(options ...any) *ToolStore; when called with no arguments it defaults tolog=nilanddoTrace=false, sollm.NewToolStore()at lines 206-208 won’t cause a compile-time break. Standardizing tollm.NewToolStore(nil, false)is optional for consistency.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@conversations/bot_channel_tool_filter_test.go` around lines 206 - 208, The call llm.NewToolStore() is valid but for consistency make its defaults explicit: in the llm.Context initialization replace llm.NewToolStore() with llm.NewToolStore(nil, false) so the intent (log=nil, doTrace=false) is clear; update the llm.Context creation where Tools is set to reference the explicit call to llm.NewToolStore(nil, false).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@conversations/loaded_state_flow_test.go`:
- Around line 213-216: The fake UpsertLoadedMCPTool currently appends every
tool, causing duplicates; change loadedStateStore.UpsertLoadedMCPTool to
implement upsert semantics by scanning s.rows for an existing
store.LoadedMCPTool with the same identity (match on the unique identifier
field(s) used by LoadedMCPTool, e.g., ID or Name), replace that entry in-place
if found, and return; only append if no match was found. Use the existing
function name UpsertLoadedMCPTool and the loadedStateStore.rows slice to locate
and update the matching element.
---
Outside diff comments:
In `@conversations/handle_messages.go`:
- Around line 438-458: The context is rebuilt with
buildConversationContextWithTools which prunes DisabledMCPServerOrigins too
early and AttachConversationID can re-add tools; after calling
c.contextBuilder.AttachConversationID(llmContext, bot,
convResult.ConversationID) re-apply the pruning of DisabledMCPServerOrigins (or
call the existing helper that removes user-disabled MCP servers from
llmContext.Tools) so DM/group DM flows do not reintroduce tools the user
disabled; update the sequence around CreateOrGetDMConversation /
AttachConversationID to prune llmContext.Tools immediately after
AttachConversationID (ensuring ensureDMWebSearchTracking still runs on the final
pruned context).
---
Nitpick comments:
In `@api/api_channel_analysis_test.go`:
- Around line 196-212: Replace the linear
TestChannelAnalysisToolAvailabilityRequiresEmbeddedOrigin with a table-driven
test: create a tests := []struct{name string, initialTools []llm.Tool, addTools
[]llm.Tool, expectedMissing []string}{...} containing the two scenarios (first:
initialTools includes read_channel with ServerOrigin
"https://remote.example.com" and get_channel_info with mcp.EmbeddedClientKey
expecting missing ["read_channel"]; second: initialTools includes both tools
with EmbeddedClientKey expecting missing []), then iterate tests with
t.Run(tc.name, func(t *testing.T){ tools := llm.NewToolStore(nil, false);
tools.AddTools(tc.initialTools); if len(tc.addTools)>0 {
tools.AddTools(tc.addTools) }; _, missing :=
channelAnalysisToolAvailability(tools); use require.ElementsMatch/require.Empty
as appropriate }), making sure each subtest creates a fresh ToolStore to avoid
state leakage.
In `@conversations/bot_channel_tool_filter_test.go`:
- Around line 206-208: The call llm.NewToolStore() is valid but for consistency
make its defaults explicit: in the llm.Context initialization replace
llm.NewToolStore() with llm.NewToolStore(nil, false) so the intent (log=nil,
doTrace=false) is clear; update the llm.Context creation where Tools is set to
reference the explicit call to llm.NewToolStore(nil, false).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: c75c0cbb-76dd-4424-ab48-abef4f97506a
📒 Files selected for processing (22)
api/api_agents.goapi/api_agents_test.goapi/api_channel.goapi/api_channel_analysis_test.goapi/api_config_test.goapi/api_llm_bridge.goapi/api_mcp_test.gochannels/analysis_conversation_test.gochannels/channels.goconversations/bot_channel_tool_filter.goconversations/bot_channel_tool_filter_test.goconversations/conversations.goconversations/dynamic_mcp_workflow_test.goconversations/handle_messages.goconversations/loaded_state_flow_test.goconversations/regeneration.goconversations/single_build_test.goconversations/tool_approval.goconversations/tool_approval_internal_test.goconversations/tool_policy_test.goprompts/standard_personality_without_locale.tmplprompts/standard_personality_without_locale_test.go
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Stale comment
Security Review — No Findings
I performed a comprehensive security analysis of the changes in this PR which wire dynamic MCP tool loading into conversation, approval, channel analysis, regeneration, and API flows. I examined:
Tool resolution and approval flow (
resolveApprovedToolUseBlock): ValidatesServerOriginandMCPBareNamemetadata from stored conversation blocks before executing, preventing tool substitution after registry changes. TheHandleToolCallpath verifiesconv.UserID == userID(requester-only gating).Bot-activated channel constraint enforcement: The pre-build
WithLLMContextMCPToolFilterrestricts the MCP tool registry to onlyauto_run_everywhere-policy tools before the strict store is built. This meanssearch_tools/load_toolmeta-tools can only discover/load tools with the appropriate policy — not bypass the constraint. The post-buildapplyBotChannelAutoEverywhereToolFilterprovides defense-in-depth.DM disabled-server re-pruning:
removePreFilteredMCPServersFromVisibleStoreis correctly called afterAttachConversationID(which may restore loaded tools), so user-disabled MCP servers remain pruned even after late-binding the conversation.Fail-closed patterns: When
toolPolicyCheckeris nil,shouldAutoExecuteToolreturns false,botChannelAutoEverywhereKeepToolreturns false, andallToolsAutoRunEverywherereturns false — all fail-closed.
Sharedflag scoping: TheHandleToolResultchange narrowsShared=trueto only tool blocks belonging to the clicked post (viaclickedPostToolUseIDs), reducing the blast radius of the share decision — a security improvement.Meta-tool identity check:
isMCPMetaToolCallrequires BOTH name-match AND emptyServerOrigin, preventing an MCP server from registering a tool namedsearch_toolsto gain auto-approval.No medium, high, or critical vulnerabilities identified.
Sent by Cursor Automation: Find vulnerabilities
There was a problem hiding this comment.
Security Review — No Findings
I reviewed the production code changes in this PR with focus on the security-sensitive areas for a multi-user Mattermost plugin:
Areas Analyzed
-
MCP meta-tools (
search_tools,load_tool) bypassing approval — These are intentionally auto-approved as they are read-only discovery/loading operations.search_toolsonly returns tool names/summaries from the user-scoped registry (built viaGetToolsForUser).load_toolonly materializes a tool's schema. Business tools loaded viaload_toolstill go through full policy enforcement before execution. -
Channel tool filtering preserving meta-tools —
applyBotChannelAutoEverywhereToolFilternow preserves meta-tools even in fail-closed mode. This is safe because loaded business tools still requireauto_run_everywherepolicy to auto-execute in channels, and tools withaskpolicy surface for user approval. -
resolveApprovedToolUseBlockserver origin validation — This is a security improvement. The new function validatesServerOriginandMCPBareNameagainst persisted approval metadata before executing, preventing tool identity spoofing during deferred approval execution. -
resolveToolStoreLookupNameambiguity handling — Fails closed when a bare tool name maps to multiple registered tools without a disambiguatingServerOrigin. Prevents unintended tool execution. -
Meta-tool spoofing resistance —
isMCPMetaToolCallrequirestc.ServerOrigin == ""and verifies the tool store doesn't associate the name with a non-empty origin, preventing an external MCP server from registering tools namedsearch_tools/load_toolthat bypass approval. -
Prompt injection via dynamic tool workflow — The system prompt instructs the LLM to ask clarification before side-effecting actions. While this is a defense-in-depth measure (LLM instructions are not a security boundary), the server-side policy enforcement (
shouldAutoExecuteTool,allToolsAutoRunEverywhere) provides the actual gate — tools that need approval still require it regardless of what the LLM is instructed to do.
Conclusion
No medium, high, or critical vulnerabilities identified. The PR maintains existing permission/authorization patterns while adding server-origin validation on tool approval execution (a security hardening).
Sent by Cursor Automation: Find vulnerabilities


Summary
Wires dynamic MCP loading into conversation, approval, channel analysis, regeneration, and API flows, including safer prompt guidance for underspecified side-effecting actions.
Stack: 5/6
Ticket Link
N/A
Screenshots
N/A
Release Note
Made with Cursor
Summary by CodeRabbit
New Features
Bug Fixes
Tests