Skip to content

fix(coderd/x/chatd): normalize nil MCP tool properties to empty object#27438

Open
rodmk wants to merge 1 commit into
coder:mainfrom
rodmk:fix/openai-nil-properties-zero-arg-mcp
Open

fix(coderd/x/chatd): normalize nil MCP tool properties to empty object#27438
rodmk wants to merge 1 commit into
coder:mainfrom
rodmk:fix/openai-nil-properties-zero-arg-mcp

Conversation

@rodmk

@rodmk rodmk commented Jul 22, 2026

Copy link
Copy Markdown

Zero-argument MCP tools whose input schema omits properties reach the OpenAI function schema as "properties": null, which OpenAI rejects with Invalid schema for function '<tool>': None is not of type 'object' (HTTP 400) before the tool executes. Anthropic-backed chats are unaffected because they do not strict-validate the schema, so the same tool works on Sonnet but fails on GPT.

This is the object-valued counterpart of the already-merged required fix (#23538): an absent or nil parameter map is passed straight through and serializes to null instead of {}.

Fix

Coerce a nil parameter map to an explicit empty object in two places:

  • buildToolDefinitions (coderd/x/chatd/chatloop/chatloop.go) — the provider boundary that every AgentTool (MCP, built-in, and dynamic tools) passes through.
  • mcpToolWrapper.Info (coderd/x/chatd/mcpclient/mcpclient.go) — normalizes at the source and additionally covers the nested schema built when model_intent wrapping is enabled, which the boundary fix cannot reach.

This mirrors the guard already merged for the sibling required field.

Refs #27252

Verification

Added regression tests in both packages using a zero-argument tool whose schema omits properties (plain and model_intent variants). Each test fails without the guard (parameters come back nil, serializing to null) and passes with it.

$ go test ./coderd/x/chatd/mcpclient/ -run TestConnectAll_NilParametersBecomesEmptyObject -v
--- PASS: TestConnectAll_NilParametersBecomesEmptyObject_ModelIntent (0.00s)
--- PASS: TestConnectAll_NilParametersBecomesEmptyObject (0.00s)
ok  	github.com/coder/coder/v2/coderd/x/chatd/mcpclient

$ go test ./coderd/x/chatd/chatloop/ -run TestBuildToolDefinitions_NilParametersBecomesEmptyObject -v
--- PASS: TestBuildToolDefinitions_NilParametersBecomesEmptyObject (0.00s)
ok  	github.com/coder/coder/v2/coderd/x/chatd/chatloop

Confirmed the same two tests fail on the unpatched tree (Parameters should never be nil / properties should never be nil). golangci-lint run, paralleltestctx, and go vet are clean on both packages; full mcpclient and chatloop package suites pass, including under -race.

AI disclosure

This change was authored with AI assistance (Claude Code) per the repository's AI contribution guidelines. The diff, test design, and verification above were reviewed manually by the submitter.

🤖 Generated with Claude Code

Zero-argument MCP tools whose input schema omits "properties" reach the
OpenAI function schema as "properties": null, which OpenAI rejects with
"None is not of type 'object'" (HTTP 400). Anthropic-backed chats are
unaffected because they do not strict-validate the schema.

Coerce a nil parameter map to an explicit empty object at the provider
boundary in buildToolDefinitions and at the source in the MCP tool
wrapper's Info, covering both the plain and model_intent-wrapped schema
paths. This mirrors the guard already merged for the sibling "required"
field.

Refs coder#27252

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added the community Pull Requests and issues created by the community. label Jul 22, 2026
@rodmk
rodmk marked this pull request as ready for review July 22, 2026 22:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community Pull Requests and issues created by the community.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant