Skip to content

fix: normalize MCP tool schemas missing properties field for OpenAI Responses API#58299

Merged
steipete merged 2 commits into
openclaw:mainfrom
yelog:fix/58246-tool-schema-normalization
Mar 31, 2026
Merged

fix: normalize MCP tool schemas missing properties field for OpenAI Responses API#58299
steipete merged 2 commits into
openclaw:mainfrom
yelog:fix/58246-tool-schema-normalization

Conversation

@yelog

@yelog yelog commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Summary

  • MCP tools with no parameters produce { type: "object" } schemas without a properties field. The OpenAI Responses API rejects these schemas, silently crashing entire sessions.
  • Adds properties: {} injection in normalizeToolParameters() (pi-tools.schema.ts) and convertTools() (openai-ws-message-conversion.ts) to ensure all object-type schemas include a properties field.
  • Adds test coverage for both normalization paths.

Changes

  • src/agents/pi-tools.schema.ts: Added branch in normalizeToolParameters() to inject properties: {} for bare { type: "object" } schemas before union/variant handling.
  • src/agents/openai-ws-message-conversion.ts: Modified convertTools() to check and normalize parameters before passing to the OpenAI Responses API.
  • src/agents/pi-tools.schema.test.ts: Added 3 test cases for schema normalization.
  • src/agents/openai-ws-stream.test.ts: Added 2 test cases for convertTools normalization.

Closes #58246

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Mar 31, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 68e02c9449

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agents/pi-tools.schema.ts Outdated
Comment on lines +140 to +144
"type" in schema &&
!("properties" in schema) &&
!Array.isArray(schema.anyOf) &&
!Array.isArray(schema.oneOf)
) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restrict properties injection to object schemas

This branch now adds properties: {} for any schema that merely has a type key, not just type: "object". If a tool declares a scalar/array root schema (for example type: "string"), the function rewrites it to an invalid combination like {"type":"string","properties":{}}, which can cause provider-side schema validation failures. The fix should gate this path on schema.type === "object" (or equivalent) before injecting properties.

Useful? React with 👍 / 👎.

@greptile-apps

greptile-apps Bot commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a real crash path where MCP tools with no parameters produce { type: "object" } schemas (without a properties field), which the OpenAI Responses API silently rejects — taking down entire sessions. The fix injects properties: {} in both normalization paths (normalizeToolParameters in pi-tools.schema.ts and convertTools in openai-ws-message-conversion.ts), and adds targeted test coverage for both paths.

Changes:

  • src/agents/pi-tools.schema.ts: New branch in normalizeToolParameters() to inject properties: {} for schemas that have type but lack properties, placed correctly before the anyOf/oneOf union-flattening block.
  • src/agents/openai-ws-message-conversion.ts: convertTools() now checks and normalizes bare object schemas before forwarding to the OpenAI Responses API. This path correctly guards with params.type === \"object\".
  • src/agents/pi-tools.schema.test.ts / src/agents/openai-ws-stream.test.ts: Five new test cases with good coverage of the injection, preservation, and additionalProperties scenarios.

Minor note: The condition in normalizeToolParameters checks \"type\" in schema (any type value) rather than schema.type === \"object\" as the convertTools path does. Top-level MCP tool schemas are always objects in practice, so this won't cause a real bug today, but aligning the guard would be more defensive.

Confidence Score: 5/5

Safe to merge — the fix directly addresses a confirmed crash and all remaining feedback is a minor style suggestion.

The fix is correct and well-targeted. Both normalization paths are covered by tests. The only finding is a P2 inconsistency: normalizeToolParameters does not restrict the properties: {} injection to schema.type === "object" the way convertTools does, which could theoretically inject invalid JSON Schema into non-object-typed schemas. In practice this path is unreachable since MCP tools always produce object schemas at the top level, so it does not block merge.

No files require special attention beyond the minor guard tightening noted in src/agents/pi-tools.schema.ts.

Important Files Changed

Filename Overview
src/agents/pi-tools.schema.ts Adds a new branch to inject properties: {} for bare { type: "object" } schemas. Logic is correct for real-world MCP tool schemas; the guard could be tightened to schema.type === "object" to match the stricter check used in convertTools.
src/agents/openai-ws-message-conversion.ts Correctly injects properties: {} only for type === "object" schemas missing a properties field; clean and minimal change with no regressions.
src/agents/pi-tools.schema.test.ts Adds 3 well-scoped test cases covering: no-parameter MCP tool injection, preservation of existing properties, and additionalProperties-only schema. All cases are correct.
src/agents/openai-ws-stream.test.ts Adds 2 test cases for convertTools — injection for missing-properties schemas and preservation for schemas that already have properties. Both are correct and align with the production fix.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/agents/pi-tools.schema.ts
Line: 139-150

Comment:
**Missing `type === "object"` guard — inconsistency with `convertTools`**

The sibling fix in `convertTools` explicitly narrows the injection to `params.type === "object"`:

```ts
params.type === "object" && !("properties" in params)
```

The condition added here is broader — it fires for *any* schema that has a `type` field but no `properties` field, including `{ type: "string" }`, `{ type: "array" }`, etc. For those non-object types, injecting `properties: {}` would produce invalid JSON Schema.

In practice all top-level tool schemas coming from MCP are `type: "object"`, so this is unlikely to cause a real-world bug today, but adding the narrower guard keeps the two paths consistent and future-proof:

```suggestion
  if (
    "type" in schema &&
    schema.type === "object" &&
    !("properties" in schema) &&
    !Array.isArray(schema.anyOf) &&
    !Array.isArray(schema.oneOf)
  ) {
    const schemaWithProperties = { ...schema, properties: {} };
    return preserveToolMeta({
      ...tool,
      parameters: applyProviderCleaning(schemaWithProperties),
    });
  }
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix: normalize MCP tool schemas missing ..." | Re-trigger Greptile

Comment on lines +139 to +150
if (
"type" in schema &&
!("properties" in schema) &&
!Array.isArray(schema.anyOf) &&
!Array.isArray(schema.oneOf)
) {
const schemaWithProperties = { ...schema, properties: {} };
return preserveToolMeta({
...tool,
parameters: applyProviderCleaning(schemaWithProperties),
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing type === "object" guard — inconsistency with convertTools

The sibling fix in convertTools explicitly narrows the injection to params.type === "object":

params.type === "object" && !("properties" in params)

The condition added here is broader — it fires for any schema that has a type field but no properties field, including { type: "string" }, { type: "array" }, etc. For those non-object types, injecting properties: {} would produce invalid JSON Schema.

In practice all top-level tool schemas coming from MCP are type: "object", so this is unlikely to cause a real-world bug today, but adding the narrower guard keeps the two paths consistent and future-proof:

Suggested change
if (
"type" in schema &&
!("properties" in schema) &&
!Array.isArray(schema.anyOf) &&
!Array.isArray(schema.oneOf)
) {
const schemaWithProperties = { ...schema, properties: {} };
return preserveToolMeta({
...tool,
parameters: applyProviderCleaning(schemaWithProperties),
});
}
if (
"type" in schema &&
schema.type === "object" &&
!("properties" in schema) &&
!Array.isArray(schema.anyOf) &&
!Array.isArray(schema.oneOf)
) {
const schemaWithProperties = { ...schema, properties: {} };
return preserveToolMeta({
...tool,
parameters: applyProviderCleaning(schemaWithProperties),
});
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-tools.schema.ts
Line: 139-150

Comment:
**Missing `type === "object"` guard — inconsistency with `convertTools`**

The sibling fix in `convertTools` explicitly narrows the injection to `params.type === "object"`:

```ts
params.type === "object" && !("properties" in params)
```

The condition added here is broader — it fires for *any* schema that has a `type` field but no `properties` field, including `{ type: "string" }`, `{ type: "array" }`, etc. For those non-object types, injecting `properties: {}` would produce invalid JSON Schema.

In practice all top-level tool schemas coming from MCP are `type: "object"`, so this is unlikely to cause a real-world bug today, but adding the narrower guard keeps the two paths consistent and future-proof:

```suggestion
  if (
    "type" in schema &&
    schema.type === "object" &&
    !("properties" in schema) &&
    !Array.isArray(schema.anyOf) &&
    !Array.isArray(schema.oneOf)
  ) {
    const schemaWithProperties = { ...schema, properties: {} };
    return preserveToolMeta({
      ...tool,
      parameters: applyProviderCleaning(schemaWithProperties),
    });
  }
```

How can I resolve this? If you propose a fix, please make it concise.

yelog and others added 2 commits March 31, 2026 18:21
…esponses API

Tools with no parameters produce { type: "object" } schemas without a
properties field. The OpenAI Responses API rejects these, silently
crashing entire sessions.

Add properties: {} injection in normalizeToolParameters() and
convertTools() to ensure all object-type schemas include a properties
field.

Closes openclaw#58246
@steipete
steipete force-pushed the fix/58246-tool-schema-normalization branch from 68e02c9 to b69047d Compare March 31, 2026 17:30
@steipete
steipete merged commit b4433a1 into openclaw:main Mar 31, 2026
9 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Landed via temp rebase onto main.

  • Gate: pnpm check && pnpm build && pnpm test && pnpm format
  • Land commit: b69047d
  • Merge commit: b4433a1

Thanks @yelog!

@prtags

prtags Bot commented Apr 23, 2026

Copy link
Copy Markdown

Related work from PRtags group unique-heron-2zsc

Title: Open PR duplicate: MCP schema properties + auto-reset override self-heal composite

Number Title
#58299* fix: normalize MCP tool schemas missing properties field for OpenAI Responses API
#60176 fix(tools): normalize truly empty MCP tool schemas for OpenAI
#69419 session: clear auto-sourced model/auth overrides on /new and /reset
#69956 fix(mcp-http): ensure MCP tool schemas always include properties field

* This PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

openai-codex/gpt-5.4 sessions crash silently when MCP tools have parameter-free schemas

2 participants