Skip to content

Commit 610fb00

Browse files
committed
feat(examples): Add debug-server for comprehensive SDK testing
Add a debug-server example that exercises all MCP Apps SDK capabilities: Server (server.ts): - debug-tool: Configurable tool testing all content types (text, image, audio, resource, resourceLink, mixed), with options for multiple blocks, structuredContent, _meta, error simulation, and delays - debug-refresh: App-only tool (hidden from model) for polling server state Guest UI (src/mcp-app.ts): - Event Log: Real-time log of all SDK events with filtering and timestamps - Host Info: Display of context, capabilities, container dimensions, styles - Callback Status: Table showing all registered callbacks with call counts - Action Buttons: Test every SDK method: - Messages (text and image) - Logging (debug/info/warning/error) - Model context updates (text and structured) - Display mode requests (inline/fullscreen/pip) - Link opening - Manual/auto resize controls - Server tool calls with full configuration - File upload and URL retrieval This example serves as both a testing tool and reference implementation for all SDK features.
1 parent 8c3b1da commit 610fb00

9 files changed

Lines changed: 1552 additions & 0 deletions

File tree

examples/debug-server/mcp-app.html

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<meta name="color-scheme" content="light dark">
7+
<title>Debug App</title>
8+
</head>
9+
<body>
10+
<main class="main">
11+
<!-- Section 1: Event Log -->
12+
<section class="section">
13+
<h2 class="section-header">
14+
<span>Event Log</span>
15+
<div class="header-actions">
16+
<select id="log-filter">
17+
<option value="all">All Events</option>
18+
<option value="tool-input">tool-input</option>
19+
<option value="tool-input-partial">tool-input-partial</option>
20+
<option value="tool-result">tool-result</option>
21+
<option value="tool-cancelled">tool-cancelled</option>
22+
<option value="widget-state">widget-state</option>
23+
<option value="host-context-changed">host-context-changed</option>
24+
<option value="teardown">teardown</option>
25+
<option value="call-tool">call-tool</option>
26+
<option value="list-tools">list-tools</option>
27+
<option value="error">error</option>
28+
</select>
29+
<button id="clear-log-btn" class="btn-small">Clear</button>
30+
</div>
31+
</h2>
32+
<div id="event-log" class="event-log"></div>
33+
</section>
34+
35+
<!-- Section 2: Host Info -->
36+
<section class="section collapsible">
37+
<h2 class="section-header" data-toggle="host-info-content">
38+
<span>Host Info</span>
39+
<span class="toggle-icon"></span>
40+
</h2>
41+
<div id="host-info-content" class="section-content">
42+
<div class="info-grid">
43+
<div class="info-group">
44+
<h3>Context</h3>
45+
<dl id="host-context-info"></dl>
46+
</div>
47+
<div class="info-group">
48+
<h3>Capabilities</h3>
49+
<dl id="host-capabilities-info"></dl>
50+
</div>
51+
<div class="info-group">
52+
<h3>Container</h3>
53+
<dl id="host-container-info"></dl>
54+
</div>
55+
<div class="info-group">
56+
<h3>Styles Sample</h3>
57+
<div id="host-styles-sample" class="styles-sample"></div>
58+
</div>
59+
</div>
60+
</div>
61+
</section>
62+
63+
<!-- Section 3: Callback Status -->
64+
<section class="section collapsible">
65+
<h2 class="section-header" data-toggle="callback-status-content">
66+
<span>Callback Status</span>
67+
<span class="toggle-icon"></span>
68+
</h2>
69+
<div id="callback-status-content" class="section-content">
70+
<table class="callback-table">
71+
<thead>
72+
<tr>
73+
<th>Callback</th>
74+
<th>Registered</th>
75+
<th>Count</th>
76+
<th>Last Payload</th>
77+
</tr>
78+
</thead>
79+
<tbody id="callback-table-body"></tbody>
80+
</table>
81+
</div>
82+
</section>
83+
84+
<!-- Section 4: Action Buttons -->
85+
<section class="section collapsible">
86+
<h2 class="section-header" data-toggle="actions-content">
87+
<span>Actions</span>
88+
<span class="toggle-icon"></span>
89+
</h2>
90+
<div id="actions-content" class="section-content">
91+
<!-- Messages -->
92+
<div class="action-group">
93+
<h3>Messages</h3>
94+
<div class="action-row">
95+
<input type="text" id="message-text" value="Hello from debug app!" placeholder="Message text">
96+
<button id="send-message-text-btn">Send Text</button>
97+
</div>
98+
<div class="action-row">
99+
<button id="send-message-image-btn">Send Test Image</button>
100+
</div>
101+
</div>
102+
103+
<!-- Logging -->
104+
<div class="action-group">
105+
<h3>Logging</h3>
106+
<div class="action-row">
107+
<input type="text" id="log-data" value="Debug log data" placeholder="Log data">
108+
</div>
109+
<div class="action-row btn-row">
110+
<button id="log-debug-btn" class="btn-small">debug</button>
111+
<button id="log-info-btn" class="btn-small">info</button>
112+
<button id="log-warning-btn" class="btn-small">warning</button>
113+
<button id="log-error-btn" class="btn-small">error</button>
114+
</div>
115+
</div>
116+
117+
<!-- Model Context -->
118+
<div class="action-group">
119+
<h3>Model Context</h3>
120+
<div class="action-row">
121+
<input type="text" id="context-text" value="Current app state info" placeholder="Context text">
122+
<button id="update-context-text-btn">Update (Text)</button>
123+
</div>
124+
<div class="action-row">
125+
<button id="update-context-structured-btn">Update (Structured)</button>
126+
</div>
127+
</div>
128+
129+
<!-- Display Mode -->
130+
<div class="action-group">
131+
<h3>Display Mode</h3>
132+
<div class="action-row btn-row">
133+
<button id="display-inline-btn" class="btn-small">Inline</button>
134+
<button id="display-fullscreen-btn" class="btn-small">Fullscreen</button>
135+
<button id="display-pip-btn" class="btn-small">PiP</button>
136+
</div>
137+
</div>
138+
139+
<!-- Links -->
140+
<div class="action-group">
141+
<h3>Links</h3>
142+
<div class="action-row">
143+
<input type="url" id="link-url" value="https://modelcontextprotocol.io/" placeholder="URL">
144+
<button id="open-link-btn">Open Link</button>
145+
</div>
146+
</div>
147+
148+
<!-- Size Controls -->
149+
<div class="action-group">
150+
<h3>Size</h3>
151+
<div class="action-row">
152+
<label><input type="checkbox" id="auto-resize-toggle" checked> Auto-resize</label>
153+
</div>
154+
<div class="action-row btn-row">
155+
<button id="resize-200x100-btn" class="btn-small">200x100</button>
156+
<button id="resize-400x300-btn" class="btn-small">400x300</button>
157+
<button id="resize-800x600-btn" class="btn-small">800x600</button>
158+
</div>
159+
<div class="action-row">
160+
<span>Current: <code id="current-size">measuring...</code></span>
161+
</div>
162+
</div>
163+
164+
<!-- Server Tools -->
165+
<div class="action-group">
166+
<h3>Server Tools</h3>
167+
<div class="tool-config">
168+
<div class="config-row">
169+
<label>Content Type:</label>
170+
<select id="tool-content-type">
171+
<option value="text">text</option>
172+
<option value="image">image</option>
173+
<option value="audio">audio</option>
174+
<option value="resource">resource</option>
175+
<option value="resourceLink">resourceLink</option>
176+
<option value="mixed">mixed</option>
177+
</select>
178+
</div>
179+
<div class="config-row">
180+
<label><input type="checkbox" id="tool-multiple-blocks"> Multiple blocks</label>
181+
</div>
182+
<div class="config-row">
183+
<label><input type="checkbox" id="tool-structured-content" checked> Include structuredContent</label>
184+
</div>
185+
<div class="config-row">
186+
<label><input type="checkbox" id="tool-include-meta"> Include _meta</label>
187+
</div>
188+
<div class="config-row">
189+
<label><input type="checkbox" id="tool-simulate-error"> Simulate error</label>
190+
</div>
191+
<div class="config-row">
192+
<label>Delay (ms):</label>
193+
<input type="number" id="tool-delay-ms" value="0" min="0" max="10000" step="100">
194+
</div>
195+
</div>
196+
<div class="action-row">
197+
<button id="call-debug-tool-btn">Call debug-tool</button>
198+
</div>
199+
<div class="action-row">
200+
<button id="call-debug-refresh-btn">Call debug-refresh</button>
201+
</div>
202+
</div>
203+
204+
<!-- File Operations -->
205+
<div class="action-group">
206+
<h3>Files</h3>
207+
<div class="action-row">
208+
<input type="file" id="file-input">
209+
</div>
210+
<div class="action-row">
211+
<button id="upload-file-btn">Upload File</button>
212+
</div>
213+
<div class="action-row">
214+
<span>Last fileId: <code id="last-file-id">none</code></span>
215+
</div>
216+
<div class="action-row">
217+
<button id="get-file-url-btn">Get File URL</button>
218+
</div>
219+
</div>
220+
</div>
221+
</section>
222+
</main>
223+
<script type="module" src="/src/mcp-app.ts"></script>
224+
</body>
225+
</html>

examples/debug-server/package.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"name": "@modelcontextprotocol/server-debug",
3+
"version": "0.4.0",
4+
"type": "module",
5+
"description": "Debug MCP App Server for testing all SDK capabilities",
6+
"repository": {
7+
"type": "git",
8+
"url": "https://github.com/modelcontextprotocol/ext-apps",
9+
"directory": "examples/debug-server"
10+
},
11+
"license": "MIT",
12+
"main": "server.ts",
13+
"files": [
14+
"server.ts",
15+
"server-utils.ts",
16+
"dist"
17+
],
18+
"scripts": {
19+
"build": "tsc --noEmit && cross-env INPUT=mcp-app.html vite build",
20+
"watch": "cross-env INPUT=mcp-app.html vite build --watch",
21+
"serve": "bun --watch server.ts",
22+
"start": "cross-env NODE_ENV=development npm run build && npm run serve",
23+
"dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve'",
24+
"prepublishOnly": "npm run build"
25+
},
26+
"dependencies": {
27+
"@modelcontextprotocol/ext-apps": "^0.4.0",
28+
"@modelcontextprotocol/sdk": "^1.24.0",
29+
"zod": "^4.1.13"
30+
},
31+
"devDependencies": {
32+
"@types/cors": "^2.8.19",
33+
"@types/express": "^5.0.0",
34+
"@types/node": "^22.0.0",
35+
"concurrently": "^9.2.1",
36+
"cors": "^2.8.5",
37+
"cross-env": "^10.1.0",
38+
"express": "^5.1.0",
39+
"typescript": "^5.9.3",
40+
"vite": "^6.0.0",
41+
"vite-plugin-singlefile": "^2.3.0"
42+
}
43+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* Shared utilities for running MCP servers with Streamable HTTP transport.
3+
*/
4+
5+
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
6+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
8+
import cors from "cors";
9+
import type { Request, Response } from "express";
10+
11+
export interface ServerOptions {
12+
port: number;
13+
name?: string;
14+
}
15+
16+
/**
17+
* Starts an MCP server with Streamable HTTP transport in stateless mode.
18+
*
19+
* @param createServer - Factory function that creates a new McpServer instance per request.
20+
* @param options - Server configuration options.
21+
*/
22+
export async function startServer(
23+
createServer: () => McpServer,
24+
options: ServerOptions,
25+
): Promise<void> {
26+
const { port, name = "MCP Server" } = options;
27+
28+
const app = createMcpExpressApp({ host: "0.0.0.0" });
29+
app.use(cors());
30+
31+
app.all("/mcp", async (req: Request, res: Response) => {
32+
const server = createServer();
33+
const transport = new StreamableHTTPServerTransport({
34+
sessionIdGenerator: undefined,
35+
});
36+
37+
res.on("close", () => {
38+
transport.close().catch(() => {});
39+
server.close().catch(() => {});
40+
});
41+
42+
try {
43+
await server.connect(transport);
44+
await transport.handleRequest(req, res, req.body);
45+
} catch (error) {
46+
console.error("MCP error:", error);
47+
if (!res.headersSent) {
48+
res.status(500).json({
49+
jsonrpc: "2.0",
50+
error: { code: -32603, message: "Internal server error" },
51+
id: null,
52+
});
53+
}
54+
}
55+
});
56+
57+
const httpServer = app.listen(port, (err) => {
58+
if (err) {
59+
console.error("Failed to start server:", err);
60+
process.exit(1);
61+
}
62+
console.log(`${name} listening on http://localhost:${port}/mcp`);
63+
});
64+
65+
const shutdown = () => {
66+
console.log("\nShutting down...");
67+
httpServer.close(() => process.exit(0));
68+
};
69+
70+
process.on("SIGINT", shutdown);
71+
process.on("SIGTERM", shutdown);
72+
}

0 commit comments

Comments
 (0)