Skip to content

fix(mcp): truncate tool names exceeding 64-char provider limit#24871

Open
georgeglarson wants to merge 2 commits intoanomalyco:devfrom
georgeglarson:fix/mcp-tool-name-64-char-limit
Open

fix(mcp): truncate tool names exceeding 64-char provider limit#24871
georgeglarson wants to merge 2 commits intoanomalyco:devfrom
georgeglarson:fix/mcp-tool-name-64-char-limit

Conversation

@georgeglarson
Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #3523

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

OpenAI's tool-use API rejects tools[].function.name strings longer than 64 characters. When an MCP server's combined <sanitized-server>_<sanitized-tool> overflows, the provider returns 400 and opencode silently stops processing because the error is logged at DEBUG but never surfaces to the TUI — exactly the symptom in the issue.

buildToolName() (new helper next to sanitize in packages/opencode/src/mcp/index.ts) returns combined names ≤64 chars unchanged. Overflows truncate to prefix(55) + "_" + hash(8) where hash is Hash.fast(combined).slice(0, 8). That preserves 55 chars of readable prefix while keeping keys distinct when two tools' first 64 chars would otherwise collide. Every truncation logs a log.warn.

Applied at tools() only — the call site feeding function.name to the provider. The :-separated keys in fetchFromClient() (prompts/resources) aren't subject to the 64-char limit, so I left them alone.

PR #15595 attempted this but was opened against the pre-Effect namespace MCP code, so that diff is rebase-incompatible regardless of approach. It also uses blind .slice(0, 64), which silently drops one of any two tools whose first 64 chars match — the hash suffix here prevents that.

How did you verify your code works?

Two new tests in packages/opencode/test/mcp/lifecycle.test.ts:

  1. Registers chrome-devtools-aaaaaaaaaaaaaaaaaaa (the issue's reproducer) with two tools whose combined names share a 55-char prefix. Asserts both end up in the registry, both ≤64 chars, both end with _[0-9a-f]{8}, and the keys are distinct.
  2. Short-name case asserts the key is unchanged <server>_<tool>.

Demonstrating the bug catch with the source change reverted (tests kept):

Run Combined name length Test result
Without this fix 82 chars expect(<= 64), got 82 — fail
With this fix 64 chars w/ _<8hex> suffix pass

Full run: bun test test/mcp/lifecycle.test.ts → 21 pass, 0 fail (19 existing + 2 new). bun run typecheck → clean.

Screenshots / recordings

N/A — no UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

OpenAI's tool-use API rejects tools[].function.name >64 chars. When
an MCP server's `<server>_<tool>` overflows, the provider returns 400
and opencode silently stops because the error is DEBUG-logged but
never surfaces to the TUI.

Add buildToolName() next to sanitize: <=64 chars passes through; over
truncates to prefix(55) + "_" + Hash.fast(combined).slice(0, 8). Logs
a warn on truncation. Applied at tools() only -- the call site that
feeds function.name to providers. fetchFromClient()'s ':'-separated
keys for prompts/resources are not subject to the 64-char limit.

Closes anomalyco#3523

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 29, 2026 00:43
@github-actions
Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Potential duplicate found:

Why it's related:
PR #15595 is explicitly mentioned in your PR description as a previous attempt to fix the same issue (truncating MCP tool names exceeding the 64-character limit). However, #15595 was opened against pre-Effect code and used a blind .slice(0, 64) approach that could silently drop tools with matching prefixes. Your PR (#24871) provides an improved implementation using a hash suffix to prevent collisions.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes MCP tool registration against OpenAI’s 64-character tools[].function.name limit by introducing a deterministic truncation strategy that preserves uniqueness, preventing provider 400s that previously caused MCP processing to stop silently.

Changes:

  • Add buildToolName() helper to cap <server>_<tool> names at 64 chars using a readable prefix plus an 8-hex hash suffix on overflow.
  • Apply buildToolName() in MCP.tools() when constructing the tool map passed to the provider.
  • Add lifecycle tests covering truncation behavior and collision avoidance, plus a short-name “unchanged” case.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
packages/opencode/src/mcp/index.ts Introduces buildToolName() and uses it when registering tools to enforce the 64-char provider constraint.
packages/opencode/test/mcp/lifecycle.test.ts Adds tests ensuring long tool names are truncated to ≤64 with unique hashed suffixes, and short names remain unchanged.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/opencode/src/mcp/index.ts Outdated
if (combined.length <= MAX_TOOL_NAME) return combined
const hash = Hash.fast(combined).slice(0, 8)
const truncated = combined.slice(0, MAX_TOOL_NAME - hash.length - 1) + "_" + hash
log.warn("MCP tool name exceeds 64 chars, truncating with hash suffix", {
Avoids drift if the limit changes. Suggested by Copilot review on PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] tools[21].function.name': string too long. Expected a string with maximum length 64, but got a string with length 65 instead.

2 participants