L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Développement · SKILL.md

ra-mcp-apps

ra-mcp-apps

MCP Apps guide: FastMCP 3.4+ server, Svelte 5 view, tool visibility, polling, sizing, fullscreen, view persistence. Use for any MCP App/UI work.

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: ra-mcp-apps description: "MCP Apps guide: FastMCP 3.4+ server, Svelte 5 view, tool visibility, polling, sizing, fullscreen, view persistence. Use for any MCP App/UI work."

MCP Apps — Quick Reference

Server (FastMCP) ↔ Host (Claude/ChatGPT) ↔ View (sandboxed iframe). The View and Server never talk directly.

Supporting Files

  • patterns.md — MCP Apps patterns: polling, chunked data, theming, fullscreen, view persistence (Brick Builder), per-view state, autoResize, command queues
  • csp-cors.md — CSP configuration, CORS, stable origins, connectDomains vs resourceDomains
  • testing.md — Testing with basic-host, Claude.ai, VS Code, tunnels

Data Flow

FieldLLM sees?View sees?Use for
contentYesYesShort text summary for model context
structuredContentNo*YesRich data for UI rendering
_metaNoYesviewUUID, timestamps, metadata

*ChatGPT exposes structuredContent to both.

Tool Visibility

VisibilityLLM?View?Use case
["model", "app"] (default)YesYesGeneral tools
["model"]YesNoLLM-only triggers
["app"]NoYesPagination, refresh, polling, dangerous actions

Server Quick Start (FastMCP 3.4+)

from fastmcp import FastMCP, Context
from fastmcp.apps import AppConfig, ResourceCSP, UI_EXTENSION_ID
from fastmcp.tools import ToolResult
from mcp import types

mcp = FastMCP("My App")
RESOURCE_URI = "ui://my-app/view.html"

# Tool with UI — creates an iframe
@mcp.tool(app=AppConfig(resource_uri=RESOURCE_URI))
async def show_data(query: str, ctx: Context) -> ToolResult:
    has_ui = ctx.client_supports_extension(UI_EXTENSION_ID)
    return ToolResult(
        content=[types.TextContent(type="text", text="Summary for LLM")],
        structured_content={"data": [...], "query": query},
    )

# App-only tool — View calls this, LLM never sees it
@mcp.tool(app=AppConfig(resource_uri=RESOURCE_URI, visibility=["app"]))
async def load_page(page: int) -> ToolResult: ...

# Resource — serves the bundled HTML
@mcp.resource(uri=RESOURCE_URI)
def get_ui() -> str:
    return Path("dist/mcp-app.html").read_text()

View Quick Start (Svelte 5)

<script lang="ts">
import { onMount } from "svelte";
import { App, applyDocumentTheme, applyHostStyleVariables, applyHostFonts, type McpUiHostContext } from "@modelcontextprotocol/ext-apps";

let app = $state<App | null>(null);
let hostContext = $state<McpUiHostContext | undefined>();
let data = $state.raw<MyData | null>(null);

$effect(() => {
  if (hostContext?.theme) applyDocumentTheme(hostContext.theme);
  if (hostContext?.styles?.variables) applyHostStyleVariables(hostContext.styles.variables);
  if (hostContext?.styles?.css?.fonts) applyHostFonts(hostContext.styles.css.fonts);
});

onMount(async () => {
  const instance = new App(
    { name: "My App", version: "1.0.0" },
    { availableDisplayModes: ["inline", "fullscreen"] },
    { autoResize: false },  // Required for flexible-height content — see patterns.md
  );
  instance.ontoolresult = (result) => { data = result.structuredContent; };
  instance.onhostcontextchanged = (ctx) => { hostContext = { ...hostContext, ...ctx }; };
  instance.onteardown = async () => { /* cleanup */ return {}; };
  await instance.connect();
  app = instance;
  hostContext = instance.getHostContext();
});
</script>

Decision Checklist

QuestionAnswer
What does the LLM need?content
What does the UI need?structuredContent
LLM triggers this?visibility: ["model"] or default
UI-only action?visibility: ["app"]
Data >100KB?Chunked app-only tool — see patterns.md
External APIs?Declare in ResourceCSP — see csp-cors.md
Fullscreen?availableDisplayModes: ["inline", "fullscreen"]
View should persist across LLM tool calls?Brick Builder pattern — see patterns.md
Multi-user server?Per-view state with MemoryStore — see patterns.md

Gotchas

  • Tool with resourceUri = new iframe. Never put AppConfig(resource_uri=...) on update tools.
  • autoResize: false required for flexible-height content to prevent infinite resize loops.
  • onteardown — always implement to stop polling and clean up.
  • CSP deny-by-default. External images silently don't load without resource_domains.
  • asyncio.gather for parallel fetches — sequential await calls wait for each other.
  • Multi-user servers need per-view state (MemoryStore + view_id), not module-level globals.
Skills enregistrées