Describe the bug
@cloudflare/codemode's DynamicWorkerExecutor sanitizes provider tool names at registration (sanitizeToolName(name) — hyphens/dots become underscores), but the sandbox proxy passes the raw lookup key straight through to the dispatcher. As a result, callers that obtain the tool name from MCP discovery (raw form) and dispatch with bracket notation get Tool "foo-bar" not found, even though the same provider works when addressed via the sanitized identifier. The asymmetry remains after #879 and #1117 — those PRs introduced and internalized sanitization on the registration side, and closed #806, but the runtime dispatch path was not made symmetric.
To Reproduce
Steps to reproduce the behavior:
- Register a provider with a hyphenated tool name:
const fns = { "github-list-issues": async () => [{ id: 1 }] };
- Dispatch from sandbox code using the raw discovery form:
await executor.execute(
`async () => await codemode["github-list-issues"]({})`,
[{ name: "codemode", fns }]
);
- See error:
Tool "github-list-issues" not found.
The same code works if the lookup key is swapped to the sanitized identifier (codemode.github_list_issues({})), even though the provider key is the raw form in both cases.
Expected behavior
Both shapes should resolve to the same provider function:
- raw, the form returned by MCP discovery —
codemode["github-list-issues"]({})
- sanitized, the form generated by the type-gen / LLM path —
codemode.github_list_issues({})
Screenshots
N/A — programmatic test failure, reproducible via vitest-pool-workers.
Version:
@cloudflare/codemode 0.3.4 (current main, after #1117).
Additional context
Root cause
packages/codemode/src/executor.ts (lines 316–328 on current main):
for (const [name, fn] of Object.entries(provider.fns)) {
sanitizedFns[sanitizeToolName(name)] = fn;
}
Combined with the proxy template at lines ~256–278 that calls __dispatchers.${p.name}.call(String(toolName), ...) with the raw toolName from the Proxy get trap, the dispatcher only finds the sanitized key.
Suggested fix
Register each tool under both its raw name and its sanitized identifier when they differ. Two added lines, no new dependencies, no behaviour change for existing callers using the sanitized form:
for (const [name, fn] of Object.entries(provider.fns)) {
const sanitized = sanitizeToolName(name);
sanitizedFns[sanitized] = fn;
if (sanitized !== name) sanitizedFns[name] = fn;
}
Three new regression tests added in packages/codemode/src/tests/executor.test.ts covering raw-hyphen dispatch, raw-dot dispatch, and the same provider being reachable under both shapes. The existing should sanitize tool names with hyphens and dots test was also tightened — it now uses a raw provider key, so the test actually exercises the registration-time sanitization that its name advertises.
Impact
Any client that builds codemode dispatch programmatically from MCP tools/list output uses the raw names (hyphens preserved). The current behaviour silently routes only via the sanitized identifier, which works for type-generated AI code but breaks for hand-written / discovery-driven dispatch — including MCP clients that hand discovery results into a codemode portal, and integration tests asserting on the discovery name.
Workaround
Until merged: dispatch only via the sanitized identifier (codemode.github_list_issues({})). Clients that hand raw discovery names into codemode can normalize names client-side: name.replace(/[-.\s]/g, "_").
PR
Follows shortly from bkalytta-wq:fix/codemode-hyphen-dispatch-asymmetry.
Describe the bug
@cloudflare/codemode'sDynamicWorkerExecutorsanitizes provider tool names at registration (sanitizeToolName(name)— hyphens/dots become underscores), but the sandbox proxy passes the raw lookup key straight through to the dispatcher. As a result, callers that obtain the tool name from MCP discovery (raw form) and dispatch with bracket notation getTool "foo-bar" not found, even though the same provider works when addressed via the sanitized identifier. The asymmetry remains after #879 and #1117 — those PRs introduced and internalized sanitization on the registration side, and closed #806, but the runtime dispatch path was not made symmetric.To Reproduce
Steps to reproduce the behavior:
Tool "github-list-issues" not found.The same code works if the lookup key is swapped to the sanitized identifier (
codemode.github_list_issues({})), even though the provider key is the raw form in both cases.Expected behavior
Both shapes should resolve to the same provider function:
codemode["github-list-issues"]({})codemode.github_list_issues({})Screenshots
N/A — programmatic test failure, reproducible via
vitest-pool-workers.Version:
@cloudflare/codemode0.3.4 (current main, after #1117).Additional context
Root cause
packages/codemode/src/executor.ts(lines 316–328 on current main):Combined with the proxy template at lines ~256–278 that calls
__dispatchers.${p.name}.call(String(toolName), ...)with the rawtoolNamefrom theProxygettrap, the dispatcher only finds the sanitized key.Suggested fix
Register each tool under both its raw name and its sanitized identifier when they differ. Two added lines, no new dependencies, no behaviour change for existing callers using the sanitized form:
Three new regression tests added in
packages/codemode/src/tests/executor.test.tscovering raw-hyphen dispatch, raw-dot dispatch, and the same provider being reachable under both shapes. The existingshould sanitize tool names with hyphens and dotstest was also tightened — it now uses a raw provider key, so the test actually exercises the registration-time sanitization that its name advertises.Impact
Any client that builds codemode dispatch programmatically from MCP
tools/listoutput uses the raw names (hyphens preserved). The current behaviour silently routes only via the sanitized identifier, which works for type-generated AI code but breaks for hand-written / discovery-driven dispatch — including MCP clients that hand discovery results into a codemode portal, and integration tests asserting on the discovery name.Workaround
Until merged: dispatch only via the sanitized identifier (
codemode.github_list_issues({})). Clients that hand raw discovery names into codemode can normalize names client-side:name.replace(/[-.\s]/g, "_").PR
Follows shortly from
bkalytta-wq:fix/codemode-hyphen-dispatch-asymmetry.