Skip to content

Add MCP Apps (SEP-1865) support to MCP annotations - #6182

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

apappascs wants to merge 1 commit 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 extensions — resourceUri and visibility fields to link tools to UI resources and control visibility (model-only, app-only, or both); visibility is emitted under _meta.ui even without a resourceUri, so app-only tools stay hidden from the model
  • Visibility enum — MODEL and APP visibility scopes
  • @McpCsp annotation — declare Content Security Policy domains (connectDomains, resourceDomains, frameDomains, baseUriDomains) for widget sandboxing, set via @McpResource(csp = @McpCsp(...)) on the UI resource; the spec reads csp from the UI resource (_meta.ui.csp of the resources/read content; the current draft also reads it from the resources/list entry)
  • @McpResource UI attributes — permissions (McpPermission: camera, microphone, geolocation, clipboardWrite), domain and prefersBorder (PrefersBorder tri-state), emitted under _meta.ui on the listed resource, template and read contents
  • McpAppResult — convenience return type that splits text → content[] (for the LLM) and structuredContent (for the widget UI)
  • MetaUtils — buildUiMeta(), buildCspMeta(), getResourceMeta() 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 the ui:// URI and text/html;profile=mcp-app MIME type pairing when its factory methods are used. The annotation-based resource providers do not use it, so the pairing is documented but not validated for @McpResource beans; enforcing it in the providers could be a follow-up
  • Comprehensive tests — McpAppResultTests, ResourceAdapterTests, MetaUtilsTest, tool and resource provider tests (sync/async × stateful/stateless)

Changes since initial version

  • The CSP moved from @McpTool to @McpResource: the spec reads csp from the UI resource (_meta.ui.csp of the resources/read content; the current draft also reads it from the resources/list entry), and tool _meta only carries resourceUri and visibility; csp is not defined on a tool's _meta. When both csp and a metaProvider supply _meta.ui, they are merged per key and the metaProvider wins on conflict.
  • redirectDomains was removed because it is not a SEP-1865 field (it exists only in ChatGPT's legacy openai/widgetCSP.redirect_domains, which can still be set via metaProvider). frameDomains and baseUriDomains were added.

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")
    );
}

And the matching UI resource, which carries the CSP:

@McpResource(
    uri = "ui://my-server/widget.html",
    mimeType = "text/html;profile=mcp-app",
    csp = @McpCsp(connectDomains = "https://api.example.com")
)
public String widget() {
    return "<html>...</html>";
}

The CSP is attached to the read contents automatically when the method returns String or List<String>; methods returning ResourceContents or ReadResourceResult must set _meta.ui.csp on the contents themselves.

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, and CSP emission on listed resources and templates
  • MetaUtilsTest — tests meta construction (including the four CSP fields, empty-CSP omission) and merging; exact-keys assertions on the CSP map ensure no redirectDomains (or any other undeclared key) is emitted
  • Tool provider tests (sync/async × stateful/stateless) — verify tools get exactly ui.resourceUri/visibility (exact-keys assertions, so no ui.csp)
  • Resource provider tests (sync/async × stateful/stateless) — verify _meta.ui.csp on the listed resource and read contents, empty CSP omitted, merge with metaProvider
  • Manual end-to-end testing (tool/resource flow) with MCPJam Inspector and ChatGPT on the initial version

@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

@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
@apappascs
apappascs force-pushed the feature/mcp-apps-sep-1865 branch from 2cef31a to cf8e066 Compare August 31, 2026 11:16
@apappascs
apappascs force-pushed the feature/mcp-apps-sep-1865 branch from 4016a92 to edc116b Compare October 13, 2026 07:39
Add SEP-1865 (MCP Apps) support to the mcp-annotations module so
Spring AI MCP servers can expose interactive UI widgets next to
their tools, using the existing `@McpTool` and `@McpResource`
programming model.

- add `resourceUri` and `visibility` to `@McpTool`, emitted as
  `_meta.ui.resourceUri` and `_meta.ui.visibility` (plus the flat
  `ui/resourceUri` alias); visibility is emitted even without a
  `resourceUri` so app-only tools stay hidden from the model
- add the `Visibility` enum and the `@McpCsp` annotation with
  `connectDomains`, `resourceDomains`, `frameDomains` and
  `baseUriDomains`
- add `csp`, `permissions`, `domain` and `prefersBorder` to
  `@McpResource`, with `McpPermission` and `PrefersBorder`, emitted
  as `_meta.ui` on the listed resource, template and read contents
  (the CSP is not defined on a tool's `_meta`)
- add `McpAppResult`, splitting text into `content[]` for the model
  and `structuredContent` for the widget, and exclude it from
  output schema generation
- add `MetaUtils` helpers to build and merge `_meta.ui`; when a
  `metaProvider` also supplies `ui`, the maps are merged per key
  and the provider wins on conflict
- merge the UI metadata in all four tool and resource providers
  (sync and async, stateful and stateless)
- add `ResourceAdapter` validation of the `ui://` URI and the
  `text/html;profile=mcp-app` MIME type pairing; this pairing is
  documented but not validated for annotation-based resources
- document MCP Apps in the MCP annotations server reference
- add unit and provider tests for all of the above

Signed-off-by: Alexandros Pappas <apappascs@gmail.com>
@apappascs
apappascs force-pushed the feature/mcp-apps-sep-1865 branch from edc116b to 87a32dc Compare October 13, 2026 09:32
@apappascs apappascs changed the title feat(mcp-annotations): add SEP-1865 MCP Apps support Add MCP Apps (SEP-1865) support to MCP annotations Oct 13, 2026
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