Skip to content

Approach 2: MCP/Approvals/Tool Contents stabilization#7299

Open
jozkee wants to merge 15 commits intodotnet:mainfrom
jozkee:tool_approval_content2
Open

Approach 2: MCP/Approvals/Tool Contents stabilization#7299
jozkee wants to merge 15 commits intodotnet:mainfrom
jozkee:tool_approval_content2

Conversation

@jozkee
Copy link
Member

@jozkee jozkee commented Feb 13, 2026

Alternative to #7245.

Benefits:

  • Approval extensibility: The MCP approval pattern is likely to expand to other hosted tools and the hierarchy needs to support that.
  • Don't leak Function-specific members, today only applies to Exception.
  • Keeps doors open:
    • MCP content can move under FunctionCallContent later.
    • Base Hosted content for MCP, CodeInt and ImageGen (mutually exclusive with FCC move, though).

Cost:

  • Reconcile Outputs with Result (can be deferred, non-breaking in the future).
  • Unify CallId nullability and name
    • ImageGen uses ImageId
    • Both ImageGen and CodeInterpreter are nullable get; set; but this can be changed to non-nullable get-only like functions.

@jozkee jozkee requested a review from a team as a code owner February 13, 2026 18:12
@github-actions github-actions bot added the area-ai Microsoft.Extensions.AI libraries label Feb 13, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR stabilizes MCP/tool-calling and approval-related content types across the Microsoft.Extensions.AI stack, updating both runtime code and tests to use the new ToolCallContent / ToolResultContent and ToolApproval* abstractions (and related serialization/telemetry plumbing).

Changes:

  • Introduces/standardizes new base content types (ToolCallContent, ToolResultContent, InputRequestContent, InputResponseContent) and migrates approvals to ToolApprovalRequestContent / ToolApprovalResponseContent.
  • Updates OpenAI Responses conversion + OpenTelemetry serialization to the new content model (e.g., Outputs vs Output, MCP call Name vs ToolName, image/code tool call IDs).
  • Refreshes and expands test coverage for the new content hierarchy, serialization roundtrips, and mixed MCP/function approval flows.

Reviewed changes

Copilot reviewed 70 out of 70 changed files in this pull request and generated no comments.

Show a summary per file
File Description
test/Libraries/Microsoft.Extensions.AI.Tests/ChatReduction/SummarizingChatReducerTests.cs Updates reducer tests to use InputRequestContent/InputResponseContent test types.
test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs Updates serialization expectations for tool call/result + approvals; aligns with constructor-based IDs and Outputs.
test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/ImageGeneratingChatClientTests.cs Switches image tool ID assertions from ImageId to CallId.
test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/FunctionInvokingChatClientTests.cs Updates approval assertions to ToolApprovalRequestContent/ToolApprovalResponseContent and ToolCall.
test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/FunctionInvokingChatClientApprovalsTests.cs Adds mixed MCP/function approval scenarios; updates approval types and introduces input cloning helper for streaming/non-streaming parity.
test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIResponseClientTests.cs Updates MCP naming/output/approval assertions and adds error + bearer-token extraction tests.
test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIResponseClientIntegrationTests.cs Updates integration tests to use ToolApprovalRequestContent and CreateResponse; switches MCP tool name/output properties.
test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIConversionTests.cs Updates MCP tool auth/header tests; expands streaming update conversion coverage (including approval correlation behavior).
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Tools/HostedMcpServerToolTests.cs Adjusts tests for nullable Headers and removal of AuthorizationToken.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/TestJsonSerializerContext.cs Updates source-gen context registrations for new polymorphic base types and tool call/result arrays.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/UserInputResponseContentTests.cs Removes tests for deprecated UserInputResponseContent.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/UserInputRequestContentTests.cs Removes tests for deprecated UserInputRequestContent.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/ToolResultContentTests.cs Adds roundtrip serialization coverage for ToolResultContent derived types.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/ToolCallContentTests.cs Adds roundtrip serialization coverage for ToolCallContent derived types.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/ToolApprovalResponseContentTests.cs Adds constructor validation + serialization roundtrip tests for ToolApprovalResponseContent.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/ToolApprovalRequestContentTests.cs Adds constructor validation + response factory + serialization roundtrip tests for ToolApprovalRequestContent.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/McpServerToolResultContentTests.cs Updates MCP result content to Outputs and validates polymorphic roundtrips.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/McpServerToolCallContentTests.cs Renames MCP call ToolNameName, Arguments mutability update, and adds serialization roundtrip.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/InputResponseContentTests.cs Adds base input-response type validation + derived serialization tests.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/InputRequestContentTests.cs Adds base input-request type validation + derived serialization tests.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/ImageGenerationToolResultContentTests.cs Moves to ctor-based CallId and removes ImageId usage.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/ImageGenerationToolCallContentTests.cs Moves to ctor-based CallId and removes ImageId usage.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/FunctionResultContentTests.cs Adds serialization roundtrip tests for ToolResultContent/AIContent polymorphism.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/FunctionCallContentTests.cs Adds serialization roundtrip tests for ToolCallContent/AIContent polymorphism.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/FunctionApprovalResponseContentTests.cs Removes deprecated FunctionApprovalResponseContent tests.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/FunctionApprovalRequestContentTests.cs Removes deprecated FunctionApprovalRequestContent tests.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/CodeInterpreterToolResultContentTests.cs Moves to ctor-based CallId and removes settable CallId.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/CodeInterpreterToolCallContentTests.cs Moves to ctor-based CallId and removes settable CallId.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/AIContentTests.cs Updates derived-type list and strengthens per-element polymorphic roundtrip checks.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/ChatCompletion/ChatResponseUpdateExtensionsTests.cs Updates coalescing tests to use CallId and ctor-based tool result creation.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/AssertExtensions.cs Extends message comparison helpers to handle tool approvals uniformly across tool call types.
src/Libraries/Microsoft.Extensions.AI/ChatReduction/SummarizingChatReducer.cs Updates “tool-related content” detection to include InputRequestContent/InputResponseContent.
src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs Updates OTel payload serialization for renamed properties (Name, Outputs, CallId) and unified approval types.
src/Libraries/Microsoft.Extensions.AI/ChatCompletion/ImageGeneratingChatClient.cs Switches to ctor-based image tool call/result creation with CallId.
src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs Migrates approvals to ToolApproval* with composed request IDs; updates request/response processing logic.
src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponsesChatClient.cs Implements MCP approval request/response correlation in Responses conversion; updates tool call/result modeling and connector auth extraction from headers.
src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIJsonContext.cs Removes IReadOnlyDictionary<string, object?> source-gen contract in favor of IDictionary<string, object?>.
src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIAssistantsChatClient.cs Switches code interpreter tool call/result construction to ctor-based CallId.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.cs Adds helper for registering derived types into polymorphism options.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.Defaults.cs Updates default serializer configuration to include new polymorphic bases and registers experimental tool types under ToolCallContent/ToolResultContent.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Tools/HostedMcpServerTool.cs Stabilizes MCP tool; removes AuthorizationToken and makes Headers nullable/settable; renames Uri ctor arg to serverAddress.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Microsoft.Extensions.AI.Abstractions.json Updates API baseline/staging to reflect stabilization and new content hierarchy.
src/Libraries/Microsoft.Extensions.AI.Abstractions/HostedMcpServerToolRequireSpecificApprovalMode.cs Removes experimental annotations in support of stabilization.
src/Libraries/Microsoft.Extensions.AI.Abstractions/HostedMcpServerToolNeverRequireApprovalMode.cs Removes experimental annotations in support of stabilization.
src/Libraries/Microsoft.Extensions.AI.Abstractions/HostedMcpServerToolApprovalMode.cs Removes experimental/polymorphic annotations in support of stabilization and updated serialization model.
src/Libraries/Microsoft.Extensions.AI.Abstractions/HostedMcpServerToolAlwaysRequireApprovalMode.cs Removes experimental annotations in support of stabilization.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Functions/ApprovalRequiredAIFunction.cs Removes experimental annotation (stabilizing the API).
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/UserInputResponseContent.cs Removes deprecated UserInputResponseContent.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/UserInputRequestContent.cs Removes deprecated UserInputRequestContent.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/ToolResultContent.cs Introduces new ToolResultContent base type for tool results.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/ToolCallContent.cs Introduces new ToolCallContent base type for tool calls.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/ToolApprovalResponseContent.cs Introduces unified approval response content for tool calls.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/ToolApprovalRequestContent.cs Introduces unified approval request content and response factory helper.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/McpServerToolResultContent.cs Updates MCP tool results to derive from ToolResultContent and renames OutputOutputs.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/McpServerToolCallContent.cs Updates MCP tool calls to derive from ToolCallContent, renames ToolNameName, and updates arguments type.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/McpServerToolApprovalResponseContent.cs Removes deprecated MCP approval response content type.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/McpServerToolApprovalRequestContent.cs Removes deprecated MCP approval request content type.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/InputResponseContent.cs Introduces new InputResponseContent base type for input responses.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/InputRequestContent.cs Introduces new InputRequestContent base type for input requests.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/ImageGenerationToolResultContent.cs Updates image result content to derive from ToolResultContent and use ctor CallId.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/ImageGenerationToolCallContent.cs Updates image call content to derive from ToolCallContent and use ctor CallId.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/FunctionResultContent.cs Updates function result content to derive from ToolResultContent.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/FunctionCallContent.cs Updates function call content to derive from ToolCallContent.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/FunctionApprovalResponseContent.cs Removes deprecated function approval response content type.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/FunctionApprovalRequestContent.cs Removes deprecated function approval request content type.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/CodeInterpreterToolResultContent.cs Updates code interpreter result content to derive from ToolResultContent and use ctor CallId.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/CodeInterpreterToolCallContent.cs Updates code interpreter call content to derive from ToolCallContent and use ctor CallId.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/AIContent.cs Registers stabilized tool/approval content types as derived types of AIContent.
src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/ChatResponseExtensions.cs Updates content coalescing logic for ctor-based tool content and CallId usage.

// as well as the [JsonSerializable] attributes for them on the JsonContext should be removed.
// [JsonDerivedType(typeof(CodeInterpreterToolResultContent), "codeInterpreterToolResult")]
// [JsonDerivedType(typeof(ImageGenerationToolResultContent), "imageGenerationToolResult")]
public class ToolResultContent : AIContent
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without a property that represents the result of the tool, folks will need to downcast to some tool type they know that will have output. I wonder if it makes sense to have a result property on this, even if it's not the serialized property.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would need to reconciliate object? Result from FRC and IList<AIContent>? Outputs from the rest. I suggest we defer for future this kind of breaking change. This could come handy for APIs that could accept the loose ToolResultContent, for now, we have it just for symmetry with ToolCallContent and we don't really use it for more. It's also related to #7057.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you worried about reconciling it later making a breaking change? I suppose we could always create a new read-only virtual property that's implemented by the derived types. This PR nearly solves #7057. It does look like a few reference counts on a unified base output property.

Copy link
Member Author

@jozkee jozkee Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made ToolCall/ToolResult contents effectively sealed (internal ctors), so moving a property up the hierarchy is not a breaking change.

If we do add Outputs to the base, I believe we still need to keep object? Result on FunctionResultContent. Result carries the return value from AIFunction.InvokeAsync(), preserving the original CLR object for leaf clients to serialize lazily in whatever format they need. Converting to IList<AIContent> at construction time is lossy so the original object is gone and providers lose the ability to serialize it in their own format. It also avoids breaking a stable type.

I'm a bit concerned about the ambiguity, but I think if we advertise Result as the raw CLR output and Outputs as a structured representation we could make it work. We already have precedent for this in OpenAI responses where we check if Result is AIContent or IEnumerable<AIContent> before falling back to serialization.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what it would look like: jozkee@90993a7.

For FunctionInvokingChatClient, I think we should not eagerly set Outputs since Result will be handled by leaf clients.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stephentoub could you please take a look at this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong opinion. If we ensure that all of the derived types have the same signature for it, we could push it down to ToolResultContent later, especially since I think all the derived content types that have it today are experimental (though we'll want to fix that soon, in particular for code interpreter). If we can rationalize what it looks like for FunctionResultContent and it makes sense, I'd be ok with it... but I think we should do it as a separate change.

@jozkee jozkee force-pushed the tool_approval_content2 branch from 1111f09 to 5343bb0 Compare February 18, 2026 04:18
Copy link
Member

@ericstj ericstj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me - your call on the output abstraction. At least think through how you might do it compatibly.

/// </summary>
/// <param name="callId">The tool call ID.</param>
/// <exception cref="ArgumentNullException"><paramref name="callId"/> is <see langword="null"/>.</exception>
internal ToolCallContent(string callId)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be public. Other tool call content / results should be able to derive from it, not just ones we define. We should also update the OpenAI response client to use this instead of AIContent for any call/result content it has types for but that we don't have an abstraction for.

/// </summary>
/// <param name="callId">The tool call ID for which this is the result.</param>
/// <exception cref="ArgumentNullException"><paramref name="callId"/> is <see langword="null"/>.</exception>
internal ToolResultContent(string callId)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

Comment on lines 196 to 203
@@ -203,6 +203,19 @@ private static void AddAIContentType(JsonSerializerOptions options, Type content
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks identical to AddDerivedContentType, just using typeof(AIContent) as baseType. Can this just call AddDerivedContentType instead?

AddAIContentType(options, typeof(ImageGenerationToolResultContent), typeDiscriminatorId: "imageGenerationToolResult", checkBuiltIn: false);

// Also register the experimental types as derived types of ToolCallContent/ToolResultContent.
AddDerivedContentType(options, typeof(ToolCallContent), typeof(CodeInterpreterToolCallContent), "codeInterpreterToolCall");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe:
a) we should also call this AddAIContentType and just have it be another overload that takes the base type
b) make it public, assuming others will need to register AIContent-derived types in a hierarchy
?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, I would prefer to make it public in a follow-up PR, though.

Comment on lines +126 to +135
// InputRequestContent and InputResponseContent are polymorphic base types that may be
// serialized as root types (not just as AIContent). They have protected constructors so
// can't be instantiated directly, but we still need metadata when serializing derived
// types (e.g., ToolApprovalRequestContent) as InputRequestContent.
[JsonSerializable(typeof(InputRequestContent))]
[JsonSerializable(typeof(InputResponseContent))]

// ToolCallContent and ToolResultContent are polymorphic base types for tool calls/results.
[JsonSerializable(typeof(ToolCallContent))]
[JsonSerializable(typeof(ToolResultContent))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do any of these need to be here? Especially once you add public ctors to ToolCall/ResultContent, they should be normal AIContent-derived types, with [JsonDerived] attributes on AIContent. For InputRequestContent/ResultContent, I understand the argument that they don't have public ctors and so a type discriminator is meaningless, but at the same time it shouldn't hurt anything and it makes it more maintainable in my opinion: they're just [JsonDerived] on the AIContent, and then you don't need a separate AddDerivedContent call, you don't need to list them separately as [JsonSerializable], etc... all the default handling kicks in.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it made sense to me for InputContent but since we are addressing ToolContents I'm ok with also having the former as JsonDerived.

message.Contents.Add(new ToolApprovalResponseContent(
mtcari.ApprovalRequestId,
mtcari.Approved,
new McpServerToolCallContent(mtcari.ApprovalRequestId, request.ToolName, request.ServerLabel)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the rationale for the mcpApprovalRequest dictionary storing the McpToolCallApprovalRequestItem rather than the ToolApprovalRequestContent instance? If it stored the latter, either instead of or in addition, then this could just use that same instance, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No particular reason, what you describe is better.

yield return CreateUpdate(new ToolApprovalResponseContent(
mtcari.ApprovalRequestId,
mtcari.Approved,
new McpServerToolCallContent(mtcari.ApprovalRequestId, request.ToolName, request.ServerLabel)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question

Comment on lines +1428 to +1430
Outputs = mtci.Error is not null ?
[new ErrorContent(mtci.Error.ToString())] :
[new TextContent(mtci.ToolOutput)],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this need to change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't have to, reverted.

/// </summary>
private static bool HasAnyApprovalContent(List<ChatMessage> messages) =>
messages.Exists(static m => m.Contents.Any(static c => c is FunctionApprovalRequestContent or FunctionApprovalResponseContent));
messages.Exists(static m => m.Contents.Any(static c => c is ToolApprovalRequestContent or ToolApprovalResponseContent));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this going to introduce any problems or perf issues now that it'll also return true for MCP approvals that are not handled by the FICC?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Narrowed the filter. No correctness issue before since we are also filtering (the narrow way) later, that second filter needs to stay for pattern matching.

switch (content)
{
case FunctionApprovalRequestContent farc:
case ToolApprovalRequestContent tarc when tarc.ToolCall is FunctionCallContent { InformationalOnly: false }:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for now, but I wonder if we're going to end up wanting to push InformationOnly down to ToolCallContent.

Copy link
Member

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good.

/// The underlying provider is not guaranteed to support or honor the headers.
/// </para>
/// <para>
/// This property is useful for specifying the authentication header or other headers required by the MCP server.
Copy link
Member

@stephentoub stephentoub Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally headers are case-insensitive. I wonder if we should make a comment about that here, because we don't have a good way to enforce it with this design (and the previous lazy-initialization looks like we had it wrong).

Providers are going to try to pull out "Authorization" or "authorization", and if the dictionary isn't using a case-insensitive comparer, it may not behave correctly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I don't think we have a good answer for this other than docs. We don't have a dictionary type that enforces case sensitiveness AFAIK and HttpHeaders may not be a good fit for this case. We have the same issue in https://github.com/modelcontextprotocol/csharp-sdk/blob/89fcf3f8b7db80bb7de6fcf7c8091b362fb18f84/src/ModelContextProtocol.Core/Client/HttpClientTransportOptions.cs#L78.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: not from your change, but this XML comment is stale; it doesn't coalesce based on Name

jozkee added 4 commits March 2, 2026 18:27
…ontent, InputRequestContent, and InputResponseContent; remove redundant [JsonSerializable] entries from AIJsonUtilities.Defaults

* Consolidate AddDerivedContentType into AddAIContentType with baseType param
* Make ToolCallContent and ToolResultContent constructors public
…ems to ToolCallContent/ToolResultContent in OpenAIResponsesChatClient (both non-streaming and streaming paths)

* Store ToolApprovalRequestContent in mcpApprovalRequests dictionary instead of McpToolCallApprovalRequestItem, reusing ToolCall on correlation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-ai Microsoft.Extensions.AI libraries

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants