From 19dbe007638d94450b10ebd12652fbad3480659b Mon Sep 17 00:00:00 2001 From: Bartok Moltbot Date: Fri, 3 Apr 2026 04:43:26 -0400 Subject: [PATCH] fix(tools): normalize truly empty MCP tool schemas for OpenAI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #60158 MCP tools with parameter-free schemas may return truly empty objects `{}` without a `type` field. The existing normalization handled `{ type: "object" }` → `{ type: "object", properties: {} }` but missed the truly empty case. OpenAI gpt-5.4 rejects tool schemas without `type: "object"` and `properties`, causing HTTP 400 errors: ``` Invalid schema for function 'flux-mcp__get_flux_instance': In context=(), object schema missing properties. ``` This change catches empty schemas (no type, no properties, no unions) before the final pass-through and converts them to the required format. Added test case for parameter-free MCP tool schemas. --- src/agents/pi-tools.schema.test.ts | 16 ++++++++++++++++ src/agents/pi-tools.schema.ts | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/src/agents/pi-tools.schema.test.ts b/src/agents/pi-tools.schema.test.ts index 81c56a7f0dd..bcaa1074402 100644 --- a/src/agents/pi-tools.schema.test.ts +++ b/src/agents/pi-tools.schema.test.ts @@ -4,6 +4,22 @@ import { normalizeToolParameters } from "./pi-tools.schema.js"; import type { AnyAgentTool } from "./pi-tools.types.js"; describe("normalizeToolParameters", () => { + it("normalizes truly empty schemas to type:object with properties:{} (MCP parameter-free tools)", () => { + const tool: AnyAgentTool = { + name: "get_flux_instance", + label: "get_flux_instance", + description: "Get current Flux instance status", + parameters: {}, + execute: vi.fn(), + }; + + const normalized = normalizeToolParameters(tool); + + const parameters = normalized.parameters as Record; + expect(parameters.type).toBe("object"); + expect(parameters.properties).toEqual({}); + }); + it("injects properties:{} for type:object schemas missing properties (MCP no-param tools)", () => { const tool: AnyAgentTool = { name: "list_regions", diff --git a/src/agents/pi-tools.schema.ts b/src/agents/pi-tools.schema.ts index 902c6ad0937..ea6d4145b71 100644 --- a/src/agents/pi-tools.schema.ts +++ b/src/agents/pi-tools.schema.ts @@ -133,6 +133,12 @@ export function normalizeToolParameterSchema( ? "oneOf" : null; if (!variantKey) { + // Handle truly empty schemas (no type, no properties, no unions) — + // OpenAI requires `type: "object"` with `properties` for tool schemas. + // MCP tools with parameter-free schemas may return `{}` or minimal objects. + if (!("type" in schemaRecord) && !("properties" in schemaRecord)) { + return applyProviderCleaning({ type: "object", properties: {}, ...schemaRecord }); + } return schema; } const variants = schemaRecord[variantKey] as unknown[];