fix(mcp): return typed structured results from every tool - #750
Conversation
PR Summary by QodoEnable structured content for all MCP tools
AI Description
Diagram
High-Level Assessment
Files changed (2)
|
Code Review by Qodo
1.
|
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 532d08b |
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit c1ca79d |
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 4ca5fde |
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 8aed4b1 |
|
Reviewed (Claude): verified end-to-end over stdio that all 26 tools advertise an object outputSchema and return structuredContent alongside the unchanged text; contract test now reads the real WithToolsFromAssembly registration. Qodo-clean on 8aed4b1, CI green — ready for review. Note: conflicts textually with #744 (every attribute line) and #738; trivial rebase for whichever merges later. |
SDK 2.2 serializes POCO/list returns as a single text ContentBlock unless the flag is set. Clients then get outputSchema / structuredContent instead. Co-authored-by: Tyler Kron <tylerkron@gmail.com>
Mirror WithToolsFromAssembly so a tool added in a new class can't skip it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…gistration Replaces the hand-rolled reflection scan, whose flags could only ever approximate the SDK's discovery rules, with the SDK registration itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
/agentic_review |
8aed4b1 to
8a36cc6
Compare
|
Code review by qodo was updated up to the latest commit 8a36cc6 |
Resolve DaqifiTools.cs against #744: keep main's ReadOnly/Destructive/OpenWorld hints on every tool and add UseStructuredContent = true alongside them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hema The SDK's default serializer drops null properties, but the outputSchema it generates lists every record property as required (a nullable one as ["T","null"]). With UseStructuredContent on, 14 of the 26 tools could return a result with a required key missing - get_server_info under --no-version-check, configure_*_channels whenever the rate was not lowered, list_analog_outputs before a write, and so on - and a client that validates results against the schema rejects the call. Register tools through a shared WithDaqifiTools() that passes the SDK defaults with DefaultIgnoreCondition = Never, so null fields are written in both structuredContent and the text block. tools/list is byte-identical. Both contract-test harnesses now register through the same call Program.cs makes, and a new test runs get_server_info through the SDK's invoke path and checks every required key is present. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit ef38167 |
|
Qodo-clean, CI green — ready for review |
Resolve DaqifiTools.cs against #761: keep main's [McpServerTool] lines and trimmed [Description] text on every tool, and add UseStructuredContent = true. Program.cs auto-merged: main's ServerInstructions plus WithDaqifiTools(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
/agentic_review |
|
Qodo-clean, CI green — ready for review |
What was wrong
Every tool returned its result only as a blob of JSON text. MCP clients that consume typed results (scripts, agent frameworks, anything that wants to read
sampleRateHzwithout parsing prose) got nooutputSchemaintools/listand nostructuredContentin the call result. They had to guess the shape of each response and parse the text themselves.How it was fixed
Every tool now sets
UseStructuredContent = true, next to the safety hints from #744 (descriptions are #761's, unchanged). The server then:outputSchemafor each tool intools/list. The schema is always an object: the spec requires that, so list and string results are wrapped as{"result": ...}.structuredContentalongside the same JSON text block.Null fields are now written out (
"latestVersion": null) rather than omitted. The generated schema lists every record property as required, with nullable ones typed["T","null"], but the SDK's default serializer drops null properties. Without this change, 13 of the 26 tools could return a result with a required key missing. Examples:get_server_infounder--no-version-check,configure_analog_channelswhenever the rate was not lowered, andlist_analog_outputsbefore any write. A client that validates results against the schema, such as the official TypeScript SDK client, would reject those calls. Tools are now registered through a sharedWithDaqifiTools()that passes the SDK's default serializer options withDefaultIgnoreCondition = Never. This one setting covers bothstructuredContentand the text block.Tests
ToolStructuredContentContractTestsbuilds the tools the way the server does and checks that each one advertises an object output schema. It fails if a tool is added without a row in its table.NullField_IsStillWritten_SoTheResultMatchesItsOutputSchemarunsget_server_info(version check off) through the SDK's own invoke path. It asserts that every key the schema requires is instructuredContentand thatlatestVersionis an explicit null. Setting the ignore condition back to the SDK default makes this test fail.ToolAnnotationContractTests) now register throughWithDaqifiTools(), the same call Program.cs makes.Verification
outputSchema.get_server_inforeturnsstructuredContentthat matches its schema, includinglatestVersion: null.list_connected_devicesreturns{"result": []}.disconnect_devicereturns{"result": "..."}plus the plain-text block. Errors still come back asisErrortext with nostructuredContent.tools/listis byte-identical with and without the serializer change. Input and output schemas are unaffected; only result serialization changes.Notes
"field": nullin the text block instead of being left out. The tool descriptions already say these fields "come back null", so the text now matches them.tools/listgrows by ~10 KB (the schemas). Clients that forward output schemas to the model pay that in context once per session.tools/listadvertises. (fix(mcp): drop dead digital sampleRateAdjustedFromHz #789 already removed the always-nullConfigureDigitalResult.SampleRateAdjustedFromHz, so that field is never advertised.)UseStructuredContent = trueand a row in both contract-test tables; the completeness tests fail otherwise.🤖 Generated with Claude Code