Build1 distinct publisher3 min readUpdated
The new tools spec lets structuredContent be any JSON value. A client hard-coded to read structuredContent.result keeps compiling, keeps passing, and starts finding nothing on the wire.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
The MCP tools specification dated 2026-07-28 allows `structuredContent` to hold any JSON value: object, array, string, number, Boolean, or null, and an `outputSchema` may describe an array or a primitive at its root [1]. That retires the object-only convention of 2025-11-25, under which a tool returning `string[]` had to ship an envelope such as `{"structuredContent": {"result": ["starter", "growth", "enterprise"]}}` [2], so a client that unconditionally reaches for `structuredContent.result` will find nothing there once both ends negotiate the newer version [3].
The reason this is dangerous rather than merely annoying is that nothing in your codebase moves. The C# method still returns `string[]`. A unit test against that return type cannot tell you what `tools/list` advertised or what `tools/call` actually carried, which is why the author of a walkthrough published on dev.to treats the change as a contract to be verified at the transport boundary [4].
The stable MCP C# SDK performs the negotiation for you: the v2.0.0 release notes call out direct non-object tool results, and the v2.2.0 tools guide documents `UseStructuredContent` [5]. For a down-level client the SDK still emits the compatibility envelope, with no second handler and no hand-written version switch [6]. Convenient, and exactly why the difference is invisible from inside your own process. The shape on the wire is a property of the peer, not of your type.
The verifier in the post is small. It pins `ModelContextProtocol` 2.2.0 and targets .NET 10 [7], registers one array tool (`list_tiers`) and one scalar tool (`count_tiers`) with `UseStructuredContent = true`, which tells the SDK to generate the output schema and serialize the return value into `structuredContent` [8]. Two `System.IO.Pipelines.Pipe` instances connect an `McpClient` to an `McpServer`, so nothing opens a port and no model is involved, but discovery and calls still cross the SDK's stream transport [9]. One pair is created with `ProtocolVersion` "2026-07-28" and a fresh pair with "2025-11-25" [10]. Separate sessions are the load-bearing detail: the negotiated protocol belongs to a connection, so flipping an option after discovery would not exercise the real compatibility path [11]. Two tools across two sessions is four calls, which is the whole budget for covering both wire shapes [19].
The modern session asserts that the advertised schema type is `array`, that the returned `StructuredContent` has `ValueKind` `Array`, and that a legacy-style read of a root object with `result` fails [12]. That last assertion is a negative control modelling the parser being removed; without it, an accidental wrapper can slip back in while a values-only test stays green [13]. The legacy session asserts the opposite shape [14], and both check the three tier values plus the text content fallback that the specification recommends when structured content is returned [15].
Two consequences worth carrying into your own code. Branch from the discovered schema rather than from the tool name or a local C# type, because the server may be implemented in another language [16]. And assert discovery and invocation together, which is what catches the asymmetric bug: a server advertising an array schema but returning a wrapped object, or the reverse, can carry the right values while violating its declared contract [17].
What to watch: whether the SDKs you depend on in other languages also emit the down-level envelope, since the source only documents that behaviour for the C# SDK [6], and whether your own servers still conform to the schemas they advertise, because the spec puts that obligation on the server and asks clients to validate [18].
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
The MCP 2026-07-28 tools specification allows structuredContent to contain any JSON value: object, array, string, number, Boolean, or null, and an outputSchema may likewise describe an array or primitive at its root.
Under MCP 2025-11-25, the convention was object-only: a tool returning string[] needed an object envelope such as {"structuredContent": {"result": ["starter", "growth", "enterprise"]}}, while the modern form is the bare array value.
A client written around the older wire shape may always reach for structuredContent.result, but once both sides negotiate MCP 2026-07-28 an array is an array and a scalar is a scalar, with no required wrapper to unwrap.
The author treats the change as a contract change worth testing at the transport boundary, because a unit test against the C# return type cannot reveal what tools/list advertised or what tools/call actually carried.
The stable MCP C# SDK handles the version negotiation; its v2.0.0 release notes call out direct non-object tool results, and the current v2.2.0 tools guide documents UseStructuredContent.
For a down-level client, the C# SDK still emits the compatibility envelope, so the author needs no second handler and no hand-written version switch.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Reproducible first-party demo, single unreplicated source
The mechanism claims are unusually well specified for a blog post: named package version, named tools, explicit assertion code for both protocol sessions, a stated run result, and pointers to a sample branch and merged pull request. But everything comes from one self-published author, the cited specification text and SDK release notes are summarized rather than quoted from the primary documents in this cluster, and no second publisher or independent run corroborates the behaviour.
Toolchain support shipped; field usage undocumented
There is real evidence that the capability exists in shipped software: a protocol revision defining the new structuredContent freedom, an SDK release line (v2.0.0 through the pinned stable 2.2.0) that implements it and still emits the compatibility envelope for down-level clients, and one working sample. There is no evidence in the supplied material of production deployments, third-party client support, or how often 2026-07-28 is actually negotiated; the author explicitly says a third-party client may lag the protocol or ignore structured content entirely.
Slightly sharper headline than the scoped demo supports
The framing that unit tests 'will not notice' and that a hard-coded client 'starts finding nothing on the wire' is broader than what is demonstrated, which is one author's offline harness against one SDK version. Against that, the body is self-limiting: it states the verifier proves only official MCP C# SDK 2.2.0 behaviour over its stream transport, warns that hosts may lag or apply narrower schema profiles, notes output schemas check shape rather than business meaning, and suggests a response record may still be clearer than a bare array. The residual gap is small.
Personal portfolio promotion, no disclosed commercial stake
The post is a self-published practitioner tutorial on dev.to that drives readers to the author's own sample repository and merged pull request, so there is a visibility and credibility incentive. No vendor sponsorship, employer relationship, product being sold, or pricing interest is disclosed or apparent, and the author documents a limitation section that works against promotional framing.
Mechanism credible, breadth unverified
Confidence is moderate: the code-level claims are specific, internally consistent, and cheap for a reader to reproduce, which supports the narrow finding. It is held down by a single-publisher cluster, no independent replication, primary specification and release-note text present only as the author's paraphrase, and no data at all on how the wider MCP ecosystem behaves.
build
x-mcp-header is a wire contract: one bad annotation invalidates the whole MCP tool1 distinct publisher
build
MCP Goes Stateless, and Your Session-Bound Servers Break1 distinct publisher
build
Rate limit your MCP servers, because a retrying agent turns one error into a billing incident1 distinct publisher
build
The only way to prove a contract test can fail is to ship a server that lies1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 20, 2026