Skip to content

Draft protocol: complete result responses omit required resultType #1676

Description

@cclabadmin

Describe the bug

When the C# SDK server handles requests declaring the 2026-07-28 protocol version, the resultType field is absent from the completed (non-input-required) result objects I tested.

The Result base class declares a ResultType property that defaults to null. The SDK's JSON serialization options include DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull (McpJsonUtilities.cs), so the null default is omitted from the wire format. As a result, "resultType" is omitted unless a result-construction path explicitly assigns it. In the completed-response server paths tested below, no such assignment appears to happen, so ordinary "complete" responses are serialized without resultType.

The 2026-07-28 draft schema defines resultType as required on the base Result type and on the result schemas relevant to these server responses:

"Result": {
    "required": ["resultType"],
    "properties": {
        "resultType": {
            "description": "... Servers implementing this protocol version MUST include this field. ..."
        }
    }
}

The InputRequiredResult subclass correctly sets ResultType = "input_required" in its constructor, so MRTR input-required responses do serialize resultType. The issue is limited to default/"complete" result paths that do not explicitly set ResultType. Task-specific result DTOs appear to set their own ResultType values and are not the subject of this report.

This appears to be a 2026-07-28 schema-compliance issue. It is related to — but distinct from — the ttlMs/cacheScope omission tracked in #1650. That issue covers SEP-2549 caching fields; this one covers resultType, which applies to result objects generally, including non-cacheable ones.

Environment tested:

  • main snapshot from 2026-06-28: d6b1615988d3a73ffb653d08778b14fa695e1318
  • Servers used: tests/ModelContextProtocol.TestServer (stdio), tests/ModelContextProtocol.ConformanceServer (HTTP)
  • Transports: stdio and stateless Streamable HTTP

To Reproduce

Steps to reproduce the behavior:

  1. Start the C# SDK server with 2026-07-28 draft protocol support enabled.
  2. Over stdio, complete initialization and send a tools/list request.
  3. Inspect the JSON response — resultType is absent.
  4. Repeat with server/discover, prompts/list, resources/list, completion/complete, tools/call.
  5. Observe that resultType is absent from the tested completed responses.

Over stateless HTTP:

  1. Send a server/discover request with per-request _meta.
  2. Inspect the JSON-RPC result — resultType is absent.

Expected behavior

Completed result objects should include "resultType": "complete" in the serialized JSON when the negotiated protocol version is 2026-07-28. The schema's required constraint and the spec text "Servers implementing this protocol version MUST include this field" appear to apply to result objects generally, not only to input-required or task-specific result paths.

Observed behavior

Across both transports, the tested completed "complete" responses did not include resultType. The field does appear in InputRequiredResult responses, which appear to set ResultType = "input_required" in the constructor. Task-specific DTOs also appear to assign their own ResultType values.

This was observed consistently across the tested methods: server/discover, tools/list, tools/call, prompts/list, prompts/get, resources/list, resources/read, completion/complete.

Additional context

The likely cause appears to be the interaction of two pieces:

  1. Result.cs: ResultType defaults to null, and the docstring notes "Defaults to null, which is equivalent to "complete"."
  2. McpJsonUtilities.cs: DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull causes null values to be omitted from serialization.

One possible fix would be to default ResultType to "complete" rather than null, so that the existing WhenWritingNull setting still serializes it. As a possible reference point, the Python SDK appears to use this model-level approach: concrete Result subclasses declare result_type: ResultType = "complete" as a Pydantic field default.

Note: I read the spec's backward-compatibility clause ("the client MUST treat the absent field as "complete"") as a client-side resilience rule, not as a server-side exemption from the required constraint.

Related: #1650 tracks client-side tolerance for missing ttlMs/cacheScope fields. resultType is a separate concern that applies to result objects generally, including non-cacheable ones such as CompleteResult and CallToolResult.

Metadata

Metadata

Labels

P1Significant bug affecting many usersbugSomething isn't workingready for workHas enough information to start

Type

No type
No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions