From 47cc2f52f50fb68908b2f3ce202091b59e970762 Mon Sep 17 00:00:00 2001 From: arimu1 <19286898+arimu1@users.noreply.github.com> Date: Sun, 26 Jul 2026 10:51:10 +0700 Subject: [PATCH] fix(mcp-apps-server): put CSP meta on resource read response Per MCP Apps spec, _meta.ui.csp belongs on contents[] objects returned by the resource read callback, not on the resource definition via metaProvider. Fixes spring-projects/spring-ai-examples#136 Signed-off-by: arimu1 <19286898+arimu1@users.noreply.github.com> --- .../com/example/mcpappsserver/DiceApp.java | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/model-context-protocol/mcp-apps-server/src/main/java/com/example/mcpappsserver/DiceApp.java b/model-context-protocol/mcp-apps-server/src/main/java/com/example/mcpappsserver/DiceApp.java index 4f6a8f8c..62bd5d9d 100644 --- a/model-context-protocol/mcp-apps-server/src/main/java/com/example/mcpappsserver/DiceApp.java +++ b/model-context-protocol/mcp-apps-server/src/main/java/com/example/mcpappsserver/DiceApp.java @@ -1,5 +1,7 @@ package com.example.mcpappsserver; +import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult; +import io.modelcontextprotocol.spec.McpSchema.TextResourceContents; import org.springframework.ai.mcp.annotation.McpResource; import org.springframework.ai.mcp.annotation.McpTool; import org.springframework.ai.mcp.annotation.context.MetaProvider; @@ -21,22 +23,24 @@ public class DiceApp { @Value("classpath:/app/dice-app.html") private Resource diceAppResource; - @McpResource(name = "Dice App Resource", - uri = "ui://dice/dice-app.html", - mimeType = "text/html;profile=mcp-app", - metaProvider = CspMetaProvider.class) - public String getDiceAppResource() throws IOException { - return diceAppResource.getContentAsString(Charset.defaultCharset()); - } + private static final String DICE_APP_URI = "ui://dice/dice-app.html"; - public static final class CspMetaProvider implements MetaProvider { - @Override - public Map getMeta() { - return Map.of("ui", - Map.of("csp", - Map.of("resourceDomains", - List.of("https://unpkg.com")))); - } + private static final String DICE_APP_MIME_TYPE = "text/html;profile=mcp-app"; + + @McpResource(name = "Dice App Resource", + uri = DICE_APP_URI, + mimeType = DICE_APP_MIME_TYPE) + public ReadResourceResult getDiceAppResource() throws IOException { + String html = diceAppResource.getContentAsString(Charset.defaultCharset()); + return ReadResourceResult.builder(List.of( + TextResourceContents.builder(DICE_APP_URI, html) + .mimeType(DICE_APP_MIME_TYPE) + .meta(Map.of("ui", + Map.of("csp", + Map.of("resourceDomains", + List.of("https://unpkg.com"))))) + .build())) + .build(); } // @@ -56,7 +60,7 @@ public static final class DiceMetaProvider implements MetaProvider { public Map getMeta() { return Map.of("ui", Map.of( - "resourceUri", "ui://dice/dice-app.html")); + "resourceUri", DICE_APP_URI)); } }