Skip to content

Jminnetian/sanitize tool names#1423

Open
jsourcebot wants to merge 3 commits into
mainfrom
jminnetian/sanitize-tool-names
Open

Jminnetian/sanitize tool names#1423
jsourcebot wants to merge 3 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

  • New Features

    • MCP tools now display clearer, user-friendly names in chat and approval flows.
    • Tool search and tool request details now show the mapped display name instead of only the provider-safe name.
  • Bug Fixes

    • Fixed an issue where some Ask connector MCP tools could fail to run when their names weren’t compatible with model-facing naming rules.
    • Improved consistency so raw tool names still appear in the UI while model-safe names are used behind the scenes.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Introduces sanitization of MCP tool names for model-facing use while preserving raw tool names for UI display. Adds sanitizeMcpToolNameForModel and a toolDisplayNames map in mcpToolSets, propagates raw names through agent streaming via onMcpToolDiscovered and new data-mcp-tool message parts, and updates chat UI context/components to resolve and render raw display names.

Changes

MCP Tool Name Sanitization and Display

Layer / File(s) Summary
Sanitization and toolDisplayNames mapping
packages/web/src/ee/features/chat/mcp/mcpToolSets.ts, packages/web/src/ee/features/chat/mcp/mcpToolSets.test.ts, CHANGELOG.md
Adds sanitizeMcpToolNameForModel and MODEL_TOOL_NAME_MAX_LENGTH, sanitizes qualified tool names for model use, extends McpToolsResult with toolDisplayNames, and adds tests plus a changelog entry.
Agent stream propagation of raw tool names
packages/web/src/ee/features/chat/agent.ts, packages/web/src/ee/features/chat/agent.test.ts, packages/web/src/features/chat/types.ts
Adds onMcpToolDiscovered callback to emit data-mcp-tool UI messages with model and raw tool names, extends SBChatMessageDataParts, and adds a test verifying the emitted payload.
Chat UI context and state wiring
packages/web/src/ee/features/chat/mcpServerIconContext.tsx, packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx, packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
Adds McpToolNameContext/useMcpToolNameMap, tracks mcpToolNameMap state from data-mcp-tool parts, wraps UI with the new provider, and handles the new part type in StepPartRenderer.
Tool display components using raw names
packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.tsx, .../mcpToolComponent.test.tsx, .../toolApprovalBanner.tsx, .../toolSearchToolComponent.tsx
Adds getMcpToolDisplayParts helper to resolve raw display names, replaces parseMcpToolName usage across approval banner and tool search components, updates status/request labels, and adds rendering tests.

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

Sequence Diagram(s)

sequenceDiagram
    participant Agent as createAgentStream
    participant McpToolSets as getMcpTools
    participant UIStream as createUIMessageStream
    participant ChatThread as chatThread.tsx
    participant ToolComponent as McpToolComponent / ToolApprovalItem

    Agent->>McpToolSets: getMcpTools(clients)
    McpToolSets-->>Agent: tools, toolDisplayNames (model->raw)
    loop for each discovered tool
        Agent->>Agent: onMcpToolDiscovered(modelToolName, rawToolName)
        Agent->>UIStream: write data-mcp-tool part
    end
    UIStream-->>ChatThread: stream data-mcp-tool part
    ChatThread->>ChatThread: update mcpToolNameMap via onData
    ChatThread->>ToolComponent: provide McpToolNameContext (mcpToolNameMap)
    ToolComponent->>ToolComponent: getMcpToolDisplayParts(modelToolName, rawToolNames)
    ToolComponent-->>ChatThread: render displayName (raw tool/server name)
Loading

Possibly related PRs

  • sourcebot-dev/sourcebot#1353: Modifies the same mcpToolComponent.tsx and detailsCard.tsx files, adding related chat-thread MCP tool UI functionality alongside this PR's tool name mapping changes.

Suggested reviewers: msukkari

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is related to the main change because it clearly indicates tool-name sanitization, which is the core of the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ 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.

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.

1 participant