Repository navigation
Amazon Bedrock: a single tool name >64 chars rejects the entire Converse toolConfig, silently dropping MCP tools / subagents / providers #53828
Description
Activity
Thanks for the detailed write-up! Before we try to reproduce this, could you describe briefly, in your own words, what you did, what you expected and what you actually saw? We also need a few specifics:
- OpenCode version: the output of
opencode --version, plus your OS and how you installed OpenCode. - Your MCP config: in particular, whether the
pacserver (or any other MCP server) hascodemode: false. In v2, MCP tools run through Code Mode by default. Bedrock then only sees a singleexecutetool, so a long MCP tool name shouldn't reachtoolConfig. In the current code, it can only exceed 64 characters when Code Mode is turned off for that server. - The exact error: the full
ValidationExceptiontext and the related log lines. Without them we can't tell what you mean by subagents and the provider "vanishing" (for example, the exact message you get when an agent "cannot run as a subagent"). - The plugin you mentioned (
bedrock-tool-name-cap.js): it isn't attached to the issue, so please paste it if you can.
You can reply here, or edit the issue. If you edit it, please also leave a comment saying you've updated it, so we know when you're done.
These issues are related. The list is ordered by how likely each one is to cover your problem:
- [V2] MCP tool name longer than 64 characters blocks all MCP tools from every server #38808: v2 report that an MCP tool name over 64 characters blocked MCP tool registration. It was closed as stale, not fixed.
- V2: add stable MCP tool-name aliases before registration #34485: v2 MCP tool-name aliasing (cleaning up names, shortening long ones with a hash), marked as done.
- fix(ai): sanitize replayed Bedrock tool names #48750: shortens tool names to 64 characters when past tool calls are replayed to Bedrock. It does not cover the names of the tools sent to the model.
- [BUG] tools[21].function.name': string too long. Expected a string with maximum length 64, but got a string with length 65 instead. #3523 / fix(mcp): truncate tool names exceeding 64-char provider limit #43684: the same 64-character limit problem in v1 (OpenAI), and a v1 PR to truncate names that was closed without merging.
- OpenCode version: the output of
Thanks — and your Code Mode point is well taken. After digging into our own logs I now think my original root-cause (the 64-char
toolConfigoverflow) is probably wrong for our setup, and the real symptom is narrower than I framed it. Here are the specifics you asked for, and a corrected read.1. Version / OS / install
opencode v2.0.25- Windows 11 Pro (26300), PowerShell 7
- Installed via npm global (nvm4w):
C:\nvm4w\nodejs\opencode.ps1 - Provider:
amazon-bedrock(US-Gov), Claude models via Converse.
2. MCP config / Code Mode
No server sets
codemodeat all, so all MCP servers use the v2 default (Code Mode ON). Confirmed in-session: the agent only sees theexecutetool + asearchcatalog, exactly as you describe. Our servers and their connection tool counts (from the log, i.e. what the server exposes, NOT what reaches Bedrock):pac=70,azure-devops=49,playwright=25,codegraph=1. So you're right — under Code Mode those longpac_*/azure-devops_*names are collapsed behindexecuteand shouldn't reachtoolConfig.3. The exact error (and what I now believe is actually happening)
There is no
ValidationExceptionand no "Member must have length ≤ 64" anywhere in the log. I went back through the full~/.local/share/opencode/log/opencode.logand found only INFO/mcp connectedlines plus two unrelated config-normalization WARNs ($.small_model,$.compaction.prune— "unsupported"). So the dramatic "provider vanishes" framing in my original report was not something I actually observed in logs — I inferred it. I retract that part.What I did reproducibly observe: calling the Task/subagent tool with certain agents returns, synchronously:
Agent <name> cannot run as a subagentThis is a client-side guard, not a Bedrock error, and nothing is logged server-side for it.
Crucially, two of the agents that hit this are
mode: primary(dataverse-frontend-agent,cicd-migration-engineer) — for those the message is correct (a primary legitimately can't be a subagent); that was my own confusion, not a bug.The one genuine anomaly:
catms-security-auditorismode: subagent(verified in its frontmatter) yet still returns "cannot run as a subagent", and this persisted across a full process restart (confirmed the servingopencode serve --servicePID post-dated the restart). That's the behavior I can't explain and would like help with — it looks unrelated to tool-name length. If there's a known reason amode: subagentagent is excluded from a primary's dispatchable set (e.g. an allow-list, a model/variant resolution failure, a description/front-matter parse issue), that's the real lead. Happy to run any--print-logs --log-level debugrepro you want.4. The plugin (
bedrock-tool-name-cap.js)Full source below. Given #2 above, I now suspect this plugin is a no-op in our Code-Mode setup (it only renames names >64, and under Code Mode none reach it), so it is not what's gating the auditor. I'm no longer treating it as a fix; posting it only because you asked.
import { Plugin } from "@opencode/plugin" import { createHash } from "node:crypto" const MAX = 64 const HASH_LEN = 8 const SEP = "__" function aliasFor(name) { const hash = createHash("sha1").update(name).digest("hex").slice(0, HASH_LEN) const keep = MAX - SEP.length - HASH_LEN const head = name.slice(0, keep).replace(/[^a-zA-Z0-9_-]/g, "_") return `${head}${SEP}${hash}` } export default Plugin.define({ id: "bedrock-tool-name-cap", setup(ctx) { let registration try { registration = ctx.tool.transform((editor) => { try { const seen = new Set() for (const tool of editor.list()) { const id = tool.id if (typeof id !== "string") continue const tooLong = id.length > MAX const badChar = /[^a-zA-Z0-9_-]/.test(id) const dup = seen.has(id) if (!tooLong && !badChar && !dup) { seen.add(id); continue } let alias = aliasFor(id) let salt = 0 while (seen.has(alias)) alias = aliasFor(id + "#" + ++salt) seen.add(alias) try { editor.add({ ...tool, name: alias }); editor.remove(id) } catch (e) { console.warn(`bedrock-tool-name-cap: could not rename "${id}": ${e?.message ?? e}`) } } } catch (e) { console.warn(`bedrock-tool-name-cap: transform body failed: ${e?.message ?? e}`) } }) } catch (e) { console.warn(`bedrock-tool-name-cap: failed to register transform (no-op): ${e?.message ?? e}`) } return async () => { try { const r = await registration; if (r?.dispose) await r.dispose() } catch {} } }, })
Net
Please treat this as two separable things: (a) the 64-char
toolConfigrisk — you've shown Code Mode makes it a non-issue by default, so this may only matter for servers withcodemode: false(if so, a fail-loud startup check per your "alternatives" would still be a nice guardrail, and the related #34485/#48750 largely cover it); and (b) the actual thing that bit me — amode: subagentagent being undispatchable with "cannot run as a subagent", surviving a restart. I'm glad to re-file (b) as a clean, separate issue with a minimal repro + debug logs if that's more useful than reusing this one. Apologies for the misdirected framing on (a).Thanks for going back through your logs and for the clear correction. That settles part (a): with Code Mode on (the default), Bedrock only sees
execute, so long MCP tool names can't overflowtoolConfig. Since you didn't see aValidationException, there's nothing to fix here for your setup. Thecodemode: falsecase is related to #34485, #38808 and #48750.For part (b), yes, please open it as a separate issue. It's a different problem from this one. Some pointers that may help you narrow it down first:
- In v2 the message
Agent <name> cannot run as a subagentis only returned when the agent's resolved mode is exactlyprimary(packages/core/src/tool/plugin/subagent.ts). If the agent weren't found at all, you'd getUnknown agent: <name>instead. So something is turningcatms-security-auditorintoprimaryeven though its file saysmode: subagent. - Some likely causes, based on
packages/core/src/config/plugin/agent.ts:- The file is under a
mode/ormodes/folder. Files there are always forced tomode: primary, whatever their frontmatter says. Onlyagent/oragents/keep the frontmatter mode. - Another definition with the same name. For example an
agententry forcatms-security-auditorinopencode.json(global or project), or a secondcatms-security-auditor.mdin another agent folder (e.g. both~/.config/opencode/agents/and<project>/.opencode/agents/). Each definition overrides the earlier one, so a later one withmode: primarywins. - Agents with no
modeat all default toprimary. That only matters here if the frontmatter isn't being read as you expect, e.g. the file contains keys from the old v1 format, so it gets converted.
- The file is under a
When you open the new issue, please include:
- the full path(s) of every file and config entry that defines
catms-security-auditor - its frontmatter
- the primary agent you're calling it from
- your version (2.0.25), and debug logs if you have them
This issue will be closed automatically, since the original report no longer applies.
- In v2 the message
opencode-agent commented
on Oct 10, 2026 on Oct 10, 2026 – with OpenCode AgentContributorMore actionsClosing this as not planned. As you found, with Code Mode on (the default) Bedrock only sees
execute, so long MCP tool names can't overflowtoolConfig, and your logs had noValidationException. Thecodemode: falsecase is related to #34485, #38808 and #48750.The problem you still have,
catms-security-auditorreturningcannot run as a subagenteven though its file saysmode: subagent, is a separate issue. Please open a new issue for it with:- the path of every file or config entry that defines that agent (look especially for files under
mode//modes/folders, and for other definitions with the same name) - its frontmatter
- the primary agent you call it from
- your version and debug logs
Thanks for taking the time to dig into this and correct the report.
- the path of every file or config entry that defines that agent (look especially for files under
Summary
AWS Bedrock's Converse API enforces
toolSpec.name∈^[a-zA-Z0-9_-]{1,64}$(max 64 chars, unique per request). opencode serializes every exposed tool (built-ins, MCP server tools, and subagent dispatch tools) intotoolConfig.tools[]. If any one name violates the cap, Bedrock rejects the whole request with:The practical failure is silent and non-local: at session start the offending tool's whole surface (and in our case several subagents and even the provider) disappears from the dispatchable/available set, with no clear error surfaced to the user. The user just finds an agent "cannot run as a subagent" / missing.
Repro
pac≥ 2.12.1):pac_licensing_get-user-per-flow-capacity-source-flow-context-summary-for-user-id= 80 chars.Current user-side mitigations (all brittle avoidance, not fixes)
permissiondeny of whole MCP surfaces (pac_*,azure-devops_*) so the long names never serialize (manual, easy to miss — we had 4 subagents silently dropped because their deny lists weren't exhaustive).Proposed fix (core)
Normalize tool names in the Bedrock request builder (the amazon-bedrock/mantle serialization layer), immediately before constructing
toolConfig:[a-zA-Z0-9_-]or colliding, map it to a deterministic ≤64 alias (e.g.head(name, 64-9) + "__" + sha1(name)[:8]), keeping a reverse map for thetoolUse/toolResultround-trip.This makes the class of bug impossible rather than avoided, and is invisible to the model (names are opaque identifiers to it).
Alternatives considered
Workaround available today (plugin API)
The v2 Promise plugin API exposes
ctx.tool.transform(editor => ...)witheditor.list()/.add()/.remove()andctx.tool.list()documented as "keyed by effective name". A local plugin can rename >64 names to ≤64 aliases at registration time; since opencode drives tool-use by the effective name, no reverse map is needed plugin-side. (Attached:bedrock-tool-name-cap.js.) This confirms the fix is correct in principle and could be upstreamed into the Bedrock builder so plugins aren't required.Severity
High — silent capability loss (subagents/providers vanish) with a misleading downstream error, on a supported provider, triggered merely by installing/upgrading an MCP server.