Skip to content

Commit d8a4b14

Browse files
committed
chore: add demo tool to example
1 parent b8d1973 commit d8a4b14

3 files changed

Lines changed: 35 additions & 33 deletions

File tree

examples/server/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
},
1313
"packageManager": "pnpm@10.11.0+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977",
1414
"dependencies": {
15+
"@mcp-ui/server": "^1.0.4",
1516
"@modelcontextprotocol/sdk": "^1.11.1",
1617
"@vis.gl/react-google-maps": "^1.5.2",
1718
"agents": "^0.0.80",

examples/server/pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/server/src/index.ts

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { McpAgent } from "agents/mcp";
22
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
33
import { z } from "zod";
44
import { createRequestHandler } from "react-router";
5+
import { createHtmlResource } from "@mcp-ui/server";
56

67
declare module "react-router" {
78
export interface AppLoadContext {
@@ -26,6 +27,10 @@ export class MyMCP extends McpAgent {
2627
});
2728

2829
async init() {
30+
const requestUrl = this.props.requestUrl as string;
31+
const url = new URL(requestUrl);
32+
const requestHost = url.host;
33+
2934
// Simple addition tool
3035
this.server.tool(
3136
"add",
@@ -37,46 +42,34 @@ export class MyMCP extends McpAgent {
3742

3843
// Calculator tool with multiple operations
3944
this.server.tool(
40-
"calculate",
41-
{
42-
operation: z.enum(["add", "subtract", "multiply", "divide"]),
43-
a: z.number(),
44-
b: z.number(),
45-
},
46-
async ({ operation, a, b }) => {
47-
let result: number;
48-
switch (operation) {
49-
case "add":
50-
result = a + b;
51-
break;
52-
case "subtract":
53-
result = a - b;
54-
break;
55-
case "multiply":
56-
result = a * b;
57-
break;
58-
case "divide":
59-
if (b === 0)
60-
return {
61-
content: [
62-
{
63-
type: "text",
64-
text: "Error: Cannot divide by zero",
65-
},
66-
],
67-
};
68-
result = a / b;
69-
break;
70-
}
71-
return { content: [{ type: "text", text: String(result) }] };
72-
}
45+
"show_task_status",
46+
"Displays a UI for the user to see the status of tasks",
47+
async () => {
48+
const scheme = requestHost.includes('localhost') || requestHost.includes('127.0.0.1') ? 'http' : 'https';
49+
50+
const pickerPageUrl = `${scheme}://${requestHost}/task`;
51+
52+
// Generate a unique URI for this specific invocation of the file picker UI.
53+
// This URI identifies the resource block itself, not the content of the iframe.
54+
const uniqueUiAppUri = `ui-app://task-manager/${Date.now()}`;
55+
const resourceBlock = createHtmlResource({
56+
uri: uniqueUiAppUri,
57+
content: { type: "externalUrl", iframeUrl: pickerPageUrl },
58+
delivery: "text" // The URL itself is delivered as text
59+
});
60+
61+
return {
62+
content: [resourceBlock]
63+
};
64+
}
7365
);
7466
}
7567
}
7668

7769
export default {
7870
fetch(request: Request, env: Env, ctx: ExecutionContext) {
7971
const url = new URL(request.url);
72+
ctx.props.requestUrl = request.url;
8073

8174
if (url.pathname === "/sse" || url.pathname === "/sse/message") {
8275
return MyMCP.serveSSE("/sse").fetch(request, env, ctx);

0 commit comments

Comments
 (0)