diff --git a/packages/core/src/tool.ts b/packages/core/src/tool.ts index 1e46a289fec..5e2ca8401aa 100644 --- a/packages/core/src/tool.ts +++ b/packages/core/src/tool.ts @@ -308,7 +308,7 @@ function registrationError(tool: Tool.Info) { if (error) return error } const name = normalizedName(tool) - if (!/^[A-Za-z0-9_-]{1,64}$/.test(name)) return new RegistrationError({ name, message: `Invalid tool name: ${name}` }) + if (!/^[A-Za-z0-9_-]{1,128}$/.test(name)) return new RegistrationError({ name, message: `Invalid tool name: ${name}` }) const id = effectiveName(tool) if (tool.options?.codemode === false && id === "execute") return new RegistrationError({ name: id, message: 'Tool name "execute" is reserved for CodeMode' }) diff --git a/packages/core/test/mcp.test.ts b/packages/core/test/mcp.test.ts index 330abe21238..f37ec6a9553 100644 --- a/packages/core/test/mcp.test.ts +++ b/packages/core/test/mcp.test.ts @@ -2117,7 +2117,7 @@ testEffect(Layer.empty).live("isolates invalid MCP tools and preserves plugin tr }) satisfies Mcp.Tool const healthy = [tool("demo", "search"), tool("other", "lookup")] const namespace = tool("x".repeat(65), "lookup") - const catalog = yield* Ref.make([tool("demo", "x".repeat(65)), ...healthy, namespace]) + const catalog = yield* Ref.make([tool("demo", "x".repeat(129)), ...healthy, namespace]) yield* Effect.gen(function* () { const registry = yield* Tool.Service @@ -2146,7 +2146,7 @@ testEffect(Layer.empty).live("isolates invalid MCP tools and preserves plugin tr editor.remove("repaired_lookup") }) - yield* Ref.set(catalog, [tool("demo", "y".repeat(65)), ...healthy, tool("demo", "added"), namespace]) + yield* Ref.set(catalog, [tool("demo", "y".repeat(129)), ...healthy, tool("demo", "added"), namespace]) yield* bus.publish(McpEvent.ToolsChanged, { server: "demo" }) yield* waitForTool(registry, "demo_added") expect((yield* toolDefinitions(registry)).map((tool) => tool.name)).toEqual([ diff --git a/packages/core/test/tool-registry.test.ts b/packages/core/test/tool-registry.test.ts index 2c3e74dace7..f77e05a6630 100644 --- a/packages/core/test/tool-registry.test.ts +++ b/packages/core/test/tool-registry.test.ts @@ -329,7 +329,7 @@ describe("Tool", () => { { before: make(), "": make(), - ["x".repeat(65)]: make(), + ["x".repeat(129)]: make(), "echo.tool": constant("first"), echo_tool: constant("last"), execute: make(), @@ -346,6 +346,27 @@ describe("Tool", () => { }), ) + it.effect("registers 128-character MCP tool names in Code Mode", () => + Effect.gen(function* () { + const service = yield* Tool.Service + const name = "x".repeat(128) + yield* transform(service, { [name]: make(), ["x".repeat(129)]: make() }, { namespace: "cloudflare" }) + + const snapshot = yield* service.snapshot() + expect(codeModeListings(snapshot.codeModeCatalog!).map((tool) => tool.path)).toEqual([`cloudflare.${name}`]) + const result = yield* snapshot.execute({ + ...call("execute"), + call: { + type: "tool-call", + id: "call-long-mcp-name", + name: "execute", + input: { code: `return (await tools.cloudflare[${JSON.stringify(name)}]({ text: "hello" })).text` }, + }, + }) + expect(result.content).toEqual([{ type: "text", text: "hello" }]) + }), + ) + it.effect("executes native tools without requiring letter-leading names or namespace segments", () => Effect.gen(function* () { const service = yield* Tool.Service