Follow-up from PR #1484 (MCP Apps extension).
Gap
The new ModelContextProtocol.Extensions.Apps package declares IsAotCompatible=true, but nothing exercises it under NativeAOT. tests/ModelContextProtocol.AotCompatibility.TestApp only references ModelContextProtocol.Core, ModelContextProtocol, and ModelContextProtocol.AspNetCore — not Extensions.Apps. So the AOT publish job never sees the Apps code paths.
This is the reason a new { } anonymous-type bug in the capability-advertisement path (fixed during PR #1484 review) was not caught by CI. Our convention is that every shipped src/ package gets AOT publish coverage.
Proposed work
-
Reference the package and root it in tests/ModelContextProtocol.AotCompatibility.TestApp/ModelContextProtocol.AotCompatibility.TestApp.csproj:
<ProjectReference Include="..\..\src\ModelContextProtocol.Extensions.Apps\ModelContextProtocol.Extensions.Apps.csproj" />
<TrimmerRootAssembly Include="ModelContextProtocol.Extensions.Apps" />
-
Extend the test app's Program.cs to actually exercise the surface, otherwise the trimmer drops the package as unused and the publish passes vacuously. At minimum: register a tool decorated with [McpAppUi], call WithMcpApps(), round-trip ListToolsAsync(), and assert the _meta.ui metadata is present.
Caveat (for expectation-setting)
This coverage would not have surfaced the specific new { } issue as an AOT analyzer warning at publish time — that value flows through IDictionary<string, object> (boxed/polymorphic), which the trimmer can't statically trace; it would only manifest as a runtime serialization fallback (or NotSupportedException in strict AOT). But the coverage still catches a broad class of other AOT problems and matches the convention for new packages.
The broader point — that the IDictionary<string, object> extension surface (MCPEXP001) is an AOT footgun in general, since anything assigned to it bypasses source-gen unless callers use JsonObject or a source-gen-registered type — may warrant its own tracking issue.
Related
Follow-up from PR #1484 (MCP Apps extension).
Gap
The new
ModelContextProtocol.Extensions.Appspackage declaresIsAotCompatible=true, but nothing exercises it under NativeAOT.tests/ModelContextProtocol.AotCompatibility.TestApponly referencesModelContextProtocol.Core,ModelContextProtocol, andModelContextProtocol.AspNetCore— notExtensions.Apps. So the AOT publish job never sees the Apps code paths.This is the reason a
new { }anonymous-type bug in the capability-advertisement path (fixed during PR #1484 review) was not caught by CI. Our convention is that every shippedsrc/package gets AOT publish coverage.Proposed work
Reference the package and root it in
tests/ModelContextProtocol.AotCompatibility.TestApp/ModelContextProtocol.AotCompatibility.TestApp.csproj:Extend the test app's
Program.csto actually exercise the surface, otherwise the trimmer drops the package as unused and the publish passes vacuously. At minimum: register a tool decorated with[McpAppUi], callWithMcpApps(), round-tripListToolsAsync(), and assert the_meta.uimetadata is present.Caveat (for expectation-setting)
This coverage would not have surfaced the specific
new { }issue as an AOT analyzer warning at publish time — that value flows throughIDictionary<string, object>(boxed/polymorphic), which the trimmer can't statically trace; it would only manifest as a runtime serialization fallback (orNotSupportedExceptionin strict AOT). But the coverage still catches a broad class of other AOT problems and matches the convention for new packages.The broader point — that the
IDictionary<string, object>extension surface (MCPEXP001) is an AOT footgun in general, since anything assigned to it bypasses source-gen unless callers useJsonObjector a source-gen-registered type — may warrant its own tracking issue.Related
[McpAppResource]attribute / resource-side parity follow-up