ToolApprovalRequestContent.RequiresConfirmation causes MEAI001 in consumer AIContent source-generation contexts
Microsoft.Extensions.AI.Abstractions 10.8.0 introduced a source-generation regression for consumers that serialize AIContent.
ToolApprovalRequestContent is statically registered as a JsonDerivedType of AIContent and InputRequestContent. In 10.8.0, Add ToolApprovalRequestContent.RequiresConfirmation (#7549) added the serialized experimental member ToolApprovalRequestContent.RequiresConfirmation. As a result, a consumer need not use approvals—or reference RequiresConfirmation—to receive MEAI001: it is emitted from generated code whenever the consumer source-generates metadata for AIContent.
Minimal repro
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="10.8.0" />
</ItemGroup>
using Microsoft.Extensions.AI;
using System.Text.Json.Serialization;
[JsonSerializable(typeof(List<AIContent>))]
internal partial class JsonContext : JsonSerializerContext;
The build produces source-generated warnings:
warning MEAI001: 'Microsoft.Extensions.AI.ToolApprovalRequestContent.RequiresConfirmation'
is for evaluation purposes only and is subject to change or removal in future updates.
Consumers using source generation are forced to suppress MEAI001 at the project level as the only remedy. But that suppression does not remove the member from the generated contract: externally exposed JSON payloads still include the experimental requiresConfirmation field, despite the consumer never opting into the approval feature.
Expected behavior
Consumers that do not directly use experimental approval APIs should be able to source-generate AIContent metadata without receiving MEAI001.
Direct use of RequiresConfirmation should continue to require explicit experimental opt-in.
Fix serialization of [Experimental] AIContent-derived types (#6900) identified this same consumer source-generation boundary, but for experimental types: an experimental type cannot be a static JsonDerivedType member of AIContent, so it registered such types through the AIJsonUtilities.DefaultOptions runtime path instead of the static contract.
Stabilize CodeInterpreter and WebSearch content types (#7493) later removed that temporary runtime-registration pattern once the affected types became stable. The compile-time guard that remained (Microsoft.Extensions.AI.Stabilization.Tests, which treats MEAI001 as an error) only ever exercised individual stabilized types; it never source-generated a full AIContent consumer context. That is why an experimental member on an already-stable type was not caught.
How this slipped through
We had guards to prevent experimental types from leaking, but RequiresConfirmation is an experimental member on a stable, statically registered type. It has the same viral consequence: it leaks into every consumer-generated AIContent contract. This is not specific to polymorphism—any reachable experimental serialized member leaks the same way. UsageDetails' token counts and HostedFileContent.Purpose/Scope are non-polymorphic experimental members that already avoid it (see Proposed direction).
The regression symptom was visible in Add ToolApprovalRequestContent.RequiresConfirmation (#7549): it added NoWarn suppressions for MEAI001 to both Microsoft.Extensions.AI.Evaluation.Reporting and Microsoft.Extensions.AI.Evaluation.Reporting.Azure. The PR initially added the suppressions, removed them, and re-added them after RequiresConfirmation was marked experimental. Those project-level suppressions should have prompted investigation of why an experimental member was leaking into their serialization contracts.
Proposed direction
ToolApprovalRequestContent is a stable type; only its RequiresConfirmation member is experimental. So, unlike #6900's handling of experimental types, there is no need to remove the type from the static polymorphic contract. Instead, gate the experimental member at the member level—the pattern already used by UsageDetails' token-count properties (Add token-count details to UsageDetails (#7285)) and HostedFileContent.Purpose/Scope:
- Mark the public
RequiresConfirmation accessor [Experimental] and [JsonIgnore].
- Route its get/set to a new non-experimental
internal property ([JsonInclude], [JsonPropertyName("requiresConfirmation")], default true) that carries the JSON.
- Keep the existing static
JsonDerivedType registrations on AIContent and InputRequestContent (the type is stable and must remain in the polymorphic contract).
The internal member is only accessible in-assembly, so RequiresConfirmation continues to round-trip through the package's own serialization (AIJsonUtilities.DefaultOptions) while being silently dropped from a consumer's own source-generated AIContent context—no MEAI001, and no experimental requiresConfirmation field in their contract. Direct use of the public RequiresConfirmation still requires experimental opt-in.
For regression coverage, add a holistic, maintenance-free guard rather than a per-type test: a single no-suppression JsonSerializerContext in Microsoft.Extensions.AI.Stabilization.Tests (which treats MEAI001 as an error) rooted at the package's public serialization entry points—List<AIContent>, ChatResponse, ChatResponseUpdate, ChatOptions, Embedding. Because every Microsoft.Extensions.AI experiment shares the single MEAI001 id and new members hang off these existing roots, any future experimental member that leaks into a consumer-generated contract—polymorphic derived type or plain property alike—fails the build automatically.
This preserves RequiresConfirmation in the supported default serialization path while preventing it from appearing in consumer source-generated contracts.
ToolApprovalRequestContent.RequiresConfirmation causes MEAI001 in consumer AIContent source-generation contexts
Microsoft.Extensions.AI.Abstractions10.8.0 introduced a source-generation regression for consumers that serializeAIContent.ToolApprovalRequestContentis statically registered as aJsonDerivedTypeofAIContentandInputRequestContent. In 10.8.0, Add ToolApprovalRequestContent.RequiresConfirmation (#7549) added the serialized experimental memberToolApprovalRequestContent.RequiresConfirmation. As a result, a consumer need not use approvals—or referenceRequiresConfirmation—to receiveMEAI001: it is emitted from generated code whenever the consumer source-generates metadata forAIContent.Minimal repro
The build produces source-generated warnings:
Consumers using source generation are forced to suppress
MEAI001at the project level as the only remedy. But that suppression does not remove the member from the generated contract: externally exposed JSON payloads still include the experimentalrequiresConfirmationfield, despite the consumer never opting into the approval feature.Expected behavior
Consumers that do not directly use experimental approval APIs should be able to source-generate
AIContentmetadata without receivingMEAI001.Direct use of
RequiresConfirmationshould continue to require explicit experimental opt-in.Fix serialization of [Experimental] AIContent-derived types (#6900) identified this same consumer source-generation boundary, but for experimental types: an experimental type cannot be a static
JsonDerivedTypemember ofAIContent, so it registered such types through theAIJsonUtilities.DefaultOptionsruntime path instead of the static contract.Stabilize CodeInterpreter and WebSearch content types (#7493) later removed that temporary runtime-registration pattern once the affected types became stable. The compile-time guard that remained (
Microsoft.Extensions.AI.Stabilization.Tests, which treatsMEAI001as an error) only ever exercised individual stabilized types; it never source-generated a fullAIContentconsumer context. That is why an experimental member on an already-stable type was not caught.How this slipped through
We had guards to prevent experimental types from leaking, but
RequiresConfirmationis an experimental member on a stable, statically registered type. It has the same viral consequence: it leaks into every consumer-generatedAIContentcontract. This is not specific to polymorphism—any reachable experimental serialized member leaks the same way.UsageDetails' token counts andHostedFileContent.Purpose/Scopeare non-polymorphic experimental members that already avoid it (see Proposed direction).The regression symptom was visible in Add ToolApprovalRequestContent.RequiresConfirmation (#7549): it added
NoWarnsuppressions forMEAI001to bothMicrosoft.Extensions.AI.Evaluation.ReportingandMicrosoft.Extensions.AI.Evaluation.Reporting.Azure. The PR initially added the suppressions, removed them, and re-added them afterRequiresConfirmationwas marked experimental. Those project-level suppressions should have prompted investigation of why an experimental member was leaking into their serialization contracts.Proposed direction
ToolApprovalRequestContentis a stable type; only itsRequiresConfirmationmember is experimental. So, unlike #6900's handling of experimental types, there is no need to remove the type from the static polymorphic contract. Instead, gate the experimental member at the member level—the pattern already used byUsageDetails' token-count properties (Add token-count details to UsageDetails (#7285)) andHostedFileContent.Purpose/Scope:RequiresConfirmationaccessor[Experimental]and[JsonIgnore].internalproperty ([JsonInclude],[JsonPropertyName("requiresConfirmation")], defaulttrue) that carries the JSON.JsonDerivedTyperegistrations onAIContentandInputRequestContent(the type is stable and must remain in the polymorphic contract).The internal member is only accessible in-assembly, so
RequiresConfirmationcontinues to round-trip through the package's own serialization (AIJsonUtilities.DefaultOptions) while being silently dropped from a consumer's own source-generatedAIContentcontext—noMEAI001, and no experimentalrequiresConfirmationfield in their contract. Direct use of the publicRequiresConfirmationstill requires experimental opt-in.For regression coverage, add a holistic, maintenance-free guard rather than a per-type test: a single no-suppression
JsonSerializerContextinMicrosoft.Extensions.AI.Stabilization.Tests(which treatsMEAI001as an error) rooted at the package's public serialization entry points—List<AIContent>,ChatResponse,ChatResponseUpdate,ChatOptions,Embedding. Because every Microsoft.Extensions.AI experiment shares the singleMEAI001id and new members hang off these existing roots, any future experimental member that leaks into a consumer-generated contract—polymorphic derived type or plain property alike—fails the build automatically.This preserves
RequiresConfirmationin the supported default serialization path while preventing it from appearing in consumer source-generated contracts.