Add MCP Apps hello world demo tool

Implements a simple MCP Apps tool that returns an interactive React UI:

- Add `mcp_apps_hello_world` tool with `_meta.ui/resourceUri` metadata
- Add resource handler for `ui://hello-world/app.html` returning HTML
- Create React app using `@modelcontextprotocol/ext-apps/react` hooks
- Add Vite build tooling to bundle React apps into single HTML files

The tool demonstrates the MCP Apps extension (SEP-1865) which allows
MCP servers to return interactive UI that runs in the client.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Jerome Swannack 2025-12-07 17:55:06 +00:00
commit c872ecd224
No known key found for this signature in database

View file

@ -1,4 +1,6 @@
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import fs from "node:fs/promises";
import path from "node:path";
import {
CallToolRequestSchema,
CompleteRequestSchema,
@ -18,7 +20,7 @@ import {
Tool,
UnsubscribeRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import { z } from "zod/v4";
type ToolInput = Tool["inputSchema"];
@ -90,8 +92,13 @@ enum ToolName {
ANNOTATED_MESSAGE = "annotatedMessage",
GET_RESOURCE_REFERENCE = "getResourceReference",
ELICIT_INPUTS = "elicitInputs",
MCP_APPS_HELLO_WORLD = "mcp_apps_hello_world",
}
// MCP Apps constants
const RESOURCE_URI_META_KEY = "ui/resourceUri";
const HELLO_WORLD_APP_URI = "ui://hello-world/app.html";
enum PromptName {
SIMPLE = "simple_prompt",
COMPLEX = "complex_prompt",
@ -274,6 +281,21 @@ export const createMcpServer = (): McpServerWrapper => {
}
}
// MCP Apps UI resources
if (uri === HELLO_WORLD_APP_URI) {
const distDir = path.join(import.meta.dirname, "../../../apps");
const html = await fs.readFile(path.join(distDir, "mcp-app.html"), "utf-8");
return {
contents: [
{
uri,
mimeType: "text/html;profile=mcp-app",
text: html,
},
],
};
}
throw new Error(`Unknown resource: ${uri}`);
});
@ -455,6 +477,13 @@ export const createMcpServer = (): McpServerWrapper => {
"Elicitation test tool that demonstrates how to request user input with various field types",
inputSchema: { type: "object" , properties: {} },
},
{
name: ToolName.MCP_APPS_HELLO_WORLD,
description:
"Demonstrates MCP Apps - returns an interactive UI that runs in the client",
inputSchema: { type: "object", properties: {} },
_meta: { [RESOURCE_URI_META_KEY]: HELLO_WORLD_APP_URI },
},
];
return { tools };
@ -720,6 +749,18 @@ export const createMcpServer = (): McpServerWrapper => {
};
}
if (name === ToolName.MCP_APPS_HELLO_WORLD) {
return {
content: [
{
type: "text",
text: "Hello World MCP App loaded!",
},
],
_meta: { [RESOURCE_URI_META_KEY]: HELLO_WORLD_APP_URI },
};
}
throw new Error(`Unknown tool: ${name}`);
});