Skip to content

Match protocol property names case-sensitively - #1934

Open
FenjuFu wants to merge 7 commits into
modelcontextprotocol:mainfrom
FenjuFu:fix/protocol-property-casing
Open

FenjuFu wants to merge 7 commits into
modelcontextprotocol:mainfrom
FenjuFu:fix/protocol-property-casing

Conversation

@FenjuFu

@FenjuFu FenjuFu commented Oct 15, 2026 •

Copy link
Copy Markdown

Fixes #1897

The JSON-RPC envelope converter matches jsonrpc/id/method/params/result/error exactly, but the typed contents of params and result were deserialized with the internal source-generated JsonContext, which is declared with JsonSerializerDefaults.Web and therefore binds property names case-insensitively. A tools/call request with params.Name was dispatched as if it were params.name, even though middleware or proxies inspecting params.name would not recognize it. Some transports and the Tasks extension also deserialized protocol types through the case-insensitive DefaultOptions, so fields such as error.Code or a task-based CallToolResult.IsError were bound regardless of casing.

Changes

  • McpJsonUtilities.JsonContext now sets PropertyNameCaseInsensitive = false. Protocol DTOs (requests, results and notifications, on both client and server) only bind the property names defined by the specification, consistent with the envelope.
  • New public McpJsonUtilities.ProtocolOptions: a cached, read-only copy of DefaultOptions with case-sensitive property matching. It is built from the same source-generated context, so trimming and Native AOT behave as before. Consumers deserializing protocol messages themselves can use it.
  • The protocol readers that went through options now use ProtocolOptions: the stdio client and server transports (StreamClientSessionTransport, StreamServerTransport), the ASP.NET Core Streamable HTTP and SSE request reader (StreamableHttpHandler), and every protocol type in ModelContextProtocol.Extensions.Tasks (task-based CallToolResult, InputRequest, IDictionary<string, InputResponse>, JsonRpcErrorDetail). The HTTP client transports already used the case-sensitive context.
  • McpJsonUtilities.DefaultOptions explicitly keeps PropertyNameCaseInsensitive = true. It is also used for user-defined types (tool, prompt and resource parameters, McpClientTool arguments, SendRequestAsync<TParameters, TResult>), so binding of user data is unchanged. Its XML docs now point to ProtocolOptions and state that protocol types deserialized through DefaultOptions keep case-insensitive matching.

As discussed in the issue, this is exact protocol-field matching, not new validation of non-canonical JSON:

tools/call params Before After
{"Name":"echo"} dispatched to echo name missing, JSON-RPC error, handler not invoked
{"name":"echo","Name":"other"} last value wins (other) echo
{"Name":"other","name":"echo"} echo echo
{"name":"echo","Arguments":{...}} arguments bound arguments not bound (null)
{"name":"echo","arguments":{"text":"a","Text":"B"}} both keys kept both keys kept (user data)

Fields with defaults keep their defaults when only an alternately cased key is present; for example a CallToolResult with only Content still ends up with an empty content list rather than failing.

Compatibility and release notes

  • McpApps.SerializerOptions now derives from ProtocolOptions, retaining the Apps source-generated context. Apps extension fields such as resourceUri and mimeTypes are case-sensitive, consistent with GetUiCapability. Alternate-cased fields are ignored, and canonical fields win in either property order. Correctly cased JSON, serialization output, strongly typed C# usage, DefaultOptions, and caller-supplied options remain unchanged. The Apps guide and XML documentation describe this intentional compatibility change.
  • The OAuth metadata types live in the same source-generated context, so their parsing also becomes case-sensitive. Worth mentioning in the release notes together with the protocol change.

Tests

  • Nested content reads now resolve annotation and icon metadata from the supplied serializer options, preserving opt-in case-insensitive matching. Actual client API tests also cover canonical tool-result fields through ordinary, immediate-task and polled-task paths.

  • McpServerTests.Call_Tool_Request_Params_Are_Matched_Case_Sensitively sends raw tools/call requests through the server: Name only (rejected, handler not invoked), alternately cased Arguments (not bound), canonical, both key orders with name and Name, and alternately cased keys inside arguments (preserved as distinct entries).

  • McpJsonUtilitiesTests: ProtocolOptions is a read-only singleton with case-sensitive matching; for a JSON-RPC error with both code/message and Code/Message, and for a CallToolResult with Content/IsError, ProtocolOptions binds only the canonical names while DefaultOptions keeps the previous behavior. DefaultOptions_MatchesPropertyNamesCaseInsensitively still covers user types.

  • StdioServerTransportTests.ReadMessagesAsync_Should_Match_Error_Properties_Case_Sensitively reads such an error line through the stdio transport.

  • McpServerToolTests.BindsNestedArgumentPropertiesCaseInsensitively invokes a tool whose parameter is a nested user DTO, bound from LOCATION/CITY, to confirm user-data binding is unchanged.

The server and stdio transport tests failed without the earlier core change. The earlier core revision was tested on net10.0 (Windows): ModelContextProtocol.Tests 2414 passed / 6 skipped and ModelContextProtocol.AspNetCore.Tests 636 passed / 31 skipped. Those counts are historical, not a full-suite run of the latest head.

For the earlier Apps follow-up (historical), dotnet test tests/ModelContextProtocol.Tests/ModelContextProtocol.Tests.csproj -f net10.0 --filter FullyQualifiedName~McpAppsTests -p:JsonSerializerIsReflectionEnabledByDefault=false passed all 39 tests on Windows using SDK 10.0.401 / runtime 10.0.12. The nine new cases cover canonical and alternate spellings, both property orders, agreement with GetUiCapability, and caller-controlled options. The net9.0 target compiled, but its test host could not start because the local .NET 9 runtime is absent. Other target frameworks, platforms and the full suite were not rerun locally for this follow-up. The latest upstream Build and Test, CodeQL and Markdown-link workflows await maintainer approval.

As noted in the issue, this is a subtle behavior change for peers that relied on alternate casing, so it probably belongs in a minor release.

cc @tarekgh for review

Latest review follow-up (2026-10-22)

Commit 0d92883b1a3778e6fe922e73556c0724ecbdffb0 resolves nested annotation and icon contracts from the converter's supplied options. Twelve regression cases cover canonical/alternate nested fields through DefaultOptions, ProtocolOptions, and explicit case-insensitive caller options. Six client API cases cover ordinary tool results, immediate Tasks results, and a working-then-completed Tasks poll, with canonical content/isError winning over alternate spellings in either property order.

Validation on Windows with SDK 10.0.101:

  • The 18-case regression selection on the previous head reproduces four nested-options failures; the six client reader cases already pass. After the fix, the broader net9.0 selection passes 191 cases with reflection disabled by the repository's target configuration.
  • Complete ModelContextProtocol.Tests, Execution!=Manual: net10.0 passes 2,453 cases with 4 skips. The net9.0 initial run passes 2,452, fails one external Node test-server launch, and skips 4. After installing the pinned npm dependencies, the failed test's two-case method passes on net9.0. This is a targeted rerun, not a second complete net9.0 suite.
  • The previous Windows CI failure families were selected locally on net10.0: 5 pass, 1 expected skip, after setting NO_PROXY for loopback to keep the in-memory transport off the local proxy. This does not establish the cause of the earlier upstream failures or replace the complete ASP.NET Core suite.
  • net8.0, .NET Framework, other operating systems, and the complete ASP.NET Core suite were not run for this follow-up. Earlier test counts and model/runtime details above are historical. New-head upstream workflows currently require maintainer approval; local results do not imply CI success.

Note

This follow-up summary and the code/tests were prepared with AI assistance. The reported checks were executed locally.

Standalone-context compatibility follow-up (2026-10-23)

Commit f7017ba6d4d6a4e480ba746bea7b7c0cf461b8f5 restores nested content reads for standalone source-generated contexts that register only ContentBlock or CallToolResult. Caller resolvers take precedence over an SDK-generated fallback, including nested audience and icon-size metadata. Caller settings/converters are copied, the caller's options are not modified, and only read-only options are cached; direct calls with mutable options observe later changes.

Eighteen regression cases cover standalone contexts, canonical/alternate nested casing, caller contracts, and mutable options. The initial 17-case selection on the previous head produced 16 missing-metadata failures and one passing caller-contract control. Final-source Windows/SDK 10.0.101 validation passes 160 focused cases on each of net9.0 and net10.0, with System.Text.Json reflection disabled for net9.0.

The complete final-source project suite, full ASP.NET Core suite, other frameworks/platforms, and Native AOT publish/link were not completed for this follow-up. Earlier full-suite counts above are historical. Local results do not establish upstream CI success. This follow-up and its tests were prepared with AI assistance; the reported checks were executed locally.

@tarekgh tarekgh 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.

Thanks for the fix. This approach makes sense: match protocol-defined fields exactly while preserving the existing DefaultOptions behavior for user-defined data.

I checked TypeScript, Python, Go, Rust, and Ruby. None binds params.Name as the canonical tool name, and canonical name wins when both spellings are present, regardless of order. TypeScript and Python runtime probes confirmed this in both protocol eras. This supports the change here.

The important distinction is exact matching, not blanket rejection of noncanonical JSON. Peers differ in required-field validation, defaults, and raw/custom-result handling.

Looks good overall. A few non-blocking suggestions:

  • Add a client-side result test covering alternate-cased optional fields such as Content and IsError, and a nested user-DTO-through-tool test confirming that its existing binding behavior is preserved.
  • Track the remaining internal casing differences separately, particularly task-based CallToolResult deserialization and transport-level JsonRpcErrorDetail parsing. These do not bypass the tool-name fix.
  • Include the behavior change in the planned minor-release notes. The shared context also changes OAuth model parsing, so that compatibility impact should be mentioned.

CC @jeffhandley @halter73

Comment thread src/ModelContextProtocol.Core/McpJsonUtilities.cs Outdated
@halter73

Copy link
Copy Markdown
Contributor

I'd rather finish the protocol/user-data split here than leave the remaining readers as follow-ups. Tasks tool results still bind IsError, and stream/ASP.NET error readers still bind Code/Message.

We could add cached, case-sensitive McpJsonUtilities.ProtocolOptions for protocol deserialization throughout the SDK, keeping DefaultOptions and caller-supplied options for user-defined data. If we want to avoid new public API, a shared internal helper under src/Common would work too, but a public preset would also be useful to consumers deserializing protocol messages themselves.

@tarekgh I know you suggested tracking these separately, but fixing the remaining protocol-field casing inconsistencies together seems manageable in one PR. Does that sound good to you?

@tarekgh

tarekgh commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

@halter73 Yes, that sounds good to me. My suggestion to track these separately was to keep the original fix focused, but I agree that finishing the protocol/user-data split in this PR is cleaner.

A cached, read-only McpJsonUtilities.ProtocolOptions preset seems useful both internally and for consumers deserializing protocol messages themselves. It would also give extension packages a consistent contract without needing access to the internal context.

Let's use it for the remaining protocol readers, including task-based tool results and stream/ASP.NET error details, while retaining DefaultOptions and caller-supplied options for user-defined data. We should preserve source-generated/Native AOT support and add regression coverage for those paths, along with confirmation that nested user-data binding remains unchanged.

That would let us document the protocol boundary consistently rather than carrying the known internal exceptions.

@FenjuFu Would you be able to include these changes in this PR, or would you like help with the implementation or tests?

Protocol DTOs were deserialized with the source-generated context, which uses the web defaults and binds property names case-insensitively, while the JSON-RPC envelope converter matches its properties exactly. A request such as tools/call with params.Name was therefore dispatched even though middleware inspecting params.name would not recognize it.

The source-generated context now matches property names case-sensitively. DefaultOptions keeps case-insensitive matching because it is also used for user-defined types such as tool parameters.

Fixes modelcontextprotocol#1897

Signed-off-by: FenjuFu <fufenjupku@gmail.com>
Add McpJsonUtilities.ProtocolOptions, a cached read-only copy of DefaultOptions that matches property names case-sensitively, and use it wherever the SDK deserializes protocol messages through options rather than the source-generated context: the stdio client and server transports, the ASP.NET Core Streamable HTTP and SSE request reader, and the Tasks extension (task-based CallToolResult, InputRequest, InputResponse and error details).

DefaultOptions and caller-supplied options are still used for user-defined data. Its docs now point to ProtocolOptions and state that protocol types deserialized through DefaultOptions keep case-insensitive matching.

Signed-off-by: FenjuFu <fufenjupku@gmail.com>
@FenjuFu
FenjuFu force-pushed the fix/protocol-property-casing branch from e09ea77 to 39ec6e5 Compare October 18, 2026 07:16
@FenjuFu

FenjuFu commented Oct 18, 2026

Copy link
Copy Markdown
Author

@halter73 @tarekgh Sure, done in 39ec6e5 (also rebased onto main):

  • Added McpJsonUtilities.ProtocolOptions, a cached read-only copy of DefaultOptions with case-sensitive matching. It comes from the same source-generated context, so AOT/trimming is unaffected.
  • Switched the remaining protocol readers to it: the stdio client/server transports, the ASP.NET Core Streamable HTTP/SSE request reader, and all protocol types in the Tasks extension (task-based CallToolResult, InputRequest/InputResponse, error details).
  • DefaultOptions and caller-supplied options stay in place for user data. Its remarks now describe the boundary as suggested in the inline comment.
  • Tests for alternately cased error.Code/Message and CallToolResult Content/IsError (ProtocolOptions vs DefaultOptions), the stdio transport path, and a nested user DTO through a tool.

Two things I left for you to decide: McpApps.SerializerOptions is public and derived from DefaultOptions, so I didn't change it; and since the OAuth metadata types share the context, their parsing becomes case-sensitive too, which belongs in the release notes. The PR description is updated.

net10.0 locally: core 2414 passed / 6 skipped, AspNetCore 636 passed / 31 skipped, 0 failed.

@tarekgh

tarekgh commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Thanks, this addresses the earlier Tasks and transport-reader concerns. I also checked the actual immediate and polled Tasks client paths with mixed-case fields in both property orders, and canonical fields are now handled consistently.

One issue needs fixing before merging: BindsNestedArgumentPropertiesCaseInsensitively fails on the .NET 9 test target, where reflection-based JSON serialization is intentionally disabled. McpServerTool.Create throws NotSupportedException because no JsonTypeInfo is registered for WeatherRequest, so the test never reaches its binding assertions.

Could you add WeatherRequest to the existing test-generated JSON context, append that context to a clone of McpJsonUtilities.DefaultOptions, and pass those options through McpServerToolCreateOptions.SerializerOptions? That follows the existing Person test pattern. I verified this approach passes with reflection disabled.

On the two remaining decisions:

  • Apps: McpApps.SerializerOptions still binds protocol-defined extension fields case-insensitively, while GetUiCapability already parses them case-sensitively. I lean toward deriving the Apps preset from ProtocolOptions for consistency, unless we deliberately want to preserve the public preset's existing behavior. If we preserve it, let's document that exception explicitly.
  • OAuth: Agreed that the stricter metadata/token parsing should be included in the release notes alongside the protocol change.

AI assistance: Codex prepared this test-only change and validation summary.

Signed-off-by: FenjuFu <92919259+FenjuFu@users.noreply.github.com>
@FenjuFu

FenjuFu commented Oct 20, 2026

Copy link
Copy Markdown
Author

@tarekgh Addressed in de85bc8.

WeatherRequest is now registered in the existing JsonContext2. The test appends that context to a clone of McpJsonUtilities.DefaultOptions and supplies the clone through McpServerToolCreateOptions.SerializerOptions, following the existing Person pattern. This commit changes only the test; it leaves the production presets unchanged.

Validation on Windows with .NET SDK 10.0.101:

  • The exact previous head, 39ec6e5, reproduces the .NET 9 failure: NotSupportedException for missing WeatherRequest serialization metadata.
  • The updated McpServerToolTests class passes all 90 cases on both net9.0 and net10.0.
  • The entire ModelContextProtocol.Tests project, using the repository's Execution!=Manual filter, passes on both targets: 2,414 passed, 0 failed, 4 skipped per target. Other test projects, net8.0, .NET Framework, and other operating systems were not rerun for this test-only update.

The Apps preset and OAuth release-note decisions remain for the maintainers; this commit is limited to the missing test context.

Note

This comment and the test-only change were generated with AI assistance. The results above come from actual local test runs.

@tarekgh

tarekgh commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

@FenjuFu Thanks for fixing the test serialization metadata.

For Apps, I recommend deriving McpApps.SerializerOptions from McpJsonUtilities.ProtocolOptions, while retaining the existing Apps source-generated context registration. Fields such as mimeTypes and resourceUri are extension-defined protocol fields, not user-defined tool data. This would make the public preset consistent with GetUiCapability, which already matches fields case-sensitively, and with the canonical-field handling in the TypeScript Apps and Python implementations.

There is a compatibility impact: consumers deserializing alternate-cased JSON through the Apps preset would no longer have those fields bound. For example, ResourceUri would be ignored rather than treated as resourceUri. Correctly cased JSON, normal serialization output, and strongly typed C# usage would remain unchanged. I think correcting this inconsistency now is preferable to retaining two different interpretations of the same Apps JSON, but we should document it as an intentional behavior change rather than treating it as an incidental cleanup.

Please include regression coverage for canonical and alternate-cased Apps fields, including both property orders when both spellings are present, and confirm that the preset and GetUiCapability agree. The release notes should mention the Apps compatibility impact alongside the core protocol and OAuth parsing changes. DefaultOptions and caller-supplied options should remain unchanged for user-defined data.

@halter73

Copy link
Copy Markdown
Contributor

I agree. Apps fields are protocol-defined, so McpApps.SerializerOptions should derive from ProtocolOptions too. Being inside _meta doesn’t make those fields user-defined data.

@FenjuFu

FenjuFu commented Oct 21, 2026

Copy link
Copy Markdown
Author

@tarekgh @halter73 Addressed in ab0f8d8. McpApps.SerializerOptions now derives from ProtocolOptions while retaining the Apps source-generated context.

Added nine regression cases covering resourceUri and mimeTypes: canonical spelling, alternate spelling, and both property orders when both are present. The capability cases also verify agreement with GetUiCapability. A caller-controlled options test confirms that callers can still opt into case-insensitive matching and that DefaultOptions is unchanged. The Apps guide, XML docs and PR compatibility notes now describe the intentional behavior change alongside the core protocol and OAuth parsing changes.

On Windows, all 39 McpAppsTests pass on net10.0 with reflection-based serialization explicitly disabled (SDK 10.0.401 / runtime 10.0.12). The net9.0 target compiled, but its test host could not start because that runtime is not installed locally. The new upstream Build and Test, CodeQL and Markdown-link runs are action_required; could a maintainer approve them?

Copilot AI 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.

🔵 Needs a closer look

The compatibility changes need maintainer validation, and nested converters still introduce an unintended regression through case-insensitive options.

1 open issue
What changed in this PR

Aligns protocol property matching with the case-sensitive JSON-RPC envelope, addressing #1897 while aiming to preserve user-data binding.

Changes:

  • Adds ProtocolOptions and makes generated protocol contracts case-sensitive.
  • Updates transport, Tasks, and Apps serialization paths.
  • Adds casing regression tests and documents Apps compatibility changes.
File Description
tests/​ModelContextProtocol.Tests/​Transport/​StdioServerTransportTests.cs Tests strict error-field matching.
tests/​ModelContextProtocol.Tests/​Server/​McpServerToolTests.cs Tests case-insensitive nested user arguments.
tests/​ModelContextProtocol.Tests/​Server/​McpServerTests.cs Tests request-field casing and argument preservation.
tests/​ModelContextProtocol.Tests/​Server/​McpAppsTests.cs Tests Apps casing and caller options.
tests/​ModelContextProtocol.Tests/​McpJsonUtilitiesTests.cs Tests serializer presets and protocol fields.
src/​ModelContextProtocol.Extensions.Tasks/​Server/​McpTasksServerExtensions.cs Uses protocol options for task requests.
src/​ModelContextProtocol.Extensions.Tasks/​Server/​McpTasksBuilderExtensions.cs Updates task results and input-response handling.
src/​ModelContextProtocol.Extensions.Tasks/​Protocol/​TaskStatusNotificationParams.cs Updates nested input-request serialization.
src/​ModelContextProtocol.Extensions.Tasks/​Protocol/​GetTaskResult.cs Updates nested input-request serialization.
src/​ModelContextProtocol.Extensions.Tasks/​Client/​McpTasksClientExtensions.cs Uses protocol options for task exchanges.
src/​ModelContextProtocol.Extensions.Apps/​Server/​McpApps.cs Makes the Apps preset case-sensitive.
src/​ModelContextProtocol.Core/​Server/​StreamServerTransport.cs Uses strict protocol deserialization.
src/​ModelContextProtocol.Core/​McpJsonUtilities.cs Adds protocol options and strict generated contracts.
src/​ModelContextProtocol.Core/​Client/​StreamClientSessionTransport.cs Uses strict protocol deserialization.
src/​ModelContextProtocol.AspNetCore/​StreamableHttpHandler.cs Selects protocol options for HTTP handling.
docs/​concepts/​apps/​apps.md Documents Apps casing compatibility.

Review effort: Balanced

Comment thread src/ModelContextProtocol.Core/McpJsonUtilities.cs
@tarekgh

tarekgh commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

One small, non-blocking follow-up to the earlier client-side test suggestion: the new ProtocolOptions test covers serializer behavior, but not the client reader wiring. Please add regression coverage through the actual client API using a controlled response containing canonical and alternate-cased content/Content and isError/IsError fields, verifying that canonical fields win in either property order. Covering the ordinary tool-result path and the immediate/polled Tasks paths would help prevent these readers from accidentally reverting to case-insensitive options.

Add client API regression coverage for canonical tool-result fields in ordinary, immediate-task and polled-task responses.

AI assistance was used to prepare this change and its tests.

Signed-off-by: FenjuFu <92919259+FenjuFu@users.noreply.github.com>
@FenjuFu

FenjuFu commented Oct 22, 2026

Copy link
Copy Markdown
Author

Addressed in 0d92883.

Nested annotations and icons now resolve their contracts from the converter's supplied options. The 12 new cases cover DefaultOptions, ProtocolOptions and explicit case-insensitive caller options; four reproduce the previous regression.

The client follow-up adds six cases through the actual CallToolAsync, immediate CallToolAsTaskAsync and working/completed CallToolWithPollingAsync paths. Canonical content/isError win in both property orders.

Windows validation (SDK 10.0.101): the related net9.0 selection passes 191 cases; the full net10.0 ModelContextProtocol.Tests project passes 2,453 with 4 skips. The first full net9.0 run had one missing Node test-server launch (2,452 pass / 1 fail / 4 skip); after installing pinned npm dependencies, that method's two cases pass. The selected previous ASP.NET Core CI failure families pass locally (5 pass / 1 skip) with loopback proxy bypass. Other targets/OS and the full ASP.NET Core suite were not rerun.

The three new-head workflows require maintainer approval; these local results do not establish why the earlier Windows CI failed.

Note

This reply and follow-up code/tests were generated with AI assistance. Test counts are from actual local runs.

Copilot AI 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.

🟡 Changes recommended

Nested content reads can fail for previously supported standalone source-generated contexts that lack icon or annotation metadata.

2 open issues

Review effort: Balanced

while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
{
icons.Add(JsonSerializer.Deserialize(ref reader, McpJsonUtilities.JsonContext.Default.Icon) ??
icons.Add(JsonSerializer.Deserialize(ref reader, options.GetTypeInfo<Icon>()) ??

@tarekgh tarekgh Oct 22, 2026 •

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.

@FenjuFu Could we please address it before merging? This is an application compatibility regression: existing applications using standalone source-generated contexts can now fail to deserialize correctly cased content containing annotations or icons. I reproduced these failures on the current head, while the same cases pass on the previous commit. Preserving caller settings while providing an SDK-generated metadata fallback, with standalone-context regression coverage, would avoid breaking those applications.

Resolve nested content metadata through caller-first resolvers with an SDK-generated fallback, preserving caller settings and mutable direct-converter reads.

Add standalone-context, nested collection, caller-contract precedence, and mutable-options regression coverage.

AI assistance was used to prepare this change and its tests.

Signed-off-by: FenjuFu <92919259+FenjuFu@users.noreply.github.com>
@FenjuFu

FenjuFu commented Oct 23, 2026

Copy link
Copy Markdown
Author

@tarekgh Fixed in f7017ba.

Nested reads now keep caller resolvers first and append the SDK's source-generated metadata as a fallback, including Annotations.Audience and Icon.Sizes. Caller settings and custom contracts are preserved without changing the original options. Only read-only options are cached; direct converter calls still observe changes to mutable options.

Added 18 regression cases covering standalone contexts registering only ContentBlock or CallToolResult, both nested casing modes, caller-contract precedence, and mutable options. The initial 17-case selection on the previous head had 16 missing-metadata failures and one passing caller-contract control.

Final-source validation on Windows/SDK 10.0.101: 160 focused cases pass on each of net9.0 and net10.0; the net9.0 host has System.Text.Json reflection disabled. The complete final-source suite, full ASP.NET Core suite, other frameworks/platforms, and Native AOT publish/link were not completed for this follow-up. Upstream CI remains separate from these local results.

This follow-up and its tests were prepared with AI assistance; the reported checks were executed locally.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSON property-name casing is inconsistent between JSON-RPC envelope and request params

5 participants