Jminnetian/sanitize tool names - #1423

Merged
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names
Jul 10, 2026
Merged

Jminnetian/sanitize tool names#1423
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names

Conversation

@jsourcebot

@jsourcebotjsourcebot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

MCP server tool names with certain characters (e.g " . ") would break the tool names open AI was expecting with their models. We now sanitize the names to avoid running into this issue.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed Ask connector MCP tools failing when provider-facing names contain unsupported characters.
    • MCP tool names are now normalized for reliable execution while preserving their original names in the chat interface.
    • Updated tool approvals, requests, search results, and status messages to show clearer server and tool names.
  • Tests
    • Added coverage for name normalization, display-name mapping, tool execution, and UI rendering.

Note

Low Risk
Targeted EE Ask/MCP naming and display path with tests; execution still uses raw MCP names, so risk is limited to display/metadata and model-facing identifiers.

Overview
Ask connector MCP tools whose names include characters providers reject (e.g. dots in catalog.query-catalog-entities) were failing at the model layer. This change normalizes fully qualified tool keys exposed to the LLM via sanitizeMcpToolNameForModel (invalid chars → _, max 64 chars) while MCP execution and persistence still use the server’s raw tool name.

The agent now streams data-mcp-tool parts mapping modelToolNamerawToolName. The chat thread hydrates that into McpToolNameContext (alongside server favicons in the renamed mcpDisplayMetadataContext). Tool cards, approval banners, and connector tool search use getMcpToolDisplayParts so users see names like backstage: catalog.query-catalog-entities instead of the sanitized model identifier; older threads without metadata keep the previous parse-based fallback.

Tests cover sanitization, getMcpTools behavior, agent streaming, and UI rendering.

Reviewed by Cursor Bugbot for commit 2edd846. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

MCP tool names are sanitized for provider compatibility, while raw names are propagated through streamed metadata and restored in chat tool, approval, and search displays.

Changes

MCP tool name sanitization and display mapping

Layer / File(s)Summary
Sanitization and raw-name mapping
packages/web/src/ee/features/chat/mcp/mcpToolSets.ts, packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts, CHANGELOG.md
Sanitizes model-facing MCP tool names, preserves raw names in toolDisplayNames, updates the result shape, and tests replacement, truncation, and mapping behavior.
Agent discovery and streamed metadata
packages/web/src/features/chat/types.ts, packages/web/src/ee/features/chat/agent.ts, packages/web/src/ee/features/chat/agent.test.ts
Adds the MCP tool data-part contract, emits model/raw name pairs during MCP discovery, and verifies the streamed payload.
Chat thread mapping state
packages/web/src/ee/features/chat/mcpDisplayMetadataContext.tsx, packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx, packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
Stores mappings from initial and streamed messages, provides them through context, and excludes mapping parts from direct step rendering.
Tool display rendering
packages/web/src/ee/features/chat/components/chatThread/tools/*, packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
Uses mapped raw names in MCP tool, approval, and tool-search displays, with fallback formatting and rendering tests.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
participant MCPToolSets
participant createAgentStream
participant ChatThread
participant ToolComponents
MCPToolSets->>createAgentStream: expose sanitized tool names and raw-name mappings
createAgentStream->>ChatThread: emit data-mcp-tool model/raw pairs
ChatThread->>ToolComponents: provide McpToolNameContext
ToolComponents->>ToolComponents: render resolved display names
Loading

Suggested reviewers:msukkari

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title matches the core change: sanitizing tool names, though the prefix adds unnecessary noise.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jminnetian/sanitize-tool-names

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts (1)

117-236: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a test for punctuation-collision sanitization.

The new "keeps valid hyphenated tool names distinct from underscored names" test (Lines 220-235) only exercises a non-colliding case (- is preserved). Consider adding a test with two raw tool names that both sanitize to the same string (e.g. foo.bar and foo_bar) to lock in the intended collision-handling behavior once it's implemented — see the related comment in mcpToolSets.ts.

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts` around lines 117 -
236, Add a collision-focused test in sanitizeMcpToolNameForModel/getMcpTools
coverage: the current hyphen-vs-underscore case only proves distinct valid names
stay distinct. Create a test with two raw MCP tool names that sanitize to the
same model-facing key (for example punctuation vs underscore), and assert the
intended collision-handling behavior in getMcpTools, including the resulting
tools map and any disambiguation strategy referenced by mcpToolSets.ts.
🤖 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 `@CHANGELOG.md`:
- Line 35: The new CHANGELOG.md entry under [Unreleased] is missing the required
pull request link. Update the entry so the single-sentence description is
followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts`:
- Around line 118-127: Sanitized MCP tool names can collide, causing later
entries to overwrite earlier ones in both allTools and toolDisplayNames. Update
the MCP tool registration flow in mcpToolSets.ts where qualifiedName is passed
through sanitizeMcpToolNameForModel so collisions are detected and handled
instead of silently overwriting; if two raw names sanitize to the same key,
disambiguate the generated key (or otherwise preserve uniqueness) before storing
into allTools and toolDisplayNames. Make sure the fix is applied consistently in
the tool-building logic around the qualifiedName handling so each MCP tool keeps
its own execute function and display name.
---
Nitpick comments:
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts`:
- Around line 117-236: Add a collision-focused test in
sanitizeMcpToolNameForModel/getMcpTools coverage: the current
hyphen-vs-underscore case only proves distinct valid names stay distinct. Create
a test with two raw MCP tool names that sanitize to the same model-facing key
(for example punctuation vs underscore), and assert the intended
collision-handling behavior in getMcpTools, including the resulting tools map
and any disambiguation strategy referenced by mcpToolSets.ts.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f74ea55e-abc3-42fc-b22a-fb331728c6f4

📥 Commits

Reviewing files that changed from the base of the PR and between 9c4780d and a3c6855.

📒 Files selected for processing (13)
  • CHANGELOG.md
  • packages/web/src/ee/features/chat/agent.test.ts
  • packages/web/src/ee/features/chat/agent.ts
  • packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx
  • packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
  • packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.test.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/toolSearchToolComponent.tsx
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.ts
  • packages/web/src/ee/features/chat/mcpServerIconContext.tsx
  • packages/web/src/features/chat/types.ts

Comment threadCHANGELOG.md
- Upgraded `nodemailer` to `^9.0.1`. [#1356](https://github.com/sourcebot-dev/sourcebot/pull/1356)
- Upgraded `@opentelemetry/core` to `^2.8.0`. [#1413](https://github.com/sourcebot-dev/sourcebot/pull/1413)
- [EE] Fixed connector setup dialogs to add scrolling when connector setup content goes out of view.
- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Missing PR link in CHANGELOG entry.

Unlike the surrounding entries, this new line has no trailing [#<id>](url) link. As per coding guidelines, "Update CHANGELOG.md with an entry under [Unreleased] linking to the new PR" and "CHANGELOG.md entries must follow the format: single sentence description followed by a link in the format [#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>)."

📝 Proposed fix
-- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.+- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI. [`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.[`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
🤖 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 `@CHANGELOG.md` at line 35, The new CHANGELOG.md entry under [Unreleased] is
missing the required pull request link. Update the entry so the single-sentence
description is followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.

Source: Coding guidelines

Comment on lines +118 to +127
/**
* Provider APIs such as OpenAI Responses enforce tight charset and length
* limits for tool names. MCP tool names are server-controlled, so normalize the
* fully qualified name before exposing it to the model.
*/
export function sanitizeMcpToolNameForModel(name: string): string {
const sanitized = name.replace(/[^A-Za-z0-9_-]/g, '_') || '_';
return sanitized.slice(0, MODEL_TOOL_NAME_MAX_LENGTH);
}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Sanitized tool names are not collision-free.

sanitizeMcpToolNameForModel replaces every disallowed character with _, so two distinct raw tool names can map to the same sanitized key — e.g. catalog.query and catalog_query both become catalog_query. Truncation to 64 chars can also collide names sharing a long common prefix. Since qualifiedName is used to key both allTools (Line 330) and toolDisplayNames (Line 277), a collision silently overwrites the earlier tool's execute function and permission wiring with the later one's — the earlier tool becomes unreachable and its display name is lost, with no error or log.

Given MCP tool names are server-controlled and can contain arbitrary punctuation, this isn't a purely theoretical edge case.

🛠️ Proposed fix: detect and disambiguate collisions at the call site
 const originalExecute = tool.execute;
const rawQualifiedName = `${prefix}__${toolName}`;
- const qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ let qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ if (qualifiedName in allTools) {+ let suffix = 1;+ let candidate = qualifiedName;+ do {+ const suffixStr = `_${suffix++}`;+ candidate = `${qualifiedName.slice(0, MODEL_TOOL_NAME_MAX_LENGTH - suffixStr.length)}${suffixStr}`;+ } while (candidate in allTools);+ qualifiedName = candidate;+ }
const timeoutMs = env.SOURCEBOT_MCP_TOOL_CALL_TIMEOUT_MS;
toolDisplayNames[qualifiedName] = toolName;

Also applies to: 274-277

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts` around lines 118 - 127,
Sanitized MCP tool names can collide, causing later entries to overwrite earlier
ones in both allTools and toolDisplayNames. Update the MCP tool registration
flow in mcpToolSets.ts where qualifiedName is passed through
sanitizeMcpToolNameForModel so collisions are detected and handled instead of
silently overwriting; if two raw names sanitize to the same key, disambiguate
the generated key (or otherwise preserve uniqueness) before storing into
allTools and toolDisplayNames. Make sure the fix is applied consistently in the
tool-building logic around the qualifiedName handling so each MCP tool keeps its
own execute function and display name.

@@ -8,3 +8,10 @@ export type McpServerIconMap = Record<string, string>;
export const McpServerIconContext = createContext<McpServerIconMap>({});

export const useMcpServerIconMap = () => useContext(McpServerIconContext);

// Maps provider-safe model tool names back to raw MCP tool names for display.
export type McpToolNameMap = Record<string, string>;

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.

nit: consider moving this into a separate file

brendan-kellam
brendan-kellam previously approved these changes Jul 6, 2026

@brendan-kellambrendan-kellam 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.

lgtm

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0ccbb4b. Configure here.

Comment threadpackages/web/src/ee/features/chat/mcp/mcpToolSets.ts
@jsourcebot
jsourcebot merged commit d9d252b into mainJul 10, 2026
10 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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.

2 participants

@jsourcebot@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Jminnetian/sanitize tool names - #1423

Merged
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names
Jul 10, 2026
Merged

Jminnetian/sanitize tool names#1423
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names

Conversation

@jsourcebot

@jsourcebotjsourcebot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

MCP server tool names with certain characters (e.g " . ") would break the tool names open AI was expecting with their models. We now sanitize the names to avoid running into this issue.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed Ask connector MCP tools failing when provider-facing names contain unsupported characters.
    • MCP tool names are now normalized for reliable execution while preserving their original names in the chat interface.
    • Updated tool approvals, requests, search results, and status messages to show clearer server and tool names.
  • Tests
    • Added coverage for name normalization, display-name mapping, tool execution, and UI rendering.

Note

Low Risk
Targeted EE Ask/MCP naming and display path with tests; execution still uses raw MCP names, so risk is limited to display/metadata and model-facing identifiers.

Overview
Ask connector MCP tools whose names include characters providers reject (e.g. dots in catalog.query-catalog-entities) were failing at the model layer. This change normalizes fully qualified tool keys exposed to the LLM via sanitizeMcpToolNameForModel (invalid chars → _, max 64 chars) while MCP execution and persistence still use the server’s raw tool name.

The agent now streams data-mcp-tool parts mapping modelToolNamerawToolName. The chat thread hydrates that into McpToolNameContext (alongside server favicons in the renamed mcpDisplayMetadataContext). Tool cards, approval banners, and connector tool search use getMcpToolDisplayParts so users see names like backstage: catalog.query-catalog-entities instead of the sanitized model identifier; older threads without metadata keep the previous parse-based fallback.

Tests cover sanitization, getMcpTools behavior, agent streaming, and UI rendering.

Reviewed by Cursor Bugbot for commit 2edd846. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

MCP tool names are sanitized for provider compatibility, while raw names are propagated through streamed metadata and restored in chat tool, approval, and search displays.

Changes

MCP tool name sanitization and display mapping

Layer / File(s)Summary
Sanitization and raw-name mapping
packages/web/src/ee/features/chat/mcp/mcpToolSets.ts, packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts, CHANGELOG.md
Sanitizes model-facing MCP tool names, preserves raw names in toolDisplayNames, updates the result shape, and tests replacement, truncation, and mapping behavior.
Agent discovery and streamed metadata
packages/web/src/features/chat/types.ts, packages/web/src/ee/features/chat/agent.ts, packages/web/src/ee/features/chat/agent.test.ts
Adds the MCP tool data-part contract, emits model/raw name pairs during MCP discovery, and verifies the streamed payload.
Chat thread mapping state
packages/web/src/ee/features/chat/mcpDisplayMetadataContext.tsx, packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx, packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
Stores mappings from initial and streamed messages, provides them through context, and excludes mapping parts from direct step rendering.
Tool display rendering
packages/web/src/ee/features/chat/components/chatThread/tools/*, packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
Uses mapped raw names in MCP tool, approval, and tool-search displays, with fallback formatting and rendering tests.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
participant MCPToolSets
participant createAgentStream
participant ChatThread
participant ToolComponents
MCPToolSets->>createAgentStream: expose sanitized tool names and raw-name mappings
createAgentStream->>ChatThread: emit data-mcp-tool model/raw pairs
ChatThread->>ToolComponents: provide McpToolNameContext
ToolComponents->>ToolComponents: render resolved display names
Loading

Suggested reviewers:msukkari

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title matches the core change: sanitizing tool names, though the prefix adds unnecessary noise.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jminnetian/sanitize-tool-names

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts (1)

117-236: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a test for punctuation-collision sanitization.

The new "keeps valid hyphenated tool names distinct from underscored names" test (Lines 220-235) only exercises a non-colliding case (- is preserved). Consider adding a test with two raw tool names that both sanitize to the same string (e.g. foo.bar and foo_bar) to lock in the intended collision-handling behavior once it's implemented — see the related comment in mcpToolSets.ts.

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts` around lines 117 -
236, Add a collision-focused test in sanitizeMcpToolNameForModel/getMcpTools
coverage: the current hyphen-vs-underscore case only proves distinct valid names
stay distinct. Create a test with two raw MCP tool names that sanitize to the
same model-facing key (for example punctuation vs underscore), and assert the
intended collision-handling behavior in getMcpTools, including the resulting
tools map and any disambiguation strategy referenced by mcpToolSets.ts.
🤖 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 `@CHANGELOG.md`:
- Line 35: The new CHANGELOG.md entry under [Unreleased] is missing the required
pull request link. Update the entry so the single-sentence description is
followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts`:
- Around line 118-127: Sanitized MCP tool names can collide, causing later
entries to overwrite earlier ones in both allTools and toolDisplayNames. Update
the MCP tool registration flow in mcpToolSets.ts where qualifiedName is passed
through sanitizeMcpToolNameForModel so collisions are detected and handled
instead of silently overwriting; if two raw names sanitize to the same key,
disambiguate the generated key (or otherwise preserve uniqueness) before storing
into allTools and toolDisplayNames. Make sure the fix is applied consistently in
the tool-building logic around the qualifiedName handling so each MCP tool keeps
its own execute function and display name.
---
Nitpick comments:
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts`:
- Around line 117-236: Add a collision-focused test in
sanitizeMcpToolNameForModel/getMcpTools coverage: the current
hyphen-vs-underscore case only proves distinct valid names stay distinct. Create
a test with two raw MCP tool names that sanitize to the same model-facing key
(for example punctuation vs underscore), and assert the intended
collision-handling behavior in getMcpTools, including the resulting tools map
and any disambiguation strategy referenced by mcpToolSets.ts.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f74ea55e-abc3-42fc-b22a-fb331728c6f4

📥 Commits

Reviewing files that changed from the base of the PR and between 9c4780d and a3c6855.

📒 Files selected for processing (13)
  • CHANGELOG.md
  • packages/web/src/ee/features/chat/agent.test.ts
  • packages/web/src/ee/features/chat/agent.ts
  • packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx
  • packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
  • packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.test.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/toolSearchToolComponent.tsx
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.ts
  • packages/web/src/ee/features/chat/mcpServerIconContext.tsx
  • packages/web/src/features/chat/types.ts

Comment threadCHANGELOG.md
- Upgraded `nodemailer` to `^9.0.1`. [#1356](https://github.com/sourcebot-dev/sourcebot/pull/1356)
- Upgraded `@opentelemetry/core` to `^2.8.0`. [#1413](https://github.com/sourcebot-dev/sourcebot/pull/1413)
- [EE] Fixed connector setup dialogs to add scrolling when connector setup content goes out of view.
- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Missing PR link in CHANGELOG entry.

Unlike the surrounding entries, this new line has no trailing [#<id>](url) link. As per coding guidelines, "Update CHANGELOG.md with an entry under [Unreleased] linking to the new PR" and "CHANGELOG.md entries must follow the format: single sentence description followed by a link in the format [#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>)."

📝 Proposed fix
-- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.+- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI. [`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.[`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
🤖 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 `@CHANGELOG.md` at line 35, The new CHANGELOG.md entry under [Unreleased] is
missing the required pull request link. Update the entry so the single-sentence
description is followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.

Source: Coding guidelines

Comment on lines +118 to +127
/**
* Provider APIs such as OpenAI Responses enforce tight charset and length
* limits for tool names. MCP tool names are server-controlled, so normalize the
* fully qualified name before exposing it to the model.
*/
export function sanitizeMcpToolNameForModel(name: string): string {
const sanitized = name.replace(/[^A-Za-z0-9_-]/g, '_') || '_';
return sanitized.slice(0, MODEL_TOOL_NAME_MAX_LENGTH);
}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Sanitized tool names are not collision-free.

sanitizeMcpToolNameForModel replaces every disallowed character with _, so two distinct raw tool names can map to the same sanitized key — e.g. catalog.query and catalog_query both become catalog_query. Truncation to 64 chars can also collide names sharing a long common prefix. Since qualifiedName is used to key both allTools (Line 330) and toolDisplayNames (Line 277), a collision silently overwrites the earlier tool's execute function and permission wiring with the later one's — the earlier tool becomes unreachable and its display name is lost, with no error or log.

Given MCP tool names are server-controlled and can contain arbitrary punctuation, this isn't a purely theoretical edge case.

🛠️ Proposed fix: detect and disambiguate collisions at the call site
 const originalExecute = tool.execute;
const rawQualifiedName = `${prefix}__${toolName}`;
- const qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ let qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ if (qualifiedName in allTools) {+ let suffix = 1;+ let candidate = qualifiedName;+ do {+ const suffixStr = `_${suffix++}`;+ candidate = `${qualifiedName.slice(0, MODEL_TOOL_NAME_MAX_LENGTH - suffixStr.length)}${suffixStr}`;+ } while (candidate in allTools);+ qualifiedName = candidate;+ }
const timeoutMs = env.SOURCEBOT_MCP_TOOL_CALL_TIMEOUT_MS;
toolDisplayNames[qualifiedName] = toolName;

Also applies to: 274-277

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts` around lines 118 - 127,
Sanitized MCP tool names can collide, causing later entries to overwrite earlier
ones in both allTools and toolDisplayNames. Update the MCP tool registration
flow in mcpToolSets.ts where qualifiedName is passed through
sanitizeMcpToolNameForModel so collisions are detected and handled instead of
silently overwriting; if two raw names sanitize to the same key, disambiguate
the generated key (or otherwise preserve uniqueness) before storing into
allTools and toolDisplayNames. Make sure the fix is applied consistently in the
tool-building logic around the qualifiedName handling so each MCP tool keeps its
own execute function and display name.

@@ -8,3 +8,10 @@ export type McpServerIconMap = Record<string, string>;
export const McpServerIconContext = createContext<McpServerIconMap>({});

export const useMcpServerIconMap = () => useContext(McpServerIconContext);

// Maps provider-safe model tool names back to raw MCP tool names for display.
export type McpToolNameMap = Record<string, string>;

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.

nit: consider moving this into a separate file

brendan-kellam
brendan-kellam previously approved these changes Jul 6, 2026

@brendan-kellambrendan-kellam 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.

lgtm

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0ccbb4b. Configure here.

Comment threadpackages/web/src/ee/features/chat/mcp/mcpToolSets.ts
@jsourcebot
jsourcebot merged commit d9d252b into mainJul 10, 2026
10 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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.

2 participants

@jsourcebot@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Jminnetian/sanitize tool names - #1423

Merged
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names
Jul 10, 2026
Merged

Jminnetian/sanitize tool names#1423
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names

Conversation

@jsourcebot

@jsourcebotjsourcebot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

MCP server tool names with certain characters (e.g " . ") would break the tool names open AI was expecting with their models. We now sanitize the names to avoid running into this issue.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed Ask connector MCP tools failing when provider-facing names contain unsupported characters.
    • MCP tool names are now normalized for reliable execution while preserving their original names in the chat interface.
    • Updated tool approvals, requests, search results, and status messages to show clearer server and tool names.
  • Tests
    • Added coverage for name normalization, display-name mapping, tool execution, and UI rendering.

Note

Low Risk
Targeted EE Ask/MCP naming and display path with tests; execution still uses raw MCP names, so risk is limited to display/metadata and model-facing identifiers.

Overview
Ask connector MCP tools whose names include characters providers reject (e.g. dots in catalog.query-catalog-entities) were failing at the model layer. This change normalizes fully qualified tool keys exposed to the LLM via sanitizeMcpToolNameForModel (invalid chars → _, max 64 chars) while MCP execution and persistence still use the server’s raw tool name.

The agent now streams data-mcp-tool parts mapping modelToolNamerawToolName. The chat thread hydrates that into McpToolNameContext (alongside server favicons in the renamed mcpDisplayMetadataContext). Tool cards, approval banners, and connector tool search use getMcpToolDisplayParts so users see names like backstage: catalog.query-catalog-entities instead of the sanitized model identifier; older threads without metadata keep the previous parse-based fallback.

Tests cover sanitization, getMcpTools behavior, agent streaming, and UI rendering.

Reviewed by Cursor Bugbot for commit 2edd846. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

MCP tool names are sanitized for provider compatibility, while raw names are propagated through streamed metadata and restored in chat tool, approval, and search displays.

Changes

MCP tool name sanitization and display mapping

Layer / File(s)Summary
Sanitization and raw-name mapping
packages/web/src/ee/features/chat/mcp/mcpToolSets.ts, packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts, CHANGELOG.md
Sanitizes model-facing MCP tool names, preserves raw names in toolDisplayNames, updates the result shape, and tests replacement, truncation, and mapping behavior.
Agent discovery and streamed metadata
packages/web/src/features/chat/types.ts, packages/web/src/ee/features/chat/agent.ts, packages/web/src/ee/features/chat/agent.test.ts
Adds the MCP tool data-part contract, emits model/raw name pairs during MCP discovery, and verifies the streamed payload.
Chat thread mapping state
packages/web/src/ee/features/chat/mcpDisplayMetadataContext.tsx, packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx, packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
Stores mappings from initial and streamed messages, provides them through context, and excludes mapping parts from direct step rendering.
Tool display rendering
packages/web/src/ee/features/chat/components/chatThread/tools/*, packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
Uses mapped raw names in MCP tool, approval, and tool-search displays, with fallback formatting and rendering tests.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
participant MCPToolSets
participant createAgentStream
participant ChatThread
participant ToolComponents
MCPToolSets->>createAgentStream: expose sanitized tool names and raw-name mappings
createAgentStream->>ChatThread: emit data-mcp-tool model/raw pairs
ChatThread->>ToolComponents: provide McpToolNameContext
ToolComponents->>ToolComponents: render resolved display names
Loading

Suggested reviewers:msukkari

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title matches the core change: sanitizing tool names, though the prefix adds unnecessary noise.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jminnetian/sanitize-tool-names

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts (1)

117-236: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a test for punctuation-collision sanitization.

The new "keeps valid hyphenated tool names distinct from underscored names" test (Lines 220-235) only exercises a non-colliding case (- is preserved). Consider adding a test with two raw tool names that both sanitize to the same string (e.g. foo.bar and foo_bar) to lock in the intended collision-handling behavior once it's implemented — see the related comment in mcpToolSets.ts.

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts` around lines 117 -
236, Add a collision-focused test in sanitizeMcpToolNameForModel/getMcpTools
coverage: the current hyphen-vs-underscore case only proves distinct valid names
stay distinct. Create a test with two raw MCP tool names that sanitize to the
same model-facing key (for example punctuation vs underscore), and assert the
intended collision-handling behavior in getMcpTools, including the resulting
tools map and any disambiguation strategy referenced by mcpToolSets.ts.
🤖 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 `@CHANGELOG.md`:
- Line 35: The new CHANGELOG.md entry under [Unreleased] is missing the required
pull request link. Update the entry so the single-sentence description is
followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts`:
- Around line 118-127: Sanitized MCP tool names can collide, causing later
entries to overwrite earlier ones in both allTools and toolDisplayNames. Update
the MCP tool registration flow in mcpToolSets.ts where qualifiedName is passed
through sanitizeMcpToolNameForModel so collisions are detected and handled
instead of silently overwriting; if two raw names sanitize to the same key,
disambiguate the generated key (or otherwise preserve uniqueness) before storing
into allTools and toolDisplayNames. Make sure the fix is applied consistently in
the tool-building logic around the qualifiedName handling so each MCP tool keeps
its own execute function and display name.
---
Nitpick comments:
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts`:
- Around line 117-236: Add a collision-focused test in
sanitizeMcpToolNameForModel/getMcpTools coverage: the current
hyphen-vs-underscore case only proves distinct valid names stay distinct. Create
a test with two raw MCP tool names that sanitize to the same model-facing key
(for example punctuation vs underscore), and assert the intended
collision-handling behavior in getMcpTools, including the resulting tools map
and any disambiguation strategy referenced by mcpToolSets.ts.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f74ea55e-abc3-42fc-b22a-fb331728c6f4

📥 Commits

Reviewing files that changed from the base of the PR and between 9c4780d and a3c6855.

📒 Files selected for processing (13)
  • CHANGELOG.md
  • packages/web/src/ee/features/chat/agent.test.ts
  • packages/web/src/ee/features/chat/agent.ts
  • packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx
  • packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
  • packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.test.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/toolSearchToolComponent.tsx
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.ts
  • packages/web/src/ee/features/chat/mcpServerIconContext.tsx
  • packages/web/src/features/chat/types.ts

Comment threadCHANGELOG.md
- Upgraded `nodemailer` to `^9.0.1`. [#1356](https://github.com/sourcebot-dev/sourcebot/pull/1356)
- Upgraded `@opentelemetry/core` to `^2.8.0`. [#1413](https://github.com/sourcebot-dev/sourcebot/pull/1413)
- [EE] Fixed connector setup dialogs to add scrolling when connector setup content goes out of view.
- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Missing PR link in CHANGELOG entry.

Unlike the surrounding entries, this new line has no trailing [#<id>](url) link. As per coding guidelines, "Update CHANGELOG.md with an entry under [Unreleased] linking to the new PR" and "CHANGELOG.md entries must follow the format: single sentence description followed by a link in the format [#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>)."

📝 Proposed fix
-- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.+- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI. [`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.[`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
🤖 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 `@CHANGELOG.md` at line 35, The new CHANGELOG.md entry under [Unreleased] is
missing the required pull request link. Update the entry so the single-sentence
description is followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.

Source: Coding guidelines

Comment on lines +118 to +127
/**
* Provider APIs such as OpenAI Responses enforce tight charset and length
* limits for tool names. MCP tool names are server-controlled, so normalize the
* fully qualified name before exposing it to the model.
*/
export function sanitizeMcpToolNameForModel(name: string): string {
const sanitized = name.replace(/[^A-Za-z0-9_-]/g, '_') || '_';
return sanitized.slice(0, MODEL_TOOL_NAME_MAX_LENGTH);
}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Sanitized tool names are not collision-free.

sanitizeMcpToolNameForModel replaces every disallowed character with _, so two distinct raw tool names can map to the same sanitized key — e.g. catalog.query and catalog_query both become catalog_query. Truncation to 64 chars can also collide names sharing a long common prefix. Since qualifiedName is used to key both allTools (Line 330) and toolDisplayNames (Line 277), a collision silently overwrites the earlier tool's execute function and permission wiring with the later one's — the earlier tool becomes unreachable and its display name is lost, with no error or log.

Given MCP tool names are server-controlled and can contain arbitrary punctuation, this isn't a purely theoretical edge case.

🛠️ Proposed fix: detect and disambiguate collisions at the call site
 const originalExecute = tool.execute;
const rawQualifiedName = `${prefix}__${toolName}`;
- const qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ let qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ if (qualifiedName in allTools) {+ let suffix = 1;+ let candidate = qualifiedName;+ do {+ const suffixStr = `_${suffix++}`;+ candidate = `${qualifiedName.slice(0, MODEL_TOOL_NAME_MAX_LENGTH - suffixStr.length)}${suffixStr}`;+ } while (candidate in allTools);+ qualifiedName = candidate;+ }
const timeoutMs = env.SOURCEBOT_MCP_TOOL_CALL_TIMEOUT_MS;
toolDisplayNames[qualifiedName] = toolName;

Also applies to: 274-277

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts` around lines 118 - 127,
Sanitized MCP tool names can collide, causing later entries to overwrite earlier
ones in both allTools and toolDisplayNames. Update the MCP tool registration
flow in mcpToolSets.ts where qualifiedName is passed through
sanitizeMcpToolNameForModel so collisions are detected and handled instead of
silently overwriting; if two raw names sanitize to the same key, disambiguate
the generated key (or otherwise preserve uniqueness) before storing into
allTools and toolDisplayNames. Make sure the fix is applied consistently in the
tool-building logic around the qualifiedName handling so each MCP tool keeps its
own execute function and display name.

@@ -8,3 +8,10 @@ export type McpServerIconMap = Record<string, string>;
export const McpServerIconContext = createContext<McpServerIconMap>({});

export const useMcpServerIconMap = () => useContext(McpServerIconContext);

// Maps provider-safe model tool names back to raw MCP tool names for display.
export type McpToolNameMap = Record<string, string>;

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.

nit: consider moving this into a separate file

brendan-kellam
brendan-kellam previously approved these changes Jul 6, 2026

@brendan-kellambrendan-kellam 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.

lgtm

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0ccbb4b. Configure here.

Comment threadpackages/web/src/ee/features/chat/mcp/mcpToolSets.ts
@jsourcebot
jsourcebot merged commit d9d252b into mainJul 10, 2026
10 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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.

2 participants

@jsourcebot@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Jminnetian/sanitize tool names - #1423

Merged
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names
Jul 10, 2026
Merged

Jminnetian/sanitize tool names#1423
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names

Conversation

@jsourcebot

@jsourcebotjsourcebot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

MCP server tool names with certain characters (e.g " . ") would break the tool names open AI was expecting with their models. We now sanitize the names to avoid running into this issue.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed Ask connector MCP tools failing when provider-facing names contain unsupported characters.
    • MCP tool names are now normalized for reliable execution while preserving their original names in the chat interface.
    • Updated tool approvals, requests, search results, and status messages to show clearer server and tool names.
  • Tests
    • Added coverage for name normalization, display-name mapping, tool execution, and UI rendering.

Note

Low Risk
Targeted EE Ask/MCP naming and display path with tests; execution still uses raw MCP names, so risk is limited to display/metadata and model-facing identifiers.

Overview
Ask connector MCP tools whose names include characters providers reject (e.g. dots in catalog.query-catalog-entities) were failing at the model layer. This change normalizes fully qualified tool keys exposed to the LLM via sanitizeMcpToolNameForModel (invalid chars → _, max 64 chars) while MCP execution and persistence still use the server’s raw tool name.

The agent now streams data-mcp-tool parts mapping modelToolNamerawToolName. The chat thread hydrates that into McpToolNameContext (alongside server favicons in the renamed mcpDisplayMetadataContext). Tool cards, approval banners, and connector tool search use getMcpToolDisplayParts so users see names like backstage: catalog.query-catalog-entities instead of the sanitized model identifier; older threads without metadata keep the previous parse-based fallback.

Tests cover sanitization, getMcpTools behavior, agent streaming, and UI rendering.

Reviewed by Cursor Bugbot for commit 2edd846. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

MCP tool names are sanitized for provider compatibility, while raw names are propagated through streamed metadata and restored in chat tool, approval, and search displays.

Changes

MCP tool name sanitization and display mapping

Layer / File(s)Summary
Sanitization and raw-name mapping
packages/web/src/ee/features/chat/mcp/mcpToolSets.ts, packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts, CHANGELOG.md
Sanitizes model-facing MCP tool names, preserves raw names in toolDisplayNames, updates the result shape, and tests replacement, truncation, and mapping behavior.
Agent discovery and streamed metadata
packages/web/src/features/chat/types.ts, packages/web/src/ee/features/chat/agent.ts, packages/web/src/ee/features/chat/agent.test.ts
Adds the MCP tool data-part contract, emits model/raw name pairs during MCP discovery, and verifies the streamed payload.
Chat thread mapping state
packages/web/src/ee/features/chat/mcpDisplayMetadataContext.tsx, packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx, packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
Stores mappings from initial and streamed messages, provides them through context, and excludes mapping parts from direct step rendering.
Tool display rendering
packages/web/src/ee/features/chat/components/chatThread/tools/*, packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
Uses mapped raw names in MCP tool, approval, and tool-search displays, with fallback formatting and rendering tests.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
participant MCPToolSets
participant createAgentStream
participant ChatThread
participant ToolComponents
MCPToolSets->>createAgentStream: expose sanitized tool names and raw-name mappings
createAgentStream->>ChatThread: emit data-mcp-tool model/raw pairs
ChatThread->>ToolComponents: provide McpToolNameContext
ToolComponents->>ToolComponents: render resolved display names
Loading

Suggested reviewers:msukkari

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title matches the core change: sanitizing tool names, though the prefix adds unnecessary noise.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jminnetian/sanitize-tool-names

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts (1)

117-236: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a test for punctuation-collision sanitization.

The new "keeps valid hyphenated tool names distinct from underscored names" test (Lines 220-235) only exercises a non-colliding case (- is preserved). Consider adding a test with two raw tool names that both sanitize to the same string (e.g. foo.bar and foo_bar) to lock in the intended collision-handling behavior once it's implemented — see the related comment in mcpToolSets.ts.

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts` around lines 117 -
236, Add a collision-focused test in sanitizeMcpToolNameForModel/getMcpTools
coverage: the current hyphen-vs-underscore case only proves distinct valid names
stay distinct. Create a test with two raw MCP tool names that sanitize to the
same model-facing key (for example punctuation vs underscore), and assert the
intended collision-handling behavior in getMcpTools, including the resulting
tools map and any disambiguation strategy referenced by mcpToolSets.ts.
🤖 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 `@CHANGELOG.md`:
- Line 35: The new CHANGELOG.md entry under [Unreleased] is missing the required
pull request link. Update the entry so the single-sentence description is
followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts`:
- Around line 118-127: Sanitized MCP tool names can collide, causing later
entries to overwrite earlier ones in both allTools and toolDisplayNames. Update
the MCP tool registration flow in mcpToolSets.ts where qualifiedName is passed
through sanitizeMcpToolNameForModel so collisions are detected and handled
instead of silently overwriting; if two raw names sanitize to the same key,
disambiguate the generated key (or otherwise preserve uniqueness) before storing
into allTools and toolDisplayNames. Make sure the fix is applied consistently in
the tool-building logic around the qualifiedName handling so each MCP tool keeps
its own execute function and display name.
---
Nitpick comments:
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts`:
- Around line 117-236: Add a collision-focused test in
sanitizeMcpToolNameForModel/getMcpTools coverage: the current
hyphen-vs-underscore case only proves distinct valid names stay distinct. Create
a test with two raw MCP tool names that sanitize to the same model-facing key
(for example punctuation vs underscore), and assert the intended
collision-handling behavior in getMcpTools, including the resulting tools map
and any disambiguation strategy referenced by mcpToolSets.ts.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f74ea55e-abc3-42fc-b22a-fb331728c6f4

📥 Commits

Reviewing files that changed from the base of the PR and between 9c4780d and a3c6855.

📒 Files selected for processing (13)
  • CHANGELOG.md
  • packages/web/src/ee/features/chat/agent.test.ts
  • packages/web/src/ee/features/chat/agent.ts
  • packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx
  • packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
  • packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.test.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/toolSearchToolComponent.tsx
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.ts
  • packages/web/src/ee/features/chat/mcpServerIconContext.tsx
  • packages/web/src/features/chat/types.ts

Comment threadCHANGELOG.md
- Upgraded `nodemailer` to `^9.0.1`. [#1356](https://github.com/sourcebot-dev/sourcebot/pull/1356)
- Upgraded `@opentelemetry/core` to `^2.8.0`. [#1413](https://github.com/sourcebot-dev/sourcebot/pull/1413)
- [EE] Fixed connector setup dialogs to add scrolling when connector setup content goes out of view.
- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Missing PR link in CHANGELOG entry.

Unlike the surrounding entries, this new line has no trailing [#<id>](url) link. As per coding guidelines, "Update CHANGELOG.md with an entry under [Unreleased] linking to the new PR" and "CHANGELOG.md entries must follow the format: single sentence description followed by a link in the format [#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>)."

📝 Proposed fix
-- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.+- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI. [`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.[`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
🤖 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 `@CHANGELOG.md` at line 35, The new CHANGELOG.md entry under [Unreleased] is
missing the required pull request link. Update the entry so the single-sentence
description is followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.

Source: Coding guidelines

Comment on lines +118 to +127
/**
* Provider APIs such as OpenAI Responses enforce tight charset and length
* limits for tool names. MCP tool names are server-controlled, so normalize the
* fully qualified name before exposing it to the model.
*/
export function sanitizeMcpToolNameForModel(name: string): string {
const sanitized = name.replace(/[^A-Za-z0-9_-]/g, '_') || '_';
return sanitized.slice(0, MODEL_TOOL_NAME_MAX_LENGTH);
}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Sanitized tool names are not collision-free.

sanitizeMcpToolNameForModel replaces every disallowed character with _, so two distinct raw tool names can map to the same sanitized key — e.g. catalog.query and catalog_query both become catalog_query. Truncation to 64 chars can also collide names sharing a long common prefix. Since qualifiedName is used to key both allTools (Line 330) and toolDisplayNames (Line 277), a collision silently overwrites the earlier tool's execute function and permission wiring with the later one's — the earlier tool becomes unreachable and its display name is lost, with no error or log.

Given MCP tool names are server-controlled and can contain arbitrary punctuation, this isn't a purely theoretical edge case.

🛠️ Proposed fix: detect and disambiguate collisions at the call site
 const originalExecute = tool.execute;
const rawQualifiedName = `${prefix}__${toolName}`;
- const qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ let qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ if (qualifiedName in allTools) {+ let suffix = 1;+ let candidate = qualifiedName;+ do {+ const suffixStr = `_${suffix++}`;+ candidate = `${qualifiedName.slice(0, MODEL_TOOL_NAME_MAX_LENGTH - suffixStr.length)}${suffixStr}`;+ } while (candidate in allTools);+ qualifiedName = candidate;+ }
const timeoutMs = env.SOURCEBOT_MCP_TOOL_CALL_TIMEOUT_MS;
toolDisplayNames[qualifiedName] = toolName;

Also applies to: 274-277

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts` around lines 118 - 127,
Sanitized MCP tool names can collide, causing later entries to overwrite earlier
ones in both allTools and toolDisplayNames. Update the MCP tool registration
flow in mcpToolSets.ts where qualifiedName is passed through
sanitizeMcpToolNameForModel so collisions are detected and handled instead of
silently overwriting; if two raw names sanitize to the same key, disambiguate
the generated key (or otherwise preserve uniqueness) before storing into
allTools and toolDisplayNames. Make sure the fix is applied consistently in the
tool-building logic around the qualifiedName handling so each MCP tool keeps its
own execute function and display name.

@@ -8,3 +8,10 @@ export type McpServerIconMap = Record<string, string>;
export const McpServerIconContext = createContext<McpServerIconMap>({});

export const useMcpServerIconMap = () => useContext(McpServerIconContext);

// Maps provider-safe model tool names back to raw MCP tool names for display.
export type McpToolNameMap = Record<string, string>;

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.

nit: consider moving this into a separate file

brendan-kellam
brendan-kellam previously approved these changes Jul 6, 2026

@brendan-kellambrendan-kellam 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.

lgtm

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0ccbb4b. Configure here.

Comment threadpackages/web/src/ee/features/chat/mcp/mcpToolSets.ts
@jsourcebot
jsourcebot merged commit d9d252b into mainJul 10, 2026
10 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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.

2 participants

@jsourcebot@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Jminnetian/sanitize tool names - #1423

Merged
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names
Jul 10, 2026
Merged

Jminnetian/sanitize tool names#1423
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names

Conversation

@jsourcebot

@jsourcebotjsourcebot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

MCP server tool names with certain characters (e.g " . ") would break the tool names open AI was expecting with their models. We now sanitize the names to avoid running into this issue.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed Ask connector MCP tools failing when provider-facing names contain unsupported characters.
    • MCP tool names are now normalized for reliable execution while preserving their original names in the chat interface.
    • Updated tool approvals, requests, search results, and status messages to show clearer server and tool names.
  • Tests
    • Added coverage for name normalization, display-name mapping, tool execution, and UI rendering.

Note

Low Risk
Targeted EE Ask/MCP naming and display path with tests; execution still uses raw MCP names, so risk is limited to display/metadata and model-facing identifiers.

Overview
Ask connector MCP tools whose names include characters providers reject (e.g. dots in catalog.query-catalog-entities) were failing at the model layer. This change normalizes fully qualified tool keys exposed to the LLM via sanitizeMcpToolNameForModel (invalid chars → _, max 64 chars) while MCP execution and persistence still use the server’s raw tool name.

The agent now streams data-mcp-tool parts mapping modelToolNamerawToolName. The chat thread hydrates that into McpToolNameContext (alongside server favicons in the renamed mcpDisplayMetadataContext). Tool cards, approval banners, and connector tool search use getMcpToolDisplayParts so users see names like backstage: catalog.query-catalog-entities instead of the sanitized model identifier; older threads without metadata keep the previous parse-based fallback.

Tests cover sanitization, getMcpTools behavior, agent streaming, and UI rendering.

Reviewed by Cursor Bugbot for commit 2edd846. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

MCP tool names are sanitized for provider compatibility, while raw names are propagated through streamed metadata and restored in chat tool, approval, and search displays.

Changes

MCP tool name sanitization and display mapping

Layer / File(s)Summary
Sanitization and raw-name mapping
packages/web/src/ee/features/chat/mcp/mcpToolSets.ts, packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts, CHANGELOG.md
Sanitizes model-facing MCP tool names, preserves raw names in toolDisplayNames, updates the result shape, and tests replacement, truncation, and mapping behavior.
Agent discovery and streamed metadata
packages/web/src/features/chat/types.ts, packages/web/src/ee/features/chat/agent.ts, packages/web/src/ee/features/chat/agent.test.ts
Adds the MCP tool data-part contract, emits model/raw name pairs during MCP discovery, and verifies the streamed payload.
Chat thread mapping state
packages/web/src/ee/features/chat/mcpDisplayMetadataContext.tsx, packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx, packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
Stores mappings from initial and streamed messages, provides them through context, and excludes mapping parts from direct step rendering.
Tool display rendering
packages/web/src/ee/features/chat/components/chatThread/tools/*, packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
Uses mapped raw names in MCP tool, approval, and tool-search displays, with fallback formatting and rendering tests.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
participant MCPToolSets
participant createAgentStream
participant ChatThread
participant ToolComponents
MCPToolSets->>createAgentStream: expose sanitized tool names and raw-name mappings
createAgentStream->>ChatThread: emit data-mcp-tool model/raw pairs
ChatThread->>ToolComponents: provide McpToolNameContext
ToolComponents->>ToolComponents: render resolved display names
Loading

Suggested reviewers:msukkari

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title matches the core change: sanitizing tool names, though the prefix adds unnecessary noise.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jminnetian/sanitize-tool-names

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts (1)

117-236: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a test for punctuation-collision sanitization.

The new "keeps valid hyphenated tool names distinct from underscored names" test (Lines 220-235) only exercises a non-colliding case (- is preserved). Consider adding a test with two raw tool names that both sanitize to the same string (e.g. foo.bar and foo_bar) to lock in the intended collision-handling behavior once it's implemented — see the related comment in mcpToolSets.ts.

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts` around lines 117 -
236, Add a collision-focused test in sanitizeMcpToolNameForModel/getMcpTools
coverage: the current hyphen-vs-underscore case only proves distinct valid names
stay distinct. Create a test with two raw MCP tool names that sanitize to the
same model-facing key (for example punctuation vs underscore), and assert the
intended collision-handling behavior in getMcpTools, including the resulting
tools map and any disambiguation strategy referenced by mcpToolSets.ts.
🤖 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 `@CHANGELOG.md`:
- Line 35: The new CHANGELOG.md entry under [Unreleased] is missing the required
pull request link. Update the entry so the single-sentence description is
followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts`:
- Around line 118-127: Sanitized MCP tool names can collide, causing later
entries to overwrite earlier ones in both allTools and toolDisplayNames. Update
the MCP tool registration flow in mcpToolSets.ts where qualifiedName is passed
through sanitizeMcpToolNameForModel so collisions are detected and handled
instead of silently overwriting; if two raw names sanitize to the same key,
disambiguate the generated key (or otherwise preserve uniqueness) before storing
into allTools and toolDisplayNames. Make sure the fix is applied consistently in
the tool-building logic around the qualifiedName handling so each MCP tool keeps
its own execute function and display name.
---
Nitpick comments:
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts`:
- Around line 117-236: Add a collision-focused test in
sanitizeMcpToolNameForModel/getMcpTools coverage: the current
hyphen-vs-underscore case only proves distinct valid names stay distinct. Create
a test with two raw MCP tool names that sanitize to the same model-facing key
(for example punctuation vs underscore), and assert the intended
collision-handling behavior in getMcpTools, including the resulting tools map
and any disambiguation strategy referenced by mcpToolSets.ts.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f74ea55e-abc3-42fc-b22a-fb331728c6f4

📥 Commits

Reviewing files that changed from the base of the PR and between 9c4780d and a3c6855.

📒 Files selected for processing (13)
  • CHANGELOG.md
  • packages/web/src/ee/features/chat/agent.test.ts
  • packages/web/src/ee/features/chat/agent.ts
  • packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx
  • packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
  • packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.test.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/toolSearchToolComponent.tsx
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.ts
  • packages/web/src/ee/features/chat/mcpServerIconContext.tsx
  • packages/web/src/features/chat/types.ts

Comment threadCHANGELOG.md
- Upgraded `nodemailer` to `^9.0.1`. [#1356](https://github.com/sourcebot-dev/sourcebot/pull/1356)
- Upgraded `@opentelemetry/core` to `^2.8.0`. [#1413](https://github.com/sourcebot-dev/sourcebot/pull/1413)
- [EE] Fixed connector setup dialogs to add scrolling when connector setup content goes out of view.
- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Missing PR link in CHANGELOG entry.

Unlike the surrounding entries, this new line has no trailing [#<id>](url) link. As per coding guidelines, "Update CHANGELOG.md with an entry under [Unreleased] linking to the new PR" and "CHANGELOG.md entries must follow the format: single sentence description followed by a link in the format [#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>)."

📝 Proposed fix
-- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.+- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI. [`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.[`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
🤖 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 `@CHANGELOG.md` at line 35, The new CHANGELOG.md entry under [Unreleased] is
missing the required pull request link. Update the entry so the single-sentence
description is followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.

Source: Coding guidelines

Comment on lines +118 to +127
/**
* Provider APIs such as OpenAI Responses enforce tight charset and length
* limits for tool names. MCP tool names are server-controlled, so normalize the
* fully qualified name before exposing it to the model.
*/
export function sanitizeMcpToolNameForModel(name: string): string {
const sanitized = name.replace(/[^A-Za-z0-9_-]/g, '_') || '_';
return sanitized.slice(0, MODEL_TOOL_NAME_MAX_LENGTH);
}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Sanitized tool names are not collision-free.

sanitizeMcpToolNameForModel replaces every disallowed character with _, so two distinct raw tool names can map to the same sanitized key — e.g. catalog.query and catalog_query both become catalog_query. Truncation to 64 chars can also collide names sharing a long common prefix. Since qualifiedName is used to key both allTools (Line 330) and toolDisplayNames (Line 277), a collision silently overwrites the earlier tool's execute function and permission wiring with the later one's — the earlier tool becomes unreachable and its display name is lost, with no error or log.

Given MCP tool names are server-controlled and can contain arbitrary punctuation, this isn't a purely theoretical edge case.

🛠️ Proposed fix: detect and disambiguate collisions at the call site
 const originalExecute = tool.execute;
const rawQualifiedName = `${prefix}__${toolName}`;
- const qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ let qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ if (qualifiedName in allTools) {+ let suffix = 1;+ let candidate = qualifiedName;+ do {+ const suffixStr = `_${suffix++}`;+ candidate = `${qualifiedName.slice(0, MODEL_TOOL_NAME_MAX_LENGTH - suffixStr.length)}${suffixStr}`;+ } while (candidate in allTools);+ qualifiedName = candidate;+ }
const timeoutMs = env.SOURCEBOT_MCP_TOOL_CALL_TIMEOUT_MS;
toolDisplayNames[qualifiedName] = toolName;

Also applies to: 274-277

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts` around lines 118 - 127,
Sanitized MCP tool names can collide, causing later entries to overwrite earlier
ones in both allTools and toolDisplayNames. Update the MCP tool registration
flow in mcpToolSets.ts where qualifiedName is passed through
sanitizeMcpToolNameForModel so collisions are detected and handled instead of
silently overwriting; if two raw names sanitize to the same key, disambiguate
the generated key (or otherwise preserve uniqueness) before storing into
allTools and toolDisplayNames. Make sure the fix is applied consistently in the
tool-building logic around the qualifiedName handling so each MCP tool keeps its
own execute function and display name.

@@ -8,3 +8,10 @@ export type McpServerIconMap = Record<string, string>;
export const McpServerIconContext = createContext<McpServerIconMap>({});

export const useMcpServerIconMap = () => useContext(McpServerIconContext);

// Maps provider-safe model tool names back to raw MCP tool names for display.
export type McpToolNameMap = Record<string, string>;

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.

nit: consider moving this into a separate file

brendan-kellam
brendan-kellam previously approved these changes Jul 6, 2026

@brendan-kellambrendan-kellam 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.

lgtm

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0ccbb4b. Configure here.

Comment threadpackages/web/src/ee/features/chat/mcp/mcpToolSets.ts
@jsourcebot
jsourcebot merged commit d9d252b into mainJul 10, 2026
10 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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.

2 participants

@jsourcebot@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Jminnetian/sanitize tool names - #1423

Merged
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names
Jul 10, 2026
Merged

Jminnetian/sanitize tool names#1423
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names

Conversation

@jsourcebot

@jsourcebotjsourcebot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

MCP server tool names with certain characters (e.g " . ") would break the tool names open AI was expecting with their models. We now sanitize the names to avoid running into this issue.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed Ask connector MCP tools failing when provider-facing names contain unsupported characters.
    • MCP tool names are now normalized for reliable execution while preserving their original names in the chat interface.
    • Updated tool approvals, requests, search results, and status messages to show clearer server and tool names.
  • Tests
    • Added coverage for name normalization, display-name mapping, tool execution, and UI rendering.

Note

Low Risk
Targeted EE Ask/MCP naming and display path with tests; execution still uses raw MCP names, so risk is limited to display/metadata and model-facing identifiers.

Overview
Ask connector MCP tools whose names include characters providers reject (e.g. dots in catalog.query-catalog-entities) were failing at the model layer. This change normalizes fully qualified tool keys exposed to the LLM via sanitizeMcpToolNameForModel (invalid chars → _, max 64 chars) while MCP execution and persistence still use the server’s raw tool name.

The agent now streams data-mcp-tool parts mapping modelToolNamerawToolName. The chat thread hydrates that into McpToolNameContext (alongside server favicons in the renamed mcpDisplayMetadataContext). Tool cards, approval banners, and connector tool search use getMcpToolDisplayParts so users see names like backstage: catalog.query-catalog-entities instead of the sanitized model identifier; older threads without metadata keep the previous parse-based fallback.

Tests cover sanitization, getMcpTools behavior, agent streaming, and UI rendering.

Reviewed by Cursor Bugbot for commit 2edd846. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

MCP tool names are sanitized for provider compatibility, while raw names are propagated through streamed metadata and restored in chat tool, approval, and search displays.

Changes

MCP tool name sanitization and display mapping

Layer / File(s)Summary
Sanitization and raw-name mapping
packages/web/src/ee/features/chat/mcp/mcpToolSets.ts, packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts, CHANGELOG.md
Sanitizes model-facing MCP tool names, preserves raw names in toolDisplayNames, updates the result shape, and tests replacement, truncation, and mapping behavior.
Agent discovery and streamed metadata
packages/web/src/features/chat/types.ts, packages/web/src/ee/features/chat/agent.ts, packages/web/src/ee/features/chat/agent.test.ts
Adds the MCP tool data-part contract, emits model/raw name pairs during MCP discovery, and verifies the streamed payload.
Chat thread mapping state
packages/web/src/ee/features/chat/mcpDisplayMetadataContext.tsx, packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx, packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
Stores mappings from initial and streamed messages, provides them through context, and excludes mapping parts from direct step rendering.
Tool display rendering
packages/web/src/ee/features/chat/components/chatThread/tools/*, packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
Uses mapped raw names in MCP tool, approval, and tool-search displays, with fallback formatting and rendering tests.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
participant MCPToolSets
participant createAgentStream
participant ChatThread
participant ToolComponents
MCPToolSets->>createAgentStream: expose sanitized tool names and raw-name mappings
createAgentStream->>ChatThread: emit data-mcp-tool model/raw pairs
ChatThread->>ToolComponents: provide McpToolNameContext
ToolComponents->>ToolComponents: render resolved display names
Loading

Suggested reviewers:msukkari

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title matches the core change: sanitizing tool names, though the prefix adds unnecessary noise.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jminnetian/sanitize-tool-names

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts (1)

117-236: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a test for punctuation-collision sanitization.

The new "keeps valid hyphenated tool names distinct from underscored names" test (Lines 220-235) only exercises a non-colliding case (- is preserved). Consider adding a test with two raw tool names that both sanitize to the same string (e.g. foo.bar and foo_bar) to lock in the intended collision-handling behavior once it's implemented — see the related comment in mcpToolSets.ts.

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts` around lines 117 -
236, Add a collision-focused test in sanitizeMcpToolNameForModel/getMcpTools
coverage: the current hyphen-vs-underscore case only proves distinct valid names
stay distinct. Create a test with two raw MCP tool names that sanitize to the
same model-facing key (for example punctuation vs underscore), and assert the
intended collision-handling behavior in getMcpTools, including the resulting
tools map and any disambiguation strategy referenced by mcpToolSets.ts.
🤖 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 `@CHANGELOG.md`:
- Line 35: The new CHANGELOG.md entry under [Unreleased] is missing the required
pull request link. Update the entry so the single-sentence description is
followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts`:
- Around line 118-127: Sanitized MCP tool names can collide, causing later
entries to overwrite earlier ones in both allTools and toolDisplayNames. Update
the MCP tool registration flow in mcpToolSets.ts where qualifiedName is passed
through sanitizeMcpToolNameForModel so collisions are detected and handled
instead of silently overwriting; if two raw names sanitize to the same key,
disambiguate the generated key (or otherwise preserve uniqueness) before storing
into allTools and toolDisplayNames. Make sure the fix is applied consistently in
the tool-building logic around the qualifiedName handling so each MCP tool keeps
its own execute function and display name.
---
Nitpick comments:
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts`:
- Around line 117-236: Add a collision-focused test in
sanitizeMcpToolNameForModel/getMcpTools coverage: the current
hyphen-vs-underscore case only proves distinct valid names stay distinct. Create
a test with two raw MCP tool names that sanitize to the same model-facing key
(for example punctuation vs underscore), and assert the intended
collision-handling behavior in getMcpTools, including the resulting tools map
and any disambiguation strategy referenced by mcpToolSets.ts.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f74ea55e-abc3-42fc-b22a-fb331728c6f4

📥 Commits

Reviewing files that changed from the base of the PR and between 9c4780d and a3c6855.

📒 Files selected for processing (13)
  • CHANGELOG.md
  • packages/web/src/ee/features/chat/agent.test.ts
  • packages/web/src/ee/features/chat/agent.ts
  • packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx
  • packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
  • packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.test.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/toolSearchToolComponent.tsx
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.ts
  • packages/web/src/ee/features/chat/mcpServerIconContext.tsx
  • packages/web/src/features/chat/types.ts

Comment threadCHANGELOG.md
- Upgraded `nodemailer` to `^9.0.1`. [#1356](https://github.com/sourcebot-dev/sourcebot/pull/1356)
- Upgraded `@opentelemetry/core` to `^2.8.0`. [#1413](https://github.com/sourcebot-dev/sourcebot/pull/1413)
- [EE] Fixed connector setup dialogs to add scrolling when connector setup content goes out of view.
- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Missing PR link in CHANGELOG entry.

Unlike the surrounding entries, this new line has no trailing [#<id>](url) link. As per coding guidelines, "Update CHANGELOG.md with an entry under [Unreleased] linking to the new PR" and "CHANGELOG.md entries must follow the format: single sentence description followed by a link in the format [#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>)."

📝 Proposed fix
-- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.+- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI. [`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.[`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
🤖 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 `@CHANGELOG.md` at line 35, The new CHANGELOG.md entry under [Unreleased] is
missing the required pull request link. Update the entry so the single-sentence
description is followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.

Source: Coding guidelines

Comment on lines +118 to +127
/**
* Provider APIs such as OpenAI Responses enforce tight charset and length
* limits for tool names. MCP tool names are server-controlled, so normalize the
* fully qualified name before exposing it to the model.
*/
export function sanitizeMcpToolNameForModel(name: string): string {
const sanitized = name.replace(/[^A-Za-z0-9_-]/g, '_') || '_';
return sanitized.slice(0, MODEL_TOOL_NAME_MAX_LENGTH);
}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Sanitized tool names are not collision-free.

sanitizeMcpToolNameForModel replaces every disallowed character with _, so two distinct raw tool names can map to the same sanitized key — e.g. catalog.query and catalog_query both become catalog_query. Truncation to 64 chars can also collide names sharing a long common prefix. Since qualifiedName is used to key both allTools (Line 330) and toolDisplayNames (Line 277), a collision silently overwrites the earlier tool's execute function and permission wiring with the later one's — the earlier tool becomes unreachable and its display name is lost, with no error or log.

Given MCP tool names are server-controlled and can contain arbitrary punctuation, this isn't a purely theoretical edge case.

🛠️ Proposed fix: detect and disambiguate collisions at the call site
 const originalExecute = tool.execute;
const rawQualifiedName = `${prefix}__${toolName}`;
- const qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ let qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ if (qualifiedName in allTools) {+ let suffix = 1;+ let candidate = qualifiedName;+ do {+ const suffixStr = `_${suffix++}`;+ candidate = `${qualifiedName.slice(0, MODEL_TOOL_NAME_MAX_LENGTH - suffixStr.length)}${suffixStr}`;+ } while (candidate in allTools);+ qualifiedName = candidate;+ }
const timeoutMs = env.SOURCEBOT_MCP_TOOL_CALL_TIMEOUT_MS;
toolDisplayNames[qualifiedName] = toolName;

Also applies to: 274-277

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts` around lines 118 - 127,
Sanitized MCP tool names can collide, causing later entries to overwrite earlier
ones in both allTools and toolDisplayNames. Update the MCP tool registration
flow in mcpToolSets.ts where qualifiedName is passed through
sanitizeMcpToolNameForModel so collisions are detected and handled instead of
silently overwriting; if two raw names sanitize to the same key, disambiguate
the generated key (or otherwise preserve uniqueness) before storing into
allTools and toolDisplayNames. Make sure the fix is applied consistently in the
tool-building logic around the qualifiedName handling so each MCP tool keeps its
own execute function and display name.

@@ -8,3 +8,10 @@ export type McpServerIconMap = Record<string, string>;
export const McpServerIconContext = createContext<McpServerIconMap>({});

export const useMcpServerIconMap = () => useContext(McpServerIconContext);

// Maps provider-safe model tool names back to raw MCP tool names for display.
export type McpToolNameMap = Record<string, string>;

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.

nit: consider moving this into a separate file

brendan-kellam
brendan-kellam previously approved these changes Jul 6, 2026

@brendan-kellambrendan-kellam 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.

lgtm

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0ccbb4b. Configure here.

Comment threadpackages/web/src/ee/features/chat/mcp/mcpToolSets.ts
@jsourcebot
jsourcebot merged commit d9d252b into mainJul 10, 2026
10 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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.

2 participants

@jsourcebot@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Jminnetian/sanitize tool names - #1423

Merged
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names
Jul 10, 2026
Merged

Jminnetian/sanitize tool names#1423
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names

Conversation

@jsourcebot

@jsourcebotjsourcebot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

MCP server tool names with certain characters (e.g " . ") would break the tool names open AI was expecting with their models. We now sanitize the names to avoid running into this issue.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed Ask connector MCP tools failing when provider-facing names contain unsupported characters.
    • MCP tool names are now normalized for reliable execution while preserving their original names in the chat interface.
    • Updated tool approvals, requests, search results, and status messages to show clearer server and tool names.
  • Tests
    • Added coverage for name normalization, display-name mapping, tool execution, and UI rendering.

Note

Low Risk
Targeted EE Ask/MCP naming and display path with tests; execution still uses raw MCP names, so risk is limited to display/metadata and model-facing identifiers.

Overview
Ask connector MCP tools whose names include characters providers reject (e.g. dots in catalog.query-catalog-entities) were failing at the model layer. This change normalizes fully qualified tool keys exposed to the LLM via sanitizeMcpToolNameForModel (invalid chars → _, max 64 chars) while MCP execution and persistence still use the server’s raw tool name.

The agent now streams data-mcp-tool parts mapping modelToolNamerawToolName. The chat thread hydrates that into McpToolNameContext (alongside server favicons in the renamed mcpDisplayMetadataContext). Tool cards, approval banners, and connector tool search use getMcpToolDisplayParts so users see names like backstage: catalog.query-catalog-entities instead of the sanitized model identifier; older threads without metadata keep the previous parse-based fallback.

Tests cover sanitization, getMcpTools behavior, agent streaming, and UI rendering.

Reviewed by Cursor Bugbot for commit 2edd846. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

MCP tool names are sanitized for provider compatibility, while raw names are propagated through streamed metadata and restored in chat tool, approval, and search displays.

Changes

MCP tool name sanitization and display mapping

Layer / File(s)Summary
Sanitization and raw-name mapping
packages/web/src/ee/features/chat/mcp/mcpToolSets.ts, packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts, CHANGELOG.md
Sanitizes model-facing MCP tool names, preserves raw names in toolDisplayNames, updates the result shape, and tests replacement, truncation, and mapping behavior.
Agent discovery and streamed metadata
packages/web/src/features/chat/types.ts, packages/web/src/ee/features/chat/agent.ts, packages/web/src/ee/features/chat/agent.test.ts
Adds the MCP tool data-part contract, emits model/raw name pairs during MCP discovery, and verifies the streamed payload.
Chat thread mapping state
packages/web/src/ee/features/chat/mcpDisplayMetadataContext.tsx, packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx, packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
Stores mappings from initial and streamed messages, provides them through context, and excludes mapping parts from direct step rendering.
Tool display rendering
packages/web/src/ee/features/chat/components/chatThread/tools/*, packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
Uses mapped raw names in MCP tool, approval, and tool-search displays, with fallback formatting and rendering tests.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
participant MCPToolSets
participant createAgentStream
participant ChatThread
participant ToolComponents
MCPToolSets->>createAgentStream: expose sanitized tool names and raw-name mappings
createAgentStream->>ChatThread: emit data-mcp-tool model/raw pairs
ChatThread->>ToolComponents: provide McpToolNameContext
ToolComponents->>ToolComponents: render resolved display names
Loading

Suggested reviewers:msukkari

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title matches the core change: sanitizing tool names, though the prefix adds unnecessary noise.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jminnetian/sanitize-tool-names

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts (1)

117-236: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a test for punctuation-collision sanitization.

The new "keeps valid hyphenated tool names distinct from underscored names" test (Lines 220-235) only exercises a non-colliding case (- is preserved). Consider adding a test with two raw tool names that both sanitize to the same string (e.g. foo.bar and foo_bar) to lock in the intended collision-handling behavior once it's implemented — see the related comment in mcpToolSets.ts.

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts` around lines 117 -
236, Add a collision-focused test in sanitizeMcpToolNameForModel/getMcpTools
coverage: the current hyphen-vs-underscore case only proves distinct valid names
stay distinct. Create a test with two raw MCP tool names that sanitize to the
same model-facing key (for example punctuation vs underscore), and assert the
intended collision-handling behavior in getMcpTools, including the resulting
tools map and any disambiguation strategy referenced by mcpToolSets.ts.
🤖 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 `@CHANGELOG.md`:
- Line 35: The new CHANGELOG.md entry under [Unreleased] is missing the required
pull request link. Update the entry so the single-sentence description is
followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts`:
- Around line 118-127: Sanitized MCP tool names can collide, causing later
entries to overwrite earlier ones in both allTools and toolDisplayNames. Update
the MCP tool registration flow in mcpToolSets.ts where qualifiedName is passed
through sanitizeMcpToolNameForModel so collisions are detected and handled
instead of silently overwriting; if two raw names sanitize to the same key,
disambiguate the generated key (or otherwise preserve uniqueness) before storing
into allTools and toolDisplayNames. Make sure the fix is applied consistently in
the tool-building logic around the qualifiedName handling so each MCP tool keeps
its own execute function and display name.
---
Nitpick comments:
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts`:
- Around line 117-236: Add a collision-focused test in
sanitizeMcpToolNameForModel/getMcpTools coverage: the current
hyphen-vs-underscore case only proves distinct valid names stay distinct. Create
a test with two raw MCP tool names that sanitize to the same model-facing key
(for example punctuation vs underscore), and assert the intended
collision-handling behavior in getMcpTools, including the resulting tools map
and any disambiguation strategy referenced by mcpToolSets.ts.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f74ea55e-abc3-42fc-b22a-fb331728c6f4

📥 Commits

Reviewing files that changed from the base of the PR and between 9c4780d and a3c6855.

📒 Files selected for processing (13)
  • CHANGELOG.md
  • packages/web/src/ee/features/chat/agent.test.ts
  • packages/web/src/ee/features/chat/agent.ts
  • packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx
  • packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
  • packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.test.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/toolSearchToolComponent.tsx
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.ts
  • packages/web/src/ee/features/chat/mcpServerIconContext.tsx
  • packages/web/src/features/chat/types.ts

Comment threadCHANGELOG.md
- Upgraded `nodemailer` to `^9.0.1`. [#1356](https://github.com/sourcebot-dev/sourcebot/pull/1356)
- Upgraded `@opentelemetry/core` to `^2.8.0`. [#1413](https://github.com/sourcebot-dev/sourcebot/pull/1413)
- [EE] Fixed connector setup dialogs to add scrolling when connector setup content goes out of view.
- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Missing PR link in CHANGELOG entry.

Unlike the surrounding entries, this new line has no trailing [#<id>](url) link. As per coding guidelines, "Update CHANGELOG.md with an entry under [Unreleased] linking to the new PR" and "CHANGELOG.md entries must follow the format: single sentence description followed by a link in the format [#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>)."

📝 Proposed fix
-- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.+- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI. [`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.[`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
🤖 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 `@CHANGELOG.md` at line 35, The new CHANGELOG.md entry under [Unreleased] is
missing the required pull request link. Update the entry so the single-sentence
description is followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.

Source: Coding guidelines

Comment on lines +118 to +127
/**
* Provider APIs such as OpenAI Responses enforce tight charset and length
* limits for tool names. MCP tool names are server-controlled, so normalize the
* fully qualified name before exposing it to the model.
*/
export function sanitizeMcpToolNameForModel(name: string): string {
const sanitized = name.replace(/[^A-Za-z0-9_-]/g, '_') || '_';
return sanitized.slice(0, MODEL_TOOL_NAME_MAX_LENGTH);
}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Sanitized tool names are not collision-free.

sanitizeMcpToolNameForModel replaces every disallowed character with _, so two distinct raw tool names can map to the same sanitized key — e.g. catalog.query and catalog_query both become catalog_query. Truncation to 64 chars can also collide names sharing a long common prefix. Since qualifiedName is used to key both allTools (Line 330) and toolDisplayNames (Line 277), a collision silently overwrites the earlier tool's execute function and permission wiring with the later one's — the earlier tool becomes unreachable and its display name is lost, with no error or log.

Given MCP tool names are server-controlled and can contain arbitrary punctuation, this isn't a purely theoretical edge case.

🛠️ Proposed fix: detect and disambiguate collisions at the call site
 const originalExecute = tool.execute;
const rawQualifiedName = `${prefix}__${toolName}`;
- const qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ let qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ if (qualifiedName in allTools) {+ let suffix = 1;+ let candidate = qualifiedName;+ do {+ const suffixStr = `_${suffix++}`;+ candidate = `${qualifiedName.slice(0, MODEL_TOOL_NAME_MAX_LENGTH - suffixStr.length)}${suffixStr}`;+ } while (candidate in allTools);+ qualifiedName = candidate;+ }
const timeoutMs = env.SOURCEBOT_MCP_TOOL_CALL_TIMEOUT_MS;
toolDisplayNames[qualifiedName] = toolName;

Also applies to: 274-277

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts` around lines 118 - 127,
Sanitized MCP tool names can collide, causing later entries to overwrite earlier
ones in both allTools and toolDisplayNames. Update the MCP tool registration
flow in mcpToolSets.ts where qualifiedName is passed through
sanitizeMcpToolNameForModel so collisions are detected and handled instead of
silently overwriting; if two raw names sanitize to the same key, disambiguate
the generated key (or otherwise preserve uniqueness) before storing into
allTools and toolDisplayNames. Make sure the fix is applied consistently in the
tool-building logic around the qualifiedName handling so each MCP tool keeps its
own execute function and display name.

@@ -8,3 +8,10 @@ export type McpServerIconMap = Record<string, string>;
export const McpServerIconContext = createContext<McpServerIconMap>({});

export const useMcpServerIconMap = () => useContext(McpServerIconContext);

// Maps provider-safe model tool names back to raw MCP tool names for display.
export type McpToolNameMap = Record<string, string>;

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.

nit: consider moving this into a separate file

brendan-kellam
brendan-kellam previously approved these changes Jul 6, 2026

@brendan-kellambrendan-kellam 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.

lgtm

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0ccbb4b. Configure here.

Comment threadpackages/web/src/ee/features/chat/mcp/mcpToolSets.ts
@jsourcebot
jsourcebot merged commit d9d252b into mainJul 10, 2026
10 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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.

2 participants

@jsourcebot@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Jminnetian/sanitize tool names - #1423

Merged
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names
Jul 10, 2026
Merged

Jminnetian/sanitize tool names#1423
jsourcebot merged 4 commits into
mainfrom
jminnetian/sanitize-tool-names

Conversation

@jsourcebot

@jsourcebotjsourcebot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

MCP server tool names with certain characters (e.g " . ") would break the tool names open AI was expecting with their models. We now sanitize the names to avoid running into this issue.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed Ask connector MCP tools failing when provider-facing names contain unsupported characters.
    • MCP tool names are now normalized for reliable execution while preserving their original names in the chat interface.
    • Updated tool approvals, requests, search results, and status messages to show clearer server and tool names.
  • Tests
    • Added coverage for name normalization, display-name mapping, tool execution, and UI rendering.

Note

Low Risk
Targeted EE Ask/MCP naming and display path with tests; execution still uses raw MCP names, so risk is limited to display/metadata and model-facing identifiers.

Overview
Ask connector MCP tools whose names include characters providers reject (e.g. dots in catalog.query-catalog-entities) were failing at the model layer. This change normalizes fully qualified tool keys exposed to the LLM via sanitizeMcpToolNameForModel (invalid chars → _, max 64 chars) while MCP execution and persistence still use the server’s raw tool name.

The agent now streams data-mcp-tool parts mapping modelToolNamerawToolName. The chat thread hydrates that into McpToolNameContext (alongside server favicons in the renamed mcpDisplayMetadataContext). Tool cards, approval banners, and connector tool search use getMcpToolDisplayParts so users see names like backstage: catalog.query-catalog-entities instead of the sanitized model identifier; older threads without metadata keep the previous parse-based fallback.

Tests cover sanitization, getMcpTools behavior, agent streaming, and UI rendering.

Reviewed by Cursor Bugbot for commit 2edd846. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

MCP tool names are sanitized for provider compatibility, while raw names are propagated through streamed metadata and restored in chat tool, approval, and search displays.

Changes

MCP tool name sanitization and display mapping

Layer / File(s)Summary
Sanitization and raw-name mapping
packages/web/src/ee/features/chat/mcp/mcpToolSets.ts, packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts, CHANGELOG.md
Sanitizes model-facing MCP tool names, preserves raw names in toolDisplayNames, updates the result shape, and tests replacement, truncation, and mapping behavior.
Agent discovery and streamed metadata
packages/web/src/features/chat/types.ts, packages/web/src/ee/features/chat/agent.ts, packages/web/src/ee/features/chat/agent.test.ts
Adds the MCP tool data-part contract, emits model/raw name pairs during MCP discovery, and verifies the streamed payload.
Chat thread mapping state
packages/web/src/ee/features/chat/mcpDisplayMetadataContext.tsx, packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx, packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
Stores mappings from initial and streamed messages, provides them through context, and excludes mapping parts from direct step rendering.
Tool display rendering
packages/web/src/ee/features/chat/components/chatThread/tools/*, packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
Uses mapped raw names in MCP tool, approval, and tool-search displays, with fallback formatting and rendering tests.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
participant MCPToolSets
participant createAgentStream
participant ChatThread
participant ToolComponents
MCPToolSets->>createAgentStream: expose sanitized tool names and raw-name mappings
createAgentStream->>ChatThread: emit data-mcp-tool model/raw pairs
ChatThread->>ToolComponents: provide McpToolNameContext
ToolComponents->>ToolComponents: render resolved display names
Loading

Suggested reviewers:msukkari

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title matches the core change: sanitizing tool names, though the prefix adds unnecessary noise.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jminnetian/sanitize-tool-names

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts (1)

117-236: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a test for punctuation-collision sanitization.

The new "keeps valid hyphenated tool names distinct from underscored names" test (Lines 220-235) only exercises a non-colliding case (- is preserved). Consider adding a test with two raw tool names that both sanitize to the same string (e.g. foo.bar and foo_bar) to lock in the intended collision-handling behavior once it's implemented — see the related comment in mcpToolSets.ts.

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts` around lines 117 -
236, Add a collision-focused test in sanitizeMcpToolNameForModel/getMcpTools
coverage: the current hyphen-vs-underscore case only proves distinct valid names
stay distinct. Create a test with two raw MCP tool names that sanitize to the
same model-facing key (for example punctuation vs underscore), and assert the
intended collision-handling behavior in getMcpTools, including the resulting
tools map and any disambiguation strategy referenced by mcpToolSets.ts.
🤖 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 `@CHANGELOG.md`:
- Line 35: The new CHANGELOG.md entry under [Unreleased] is missing the required
pull request link. Update the entry so the single-sentence description is
followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts`:
- Around line 118-127: Sanitized MCP tool names can collide, causing later
entries to overwrite earlier ones in both allTools and toolDisplayNames. Update
the MCP tool registration flow in mcpToolSets.ts where qualifiedName is passed
through sanitizeMcpToolNameForModel so collisions are detected and handled
instead of silently overwriting; if two raw names sanitize to the same key,
disambiguate the generated key (or otherwise preserve uniqueness) before storing
into allTools and toolDisplayNames. Make sure the fix is applied consistently in
the tool-building logic around the qualifiedName handling so each MCP tool keeps
its own execute function and display name.
---
Nitpick comments:
In `@packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts`:
- Around line 117-236: Add a collision-focused test in
sanitizeMcpToolNameForModel/getMcpTools coverage: the current
hyphen-vs-underscore case only proves distinct valid names stay distinct. Create
a test with two raw MCP tool names that sanitize to the same model-facing key
(for example punctuation vs underscore), and assert the intended
collision-handling behavior in getMcpTools, including the resulting tools map
and any disambiguation strategy referenced by mcpToolSets.ts.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f74ea55e-abc3-42fc-b22a-fb331728c6f4

📥 Commits

Reviewing files that changed from the base of the PR and between 9c4780d and a3c6855.

📒 Files selected for processing (13)
  • CHANGELOG.md
  • packages/web/src/ee/features/chat/agent.test.ts
  • packages/web/src/ee/features/chat/agent.ts
  • packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx
  • packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
  • packages/web/src/ee/features/chat/components/chatThread/toolApprovalBanner.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.test.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/toolSearchToolComponent.tsx
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts
  • packages/web/src/ee/features/chat/mcp/mcpToolSets.ts
  • packages/web/src/ee/features/chat/mcpServerIconContext.tsx
  • packages/web/src/features/chat/types.ts

Comment threadCHANGELOG.md
- Upgraded `nodemailer` to `^9.0.1`. [#1356](https://github.com/sourcebot-dev/sourcebot/pull/1356)
- Upgraded `@opentelemetry/core` to `^2.8.0`. [#1413](https://github.com/sourcebot-dev/sourcebot/pull/1413)
- [EE] Fixed connector setup dialogs to add scrolling when connector setup content goes out of view.
- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Missing PR link in CHANGELOG entry.

Unlike the surrounding entries, this new line has no trailing [#<id>](url) link. As per coding guidelines, "Update CHANGELOG.md with an entry under [Unreleased] linking to the new PR" and "CHANGELOG.md entries must follow the format: single sentence description followed by a link in the format [#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>)."

📝 Proposed fix
-- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.+- [EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI. [`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.
-[EE] Fixed Ask connector MCP tools with provider-invalid names failing to run by sanitizing model-facing tool names while preserving raw names in the UI.[`#1423`](https://github.com/sourcebot-dev/sourcebot/pull/1423)
🤖 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 `@CHANGELOG.md` at line 35, The new CHANGELOG.md entry under [Unreleased] is
missing the required pull request link. Update the entry so the single-sentence
description is followed by the matching
[#<id>](https://github.com/sourcebot-dev/sourcebot/pull/<id>) link, consistent
with the surrounding CHANGELOG entries and the project’s CHANGELOG formatting
rules.

Source: Coding guidelines

Comment on lines +118 to +127
/**
* Provider APIs such as OpenAI Responses enforce tight charset and length
* limits for tool names. MCP tool names are server-controlled, so normalize the
* fully qualified name before exposing it to the model.
*/
export function sanitizeMcpToolNameForModel(name: string): string {
const sanitized = name.replace(/[^A-Za-z0-9_-]/g, '_') || '_';
return sanitized.slice(0, MODEL_TOOL_NAME_MAX_LENGTH);
}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Sanitized tool names are not collision-free.

sanitizeMcpToolNameForModel replaces every disallowed character with _, so two distinct raw tool names can map to the same sanitized key — e.g. catalog.query and catalog_query both become catalog_query. Truncation to 64 chars can also collide names sharing a long common prefix. Since qualifiedName is used to key both allTools (Line 330) and toolDisplayNames (Line 277), a collision silently overwrites the earlier tool's execute function and permission wiring with the later one's — the earlier tool becomes unreachable and its display name is lost, with no error or log.

Given MCP tool names are server-controlled and can contain arbitrary punctuation, this isn't a purely theoretical edge case.

🛠️ Proposed fix: detect and disambiguate collisions at the call site
 const originalExecute = tool.execute;
const rawQualifiedName = `${prefix}__${toolName}`;
- const qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ let qualifiedName = sanitizeMcpToolNameForModel(rawQualifiedName);+ if (qualifiedName in allTools) {+ let suffix = 1;+ let candidate = qualifiedName;+ do {+ const suffixStr = `_${suffix++}`;+ candidate = `${qualifiedName.slice(0, MODEL_TOOL_NAME_MAX_LENGTH - suffixStr.length)}${suffixStr}`;+ } while (candidate in allTools);+ qualifiedName = candidate;+ }
const timeoutMs = env.SOURCEBOT_MCP_TOOL_CALL_TIMEOUT_MS;
toolDisplayNames[qualifiedName] = toolName;

Also applies to: 274-277

🤖 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 `@packages/web/src/ee/features/chat/mcp/mcpToolSets.ts` around lines 118 - 127,
Sanitized MCP tool names can collide, causing later entries to overwrite earlier
ones in both allTools and toolDisplayNames. Update the MCP tool registration
flow in mcpToolSets.ts where qualifiedName is passed through
sanitizeMcpToolNameForModel so collisions are detected and handled instead of
silently overwriting; if two raw names sanitize to the same key, disambiguate
the generated key (or otherwise preserve uniqueness) before storing into
allTools and toolDisplayNames. Make sure the fix is applied consistently in the
tool-building logic around the qualifiedName handling so each MCP tool keeps its
own execute function and display name.

@@ -8,3 +8,10 @@ export type McpServerIconMap = Record<string, string>;
export const McpServerIconContext = createContext<McpServerIconMap>({});

export const useMcpServerIconMap = () => useContext(McpServerIconContext);

// Maps provider-safe model tool names back to raw MCP tool names for display.
export type McpToolNameMap = Record<string, string>;

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.

nit: consider moving this into a separate file

brendan-kellam
brendan-kellam previously approved these changes Jul 6, 2026

@brendan-kellambrendan-kellam 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.

lgtm

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0ccbb4b. Configure here.

Comment threadpackages/web/src/ee/features/chat/mcp/mcpToolSets.ts
@jsourcebot
jsourcebot merged commit d9d252b into mainJul 10, 2026
10 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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.

2 participants

@jsourcebot@brendan-kellam