Spaces:
Paused
Paused
icebear0828 Claude Opus 4.6 commited on
Commit ·
2df0167
1
Parent(s): e336506
fix: normalize tool schemas missing `properties` field
Browse filesMCP tools with no parameters send `{"type":"object"}` without a
`properties` key. The Codex/OpenAI backend strictly requires it,
returning 400. Add a shared `normalizeSchema()` helper that injects
`properties: {}` when missing, applied consistently across all four
format converters (OpenAI tools, OpenAI functions, Anthropic, Gemini).
Thanks to @lookvincent for identifying this issue (PR #22).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- CHANGELOG.md +1 -0
- src/translation/tool-format.ts +16 -4
CHANGELOG.md
CHANGED
|
@@ -33,6 +33,7 @@
|
|
| 33 |
|
| 34 |
### Fixed
|
| 35 |
|
|
|
|
| 36 |
- 额度窗口刷新后 Dashboard 仍显示累计 Token:本地计数器从未按窗口重置,现在 `refreshStatus()` 每次 acquire/getAccounts 时检查 `window_reset_at`,过期自动归零窗口计数器
|
| 37 |
- 空响应重试循环中账号双重释放:外层 catch 使用原始 `entryId` 而非当前活跃账号,导致换号重试失败时 double-release(`proxy-handler.ts`)
|
| 38 |
- `apply-update.ts` 模型比较不再误报删除:静态提取只含 2 个硬编码模型,与 YAML 的 24 个比较会产生 22 个假删除,现在只报新增
|
|
|
|
| 33 |
|
| 34 |
### Fixed
|
| 35 |
|
| 36 |
+
- 工具 schema 缺少 `properties` 字段导致 400 错误:MCP 工具发送 `{"type":"object"}` 无 `properties` 时,Codex 后端拒绝请求;现在所有格式转换器(OpenAI/Anthropic/Gemini)统一注入 `properties: {}`(感谢 @lookvincent 发现此问题,PR #22)
|
| 37 |
- 额度窗口刷新后 Dashboard 仍显示累计 Token:本地计数器从未按窗口重置,现在 `refreshStatus()` 每次 acquire/getAccounts 时检查 `window_reset_at`,过期自动归零窗口计数器
|
| 38 |
- 空响应重试循环中账号双重释放:外层 catch 使用原始 `entryId` 而非当前活跃账号,导致换号重试失败时 double-release(`proxy-handler.ts`)
|
| 39 |
- `apply-update.ts` 模型比较不再误报删除:静态提取只含 2 个硬编码模型,与 YAML 的 24 个比较会产生 22 个假删除,现在只报新增
|
src/translation/tool-format.ts
CHANGED
|
@@ -9,6 +9,18 @@ import type { ChatCompletionRequest } from "../types/openai.js";
|
|
| 9 |
import type { AnthropicMessagesRequest } from "../types/anthropic.js";
|
| 10 |
import type { GeminiGenerateContentRequest } from "../types/gemini.js";
|
| 11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
// ── Codex Responses API tool format ─────────────────────────────
|
| 13 |
|
| 14 |
export interface CodexToolDefinition {
|
|
@@ -30,7 +42,7 @@ export function openAIToolsToCodex(
|
|
| 30 |
name: t.function.name,
|
| 31 |
};
|
| 32 |
if (t.function.description) def.description = t.function.description;
|
| 33 |
-
if (t.function.parameters) def.parameters = t.function.parameters;
|
| 34 |
return def;
|
| 35 |
});
|
| 36 |
}
|
|
@@ -59,7 +71,7 @@ export function openAIFunctionsToCodex(
|
|
| 59 |
name: f.name,
|
| 60 |
};
|
| 61 |
if (f.description) def.description = f.description;
|
| 62 |
-
if (f.parameters) def.parameters = f.parameters;
|
| 63 |
return def;
|
| 64 |
});
|
| 65 |
}
|
|
@@ -75,7 +87,7 @@ export function anthropicToolsToCodex(
|
|
| 75 |
name: t.name,
|
| 76 |
};
|
| 77 |
if (t.description) def.description = t.description;
|
| 78 |
-
if (t.input_schema) def.parameters = t.input_schema;
|
| 79 |
return def;
|
| 80 |
});
|
| 81 |
}
|
|
@@ -110,7 +122,7 @@ export function geminiToolsToCodex(
|
|
| 110 |
name: fd.name,
|
| 111 |
};
|
| 112 |
if (fd.description) def.description = fd.description;
|
| 113 |
-
if (fd.parameters) def.parameters = fd.parameters;
|
| 114 |
defs.push(def);
|
| 115 |
}
|
| 116 |
}
|
|
|
|
| 9 |
import type { AnthropicMessagesRequest } from "../types/anthropic.js";
|
| 10 |
import type { GeminiGenerateContentRequest } from "../types/gemini.js";
|
| 11 |
|
| 12 |
+
// ── Helpers ─────────────────────────────────────────────────────
|
| 13 |
+
|
| 14 |
+
/** OpenAI requires `properties` when schema `type` is `"object"`. */
|
| 15 |
+
function normalizeSchema(
|
| 16 |
+
schema: Record<string, unknown>,
|
| 17 |
+
): Record<string, unknown> {
|
| 18 |
+
if (schema.type === "object" && !("properties" in schema)) {
|
| 19 |
+
return { ...schema, properties: {} };
|
| 20 |
+
}
|
| 21 |
+
return schema;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
// ── Codex Responses API tool format ─────────────────────────────
|
| 25 |
|
| 26 |
export interface CodexToolDefinition {
|
|
|
|
| 42 |
name: t.function.name,
|
| 43 |
};
|
| 44 |
if (t.function.description) def.description = t.function.description;
|
| 45 |
+
if (t.function.parameters) def.parameters = normalizeSchema(t.function.parameters);
|
| 46 |
return def;
|
| 47 |
});
|
| 48 |
}
|
|
|
|
| 71 |
name: f.name,
|
| 72 |
};
|
| 73 |
if (f.description) def.description = f.description;
|
| 74 |
+
if (f.parameters) def.parameters = normalizeSchema(f.parameters);
|
| 75 |
return def;
|
| 76 |
});
|
| 77 |
}
|
|
|
|
| 87 |
name: t.name,
|
| 88 |
};
|
| 89 |
if (t.description) def.description = t.description;
|
| 90 |
+
if (t.input_schema) def.parameters = normalizeSchema(t.input_schema);
|
| 91 |
return def;
|
| 92 |
});
|
| 93 |
}
|
|
|
|
| 122 |
name: fd.name,
|
| 123 |
};
|
| 124 |
if (fd.description) def.description = fd.description;
|
| 125 |
+
if (fd.parameters) def.parameters = normalizeSchema(fd.parameters);
|
| 126 |
defs.push(def);
|
| 127 |
}
|
| 128 |
}
|