Resources
Serve MCP App UIs and data as MCP resources
What Are MCP Resources?
MCP resources are read-only data exposed by your MCP server, identified by URI. In the context of MCP Apps, resources primarily serve single-file HTML bundles that render as rich UIs inside MCP clients.
MCP App Resources
An MCP App resource serves the built HTML bundle as a rich UI for a tool:
- MIME type:
text/html;profile=mcp-app - Content: single-file HTML (CSS + JS inlined) built by Vite +
vite-plugin-singlefile - Linked to tools via
_meta.ui.resourceUri
Creating a Resource
Basic Example
Here’s the hello resource from the template ( api/resources/hello.ts ):
import { readFile } from "node:fs/promises";
import { join } from "node:path";
import { createPublicResource } from "@decocms/runtime/tools";
import { HELLO_RESOURCE_URI } from "../tools/hello.ts";
import type { Env } from "../types/env.ts";
const RESOURCE_MIME_TYPE = "text/html;profile=mcp-app";
function getDistPath(): string {
const projectRoot = join(import.meta.dir, "../..");
return join(projectRoot, "dist", "client", "index.html");
}
export const helloAppResource = (_env: Env) =>
createPublicResource({
uri: HELLO_RESOURCE_URI,
name: "Hello UI",
description: "Interactive greeting display powered by MCP Apps",
mimeType: RESOURCE_MIME_TYPE,
read: async () => {
const html = await readFile(getDistPath(), "utf-8");
return {
uri: HELLO_RESOURCE_URI,
mimeType: RESOURCE_MIME_TYPE,
text: html,
};
},
});
Key parts:
createPublicResourcefrom@decocms/runtime/tools— creates a publicly accessible resourceuri— must match the_meta.ui.resourceUriin the tool definitionmimeType—text/html;profile=mcp-apptells the MCP client this is an MCP Appread()— async function that returns the HTML content
Registering Resources
Add your resource creator to the resources array in withRuntime() in api/main.ts :
const runtime = withRuntime<Env, typeof StateSchema>({
configuration: { state: StateSchema },
tools,
resources: [helloAppResource, myToolResource],
});
Resource URIs
- Convention:
ui://<app-name>/<tool-name>(e.g.,ui://mcp-app/hello) - The URI must match the
_meta.ui.resourceUriin the corresponding tool definition - The MCP client uses this URI to find and load the UI for a tool result
Export your resource URI as a constant from the tool file so both the tool and resource reference the same value.
Build Output
Vite builds web/ into dist/client/index.html :
- All CSS and JS are inlined into a single HTML file (via
vite-plugin-singlefile) - The resource reads this file at runtime and serves it to MCP clients
- In development,
bun run devrunsvite build --watchto keep the bundle updated
Multiple Resources
If your app has multiple tools with UIs, each tool gets its own resource definition — but they all serve the same HTML bundle. The router inside the bundle ( web/router.tsx ) dispatches to the correct tool UI based on toolName from the host context.
// api/resources/analytics.ts
export const analyticsAppResource = (_env: Env) =>
createPublicResource({
uri: ANALYTICS_RESOURCE_URI,
name: "Analytics UI",
description: "Interactive analytics dashboard",
mimeType: "text/html;profile=mcp-app",
read: async () => {
const html = await readFile(getDistPath(), "utf-8");
return {
uri: ANALYTICS_RESOURCE_URI,
mimeType: "text/html;profile=mcp-app",
text: html,
};
},
}); Found an error or want to improve this page?
Edit this page