Skip to content

Commit 9fa2c2a

Browse files
Make example servers self-contained with local server-utils
Copy `server-utils.ts` into each example's `src/` directory so examples can be used standalone without the shared directory. Updates imports from `"../shared/server-utils.js"` to `"./src/server-utils.js"`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent e8f7ec1 commit 9fa2c2a

File tree

18 files changed

+889
-9
lines changed

18 files changed

+889
-9
lines changed

examples/basic-server-react/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/s
33
import fs from "node:fs/promises";
44
import path from "node:path";
55
import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE, RESOURCE_URI_META_KEY } from "@modelcontextprotocol/ext-apps/server";
6-
import { startServer } from "../shared/server-utils.js";
6+
import { startServer } from "./src/server-utils.js";
77

88
const DIST_DIR = path.join(import.meta.dirname, "dist");
99
const RESOURCE_URI = "ui://get-time/mcp-app.html";
File renamed without changes.

examples/basic-server-vanillajs/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/s
33
import fs from "node:fs/promises";
44
import path from "node:path";
55
import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE, RESOURCE_URI_META_KEY } from "@modelcontextprotocol/ext-apps/server";
6-
import { startServer } from "../shared/server-utils.js";
6+
import { startServer } from "./src/server-utils.js";
77

88
const DIST_DIR = path.join(import.meta.dirname, "dist");
99
const RESOURCE_URI = "ui://get-time/mcp-app.html";
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* Shared utilities for running MCP servers with various transports.
3+
*/
4+
5+
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
6+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
9+
import cors from "cors";
10+
import type { Request, Response } from "express";
11+
12+
/**
13+
* Starts an MCP server using the appropriate transport based on command-line arguments.
14+
*
15+
* If `--stdio` is passed, uses stdio transport. Otherwise, uses Streamable HTTP transport.
16+
*
17+
* @param createServer - Factory function that creates a new McpServer instance.
18+
*/
19+
export async function startServer(
20+
createServer: () => McpServer,
21+
): Promise<void> {
22+
try {
23+
if (process.argv.includes("--stdio")) {
24+
await startStdioServer(createServer);
25+
} else {
26+
await startStreamableHttpServer(createServer);
27+
}
28+
} catch (e) {
29+
console.error(e);
30+
process.exit(1);
31+
}
32+
}
33+
34+
/**
35+
* Starts an MCP server with stdio transport.
36+
*
37+
* @param createServer - Factory function that creates a new McpServer instance.
38+
*/
39+
export async function startStdioServer(
40+
createServer: () => McpServer,
41+
): Promise<void> {
42+
await createServer().connect(new StdioServerTransport());
43+
}
44+
45+
/**
46+
* Starts an MCP server with Streamable HTTP transport in stateless mode.
47+
*
48+
* Each request creates a fresh server and transport instance, which are
49+
* closed when the response ends (no session tracking).
50+
*
51+
* The server listens on the port specified by the PORT environment variable,
52+
* defaulting to 3001 if not set.
53+
*
54+
* @param createServer - Factory function that creates a new McpServer instance per request.
55+
*/
56+
export async function startStreamableHttpServer(
57+
createServer: () => McpServer,
58+
): Promise<void> {
59+
const port = parseInt(process.env.PORT ?? "3001", 10);
60+
61+
// Express app - bind to all interfaces for development/testing
62+
const expressApp = createMcpExpressApp({ host: "0.0.0.0" });
63+
expressApp.use(cors());
64+
65+
expressApp.all("/mcp", async (req: Request, res: Response) => {
66+
// Create fresh server and transport for each request (stateless mode)
67+
const server = createServer();
68+
const transport = new StreamableHTTPServerTransport({
69+
sessionIdGenerator: undefined,
70+
});
71+
72+
// Clean up when response ends
73+
res.on("close", () => {
74+
transport.close().catch(() => {});
75+
server.close().catch(() => {});
76+
});
77+
78+
try {
79+
await server.connect(transport);
80+
await transport.handleRequest(req, res, req.body);
81+
} catch (error) {
82+
console.error("MCP error:", error);
83+
if (!res.headersSent) {
84+
res.status(500).json({
85+
jsonrpc: "2.0",
86+
error: { code: -32603, message: "Internal server error" },
87+
id: null,
88+
});
89+
}
90+
}
91+
});
92+
93+
const { promise, resolve, reject } = Promise.withResolvers<void>();
94+
95+
const httpServer = expressApp.listen(port, (err?: Error) => {
96+
if (err) return reject(err);
97+
console.log(`Server listening on http://localhost:${port}/mcp`);
98+
resolve();
99+
});
100+
101+
const shutdown = () => {
102+
console.log("\nShutting down...");
103+
httpServer.close(() => process.exit(0));
104+
};
105+
106+
process.on("SIGINT", shutdown);
107+
process.on("SIGTERM", shutdown);
108+
109+
return promise;
110+
}

examples/budget-allocator-server/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
registerAppResource,
1919
registerAppTool,
2020
} from "@modelcontextprotocol/ext-apps/server";
21-
import { startServer } from "../shared/server-utils.js";
21+
import { startServer } from "./src/server-utils.js";
2222

2323
const DIST_DIR = path.join(import.meta.dirname, "dist");
2424

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* Shared utilities for running MCP servers with various transports.
3+
*/
4+
5+
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
6+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
9+
import cors from "cors";
10+
import type { Request, Response } from "express";
11+
12+
/**
13+
* Starts an MCP server using the appropriate transport based on command-line arguments.
14+
*
15+
* If `--stdio` is passed, uses stdio transport. Otherwise, uses Streamable HTTP transport.
16+
*
17+
* @param createServer - Factory function that creates a new McpServer instance.
18+
*/
19+
export async function startServer(
20+
createServer: () => McpServer,
21+
): Promise<void> {
22+
try {
23+
if (process.argv.includes("--stdio")) {
24+
await startStdioServer(createServer);
25+
} else {
26+
await startStreamableHttpServer(createServer);
27+
}
28+
} catch (e) {
29+
console.error(e);
30+
process.exit(1);
31+
}
32+
}
33+
34+
/**
35+
* Starts an MCP server with stdio transport.
36+
*
37+
* @param createServer - Factory function that creates a new McpServer instance.
38+
*/
39+
export async function startStdioServer(
40+
createServer: () => McpServer,
41+
): Promise<void> {
42+
await createServer().connect(new StdioServerTransport());
43+
}
44+
45+
/**
46+
* Starts an MCP server with Streamable HTTP transport in stateless mode.
47+
*
48+
* Each request creates a fresh server and transport instance, which are
49+
* closed when the response ends (no session tracking).
50+
*
51+
* The server listens on the port specified by the PORT environment variable,
52+
* defaulting to 3001 if not set.
53+
*
54+
* @param createServer - Factory function that creates a new McpServer instance per request.
55+
*/
56+
export async function startStreamableHttpServer(
57+
createServer: () => McpServer,
58+
): Promise<void> {
59+
const port = parseInt(process.env.PORT ?? "3001", 10);
60+
61+
// Express app - bind to all interfaces for development/testing
62+
const expressApp = createMcpExpressApp({ host: "0.0.0.0" });
63+
expressApp.use(cors());
64+
65+
expressApp.all("/mcp", async (req: Request, res: Response) => {
66+
// Create fresh server and transport for each request (stateless mode)
67+
const server = createServer();
68+
const transport = new StreamableHTTPServerTransport({
69+
sessionIdGenerator: undefined,
70+
});
71+
72+
// Clean up when response ends
73+
res.on("close", () => {
74+
transport.close().catch(() => {});
75+
server.close().catch(() => {});
76+
});
77+
78+
try {
79+
await server.connect(transport);
80+
await transport.handleRequest(req, res, req.body);
81+
} catch (error) {
82+
console.error("MCP error:", error);
83+
if (!res.headersSent) {
84+
res.status(500).json({
85+
jsonrpc: "2.0",
86+
error: { code: -32603, message: "Internal server error" },
87+
id: null,
88+
});
89+
}
90+
}
91+
});
92+
93+
const { promise, resolve, reject } = Promise.withResolvers<void>();
94+
95+
const httpServer = expressApp.listen(port, (err?: Error) => {
96+
if (err) return reject(err);
97+
console.log(`Server listening on http://localhost:${port}/mcp`);
98+
resolve();
99+
});
100+
101+
const shutdown = () => {
102+
console.log("\nShutting down...");
103+
httpServer.close(() => process.exit(0));
104+
};
105+
106+
process.on("SIGINT", shutdown);
107+
process.on("SIGTERM", shutdown);
108+
109+
return promise;
110+
}

examples/cohort-heatmap-server/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
registerAppResource,
1010
registerAppTool,
1111
} from "@modelcontextprotocol/ext-apps/server";
12-
import { startServer } from "../shared/server-utils.js";
12+
import { startServer } from "./src/server-utils.js";
1313

1414
const DIST_DIR = path.join(import.meta.dirname, "dist");
1515

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* Shared utilities for running MCP servers with various transports.
3+
*/
4+
5+
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
6+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
9+
import cors from "cors";
10+
import type { Request, Response } from "express";
11+
12+
/**
13+
* Starts an MCP server using the appropriate transport based on command-line arguments.
14+
*
15+
* If `--stdio` is passed, uses stdio transport. Otherwise, uses Streamable HTTP transport.
16+
*
17+
* @param createServer - Factory function that creates a new McpServer instance.
18+
*/
19+
export async function startServer(
20+
createServer: () => McpServer,
21+
): Promise<void> {
22+
try {
23+
if (process.argv.includes("--stdio")) {
24+
await startStdioServer(createServer);
25+
} else {
26+
await startStreamableHttpServer(createServer);
27+
}
28+
} catch (e) {
29+
console.error(e);
30+
process.exit(1);
31+
}
32+
}
33+
34+
/**
35+
* Starts an MCP server with stdio transport.
36+
*
37+
* @param createServer - Factory function that creates a new McpServer instance.
38+
*/
39+
export async function startStdioServer(
40+
createServer: () => McpServer,
41+
): Promise<void> {
42+
await createServer().connect(new StdioServerTransport());
43+
}
44+
45+
/**
46+
* Starts an MCP server with Streamable HTTP transport in stateless mode.
47+
*
48+
* Each request creates a fresh server and transport instance, which are
49+
* closed when the response ends (no session tracking).
50+
*
51+
* The server listens on the port specified by the PORT environment variable,
52+
* defaulting to 3001 if not set.
53+
*
54+
* @param createServer - Factory function that creates a new McpServer instance per request.
55+
*/
56+
export async function startStreamableHttpServer(
57+
createServer: () => McpServer,
58+
): Promise<void> {
59+
const port = parseInt(process.env.PORT ?? "3001", 10);
60+
61+
// Express app - bind to all interfaces for development/testing
62+
const expressApp = createMcpExpressApp({ host: "0.0.0.0" });
63+
expressApp.use(cors());
64+
65+
expressApp.all("/mcp", async (req: Request, res: Response) => {
66+
// Create fresh server and transport for each request (stateless mode)
67+
const server = createServer();
68+
const transport = new StreamableHTTPServerTransport({
69+
sessionIdGenerator: undefined,
70+
});
71+
72+
// Clean up when response ends
73+
res.on("close", () => {
74+
transport.close().catch(() => {});
75+
server.close().catch(() => {});
76+
});
77+
78+
try {
79+
await server.connect(transport);
80+
await transport.handleRequest(req, res, req.body);
81+
} catch (error) {
82+
console.error("MCP error:", error);
83+
if (!res.headersSent) {
84+
res.status(500).json({
85+
jsonrpc: "2.0",
86+
error: { code: -32603, message: "Internal server error" },
87+
id: null,
88+
});
89+
}
90+
}
91+
});
92+
93+
const { promise, resolve, reject } = Promise.withResolvers<void>();
94+
95+
const httpServer = expressApp.listen(port, (err?: Error) => {
96+
if (err) return reject(err);
97+
console.log(`Server listening on http://localhost:${port}/mcp`);
98+
resolve();
99+
});
100+
101+
const shutdown = () => {
102+
console.log("\nShutting down...");
103+
httpServer.close(() => process.exit(0));
104+
};
105+
106+
process.on("SIGINT", shutdown);
107+
process.on("SIGTERM", shutdown);
108+
109+
return promise;
110+
}

examples/customer-segmentation-server/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
registerAppResource,
1313
registerAppTool,
1414
} from "@modelcontextprotocol/ext-apps/server";
15-
import { startServer } from "../shared/server-utils.js";
15+
import { startServer } from "./src/server-utils.js";
1616
import {
1717
generateCustomers,
1818
generateSegmentSummaries,

0 commit comments

Comments
 (0)