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

@jsourcebot jsourcebot 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

coderabbitai Bot 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 name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring 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 name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The 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.

@coderabbitai coderabbitai Bot 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 thread CHANGELOG.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-kellam brendan-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

@cursor cursor Bot 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 thread packages/web/src/ee/features/chat/mcp/mcpToolSets.ts
@jsourcebot jsourcebot merged commit d9d252b into main Jul 10, 2026
10 checks passed
Sign up for free to 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