fix(tools): normalize truly empty MCP tool schemas for OpenAI

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.
This commit is contained in:
Bartok Moltbot
2026-04-03 04:43:26 -04:00
committed by Peter Steinberger
parent 6845b8061c
commit 19dbe00763
2 changed files with 22 additions and 0 deletions

View File

@@ -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<string, unknown>;
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",

View File

@@ -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[];