diff --git a/packages/opencode/src/mcp/catalog.ts b/packages/opencode/src/mcp/catalog.ts index 6d4b985dd2b8..43f06ed05572 100644 --- a/packages/opencode/src/mcp/catalog.ts +++ b/packages/opencode/src/mcp/catalog.ts @@ -44,7 +44,7 @@ export function convertTool(mcpTool: MCPToolDef, client: Client, timeout?: numbe ...(mcpTool.inputSchema as JSONSchema7), type: "object", properties: (mcpTool.inputSchema.properties ?? {}) as JSONSchema7["properties"], - additionalProperties: false, + additionalProperties: (mcpTool.inputSchema as JSONSchema7).additionalProperties ?? false, } return dynamicTool({ diff --git a/packages/opencode/test/mcp/catalog.test.ts b/packages/opencode/test/mcp/catalog.test.ts new file mode 100644 index 000000000000..2a4e98d6a950 --- /dev/null +++ b/packages/opencode/test/mcp/catalog.test.ts @@ -0,0 +1,71 @@ +import { describe, expect, test } from "bun:test" +import type { Tool as MCPToolDef } from "@modelcontextprotocol/sdk/types.js" +import type { Schema } from "ai" +import { convertTool } from "../../src/mcp/catalog" + +const mockClient = {} as any + +function makeMCPTool(inputSchema: Record): MCPToolDef { + return { + name: "test_tool", + inputSchema: inputSchema as MCPToolDef["inputSchema"], + } +} + +function getJsonSchema(tool: ReturnType): Record { + return (tool.inputSchema as Schema).jsonSchema as Record +} + +describe("convertTool additionalProperties", () => { + test("defaults to false when server omits additionalProperties", () => { + const mcpTool = makeMCPTool({ + type: "object", + properties: { name: { type: "string" } }, + }) + + const tool = convertTool(mcpTool, mockClient) + const schema = getJsonSchema(tool) + + expect(schema.additionalProperties).toBe(false) + }) + + test("preserves additionalProperties: true from server schema", () => { + const mcpTool = makeMCPTool({ + type: "object", + properties: { name: { type: "string" } }, + additionalProperties: true, + }) + + const tool = convertTool(mcpTool, mockClient) + const schema = getJsonSchema(tool) + + expect(schema.additionalProperties).toBe(true) + }) + + test("preserves additionalProperties object sub-schema from server", () => { + const subSchema = { type: "string" as const } + const mcpTool = makeMCPTool({ + type: "object", + properties: { name: { type: "string" } }, + additionalProperties: subSchema, + }) + + const tool = convertTool(mcpTool, mockClient) + const schema = getJsonSchema(tool) + + expect(schema.additionalProperties).toEqual(subSchema) + }) + + test("preserves explicit additionalProperties: false from server", () => { + const mcpTool = makeMCPTool({ + type: "object", + properties: { name: { type: "string" } }, + additionalProperties: false, + }) + + const tool = convertTool(mcpTool, mockClient) + const schema = getJsonSchema(tool) + + expect(schema.additionalProperties).toBe(false) + }) +})