Skip to content

feat(mcp-annotations): add SEP-1865 MCP Apps support - #6182

Open
apappascs wants to merge 2 commits into
spring-projects:mainfrom
apappascs:feature/mcp-apps-sep-1865
Open

feat(mcp-annotations): add SEP-1865 MCP Apps support#6182
apappascs wants to merge 2 commits into
spring-projects:mainfrom
apappascs:feature/mcp-apps-sep-1865

Conversation

@apappascs

@apappascs apappascs commented May 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Add SEP-1865 (MCP Apps) support to the mcp-annotations module, enabling Spring AI MCP servers to expose interactive UI widgets alongside their tools.

What's included

  • @McpTool extensionsresourceUri, visibility, and csp fields to link tools to UI resources and control visibility (model-only, app-only, or both)
  • Visibility enumMODEL and APP visibility scopes
  • @McpCsp annotation — declare Content Security Policy domains (connectDomains, resourceDomains, redirectDomains) for widget sandboxing
  • McpAppResult — convenience return type that splits textcontent[] (for the LLM) and structuredContent (for the widget UI)
  • MetaUtilsbuildUiMeta() and mergeMeta() helpers to construct _meta.ui blocks per the SEP-1865 spec
  • Tool provider updates — all 4 providers (sync/async × stateful/stateless) now merge UI metadata into tool definitions
  • ResourceAdapter — validates ui:// URIs and text/html;profile=mcp-app MIME type for MCP App resources
  • Comprehensive testsMcpAppResultTests, ResourceAdapterTests, MetaUtilsTest, SyncMcpToolProviderTests

Example usage

@McpTool(
    name = "my_tool",
    description = "...",
    resourceUri = "ui://my-server/widget.html",
    visibility = { Visibility.MODEL, Visibility.APP }
)
public McpAppResult myTool(@McpToolParam(description = "...") String input) {
    return McpAppResult.of(
        "Text for the LLM",
        Map.of("key", "structured data for the widget")
    );
}

Context

SEP-1865 defines how MCP servers expose interactive UIs (widgets) that render inside AI chat clients (ChatGPT, Claude, etc.). This PR adds first-class annotation support so Spring AI developers can build MCP Apps with the same @McpTool / @McpResource patterns they already use.

Test plan

  • McpAppResultTests — verifies text/structuredContent split
  • ResourceAdapterTests — validates ui:// URI and MIME type handling
  • MetaUtilsTest — tests meta construction and merging
  • SyncMcpToolProviderTests — verifies tool providers inject UI metadata
  • Manual end-to-end testing with MCPJam Inspector and ChatGPT

@apappascs
apappascs force-pushed the feature/mcp-apps-sep-1865 branch from b2659e8 to cdcf2d6 Compare May 27, 2026 09:37
@apappascs
apappascs marked this pull request as draft May 27, 2026 09:37
@apappascs
apappascs force-pushed the feature/mcp-apps-sep-1865 branch from cdcf2d6 to cae74b5 Compare May 27, 2026 09:43
@apappascs

Copy link
Copy Markdown
Contributor Author

- add resourceUri, visibility, csp fields to @mcptool
- create Visibility enum and @McpCsp annotation
- add MetaUtils.buildUiMeta() and mergeMeta() helpers
- update all 4 tool providers to use merged meta
- add McpAppResult return type with text/structuredContent split
- handle McpAppResult in convertValueToCallToolResult
- add ui:// URI + MIME type validation to ResourceAdapter
- add comprehensive tests for all new functionality

Signed-off-by: Alexandros Pappas <apappascs@gmail.com>
… findings

- fix McpAppResult handler dead-code path: move instanceof branch before
  STRUCTURED branch in AbstractMcpToolMethodCallback; null guard moved
  before both branches
- exclude McpAppResult from output-schema generation in all 4 providers
  (Sync, SyncStateless, Async, AsyncStateless) — it is a wire-format
  container, not a structured data output
- fix ui/resourceUri flat alias diverging from nested ui.resourceUri after
  MetaProvider override in MetaUtils.mergeMeta
- extract duplicated ui:// vs MIME validation into validateMcpAppResource()
  helper in ResourceAdapter; add case-insensitive scheme matching (RFC 3986)
  and whitespace/case-lenient MIME comparison
- add McpAppResult compact-constructor: both-null throws IllegalArgumentException
- add @author Alexandros Pappas to McpAppResult.java
- fix buildUiMeta blank-URI guard: isEmpty() -> isBlank()
- document CSP domain format (full origins) in McpCsp javadoc; align test
  value from bare domain to https://api.example.com
- fix contradictory comments in testToolWithResourceUriAndMetaProviderMerge
- add generateOutputSchema=true E2E tests for McpAppResult across all 4
  provider test classes; add UI-meta smoke tests to Async, AsyncStateless,
  SyncStateless providers

Signed-off-by: Alexandros Pappas <apappascs@gmail.com>
@apappascs
apappascs force-pushed the feature/mcp-apps-sep-1865 branch from 0e82b86 to 92eb063 Compare June 12, 2026 12:22
@apappascs
apappascs marked this pull request as ready for review June 15, 2026 08:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants