Skip to content

Commit e0c41c6

Browse files
committed
refactor: update sheet-music-server and video-resource-server to new publishing pattern
- Move server-utils.ts from src/ to root level - Add npm publishing metadata (scoped name, repository, license, files) - Change ext-apps dependency from local to versioned ^0.2.2 - Update server.ts to handle stdio mode inline
1 parent c5c9084 commit e0c41c6

8 files changed

Lines changed: 199 additions & 241 deletions

File tree

examples/sheet-music-server/package.json

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
11
{
2-
"name": "sheet-music-server",
3-
"version": "1.0.0",
4-
"private": true,
2+
"name": "@modelcontextprotocol/server-sheet-music",
3+
"version": "0.1.0",
54
"type": "module",
5+
"description": "MCP App Server for rendering and playing sheet music from ABC notation",
6+
"repository": {
7+
"type": "git",
8+
"url": "https://github.com/modelcontextprotocol/ext-apps",
9+
"directory": "examples/sheet-music-server"
10+
},
11+
"license": "MIT",
12+
"main": "server.ts",
13+
"files": [
14+
"server.ts",
15+
"server-utils.ts",
16+
"dist"
17+
],
618
"scripts": {
719
"build": "tsc --noEmit && cross-env INPUT=mcp-app.html vite build",
820
"watch": "cross-env INPUT=mcp-app.html vite build --watch",
9-
"serve:http": "bun server.ts",
10-
"serve:stdio": "bun server.ts --stdio",
11-
"start": "npm run start:http",
12-
"start:http": "cross-env NODE_ENV=development npm run build && npm run serve:http",
13-
"start:stdio": "cross-env NODE_ENV=development npm run build && npm run serve:stdio",
14-
"dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'"
21+
"serve": "bun 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"
1525
},
1626
"dependencies": {
17-
"@modelcontextprotocol/ext-apps": "../..",
27+
"@modelcontextprotocol/ext-apps": "^0.2.2",
1828
"@modelcontextprotocol/sdk": "^1.24.0",
1929
"abcjs": "^6.4.4",
2030
"zod": "^4.1.13"
@@ -25,7 +35,7 @@
2535
"@types/node": "^22.0.0",
2636
"concurrently": "^9.2.1",
2737
"cors": "^2.8.5",
28-
"cross-env": "^7.0.3",
38+
"cross-env": "^10.1.0",
2939
"express": "^5.1.0",
3040
"typescript": "^5.9.3",
3141
"vite": "^6.0.0",
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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, () => {
58+
console.log(`${name} listening on http://localhost:${port}/mcp`);
59+
});
60+
61+
const shutdown = () => {
62+
console.log("\nShutting down...");
63+
httpServer.close(() => process.exit(0));
64+
};
65+
66+
process.on("SIGINT", shutdown);
67+
process.on("SIGTERM", shutdown);
68+
}

examples/sheet-music-server/server.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import {
1313
registerAppResource,
1414
registerAppTool,
1515
} from "@modelcontextprotocol/ext-apps/server";
16-
import { startServer } from "./src/server-utils.js";
16+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
17+
import { startServer } from "./server-utils.js";
1718

1819
const DIST_DIR = path.join(import.meta.dirname, "dist");
1920

@@ -116,4 +117,16 @@ function createServer(): McpServer {
116117
return server;
117118
}
118119

119-
startServer(createServer);
120+
async function main() {
121+
if (process.argv.includes("--stdio")) {
122+
await createServer().connect(new StdioServerTransport());
123+
} else {
124+
const port = parseInt(process.env.PORT ?? "3001", 10);
125+
await startServer(createServer, { port, name: "Sheet Music Server" });
126+
}
127+
}
128+
129+
main().catch((e) => {
130+
console.error(e);
131+
process.exit(1);
132+
});

examples/sheet-music-server/src/server-utils.ts

Lines changed: 0 additions & 110 deletions
This file was deleted.

examples/video-resource-server/package.json

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
11
{
2-
"name": "video-resource-server",
3-
"version": "1.0.0",
4-
"private": true,
2+
"name": "@modelcontextprotocol/server-video-resource",
3+
"version": "0.1.0",
54
"type": "module",
5+
"description": "MCP App Server demonstrating video resources served as base64 blobs",
6+
"repository": {
7+
"type": "git",
8+
"url": "https://github.com/modelcontextprotocol/ext-apps",
9+
"directory": "examples/video-resource-server"
10+
},
11+
"license": "MIT",
12+
"main": "server.ts",
13+
"files": [
14+
"server.ts",
15+
"server-utils.ts",
16+
"dist"
17+
],
618
"scripts": {
719
"build": "cross-env INPUT=mcp-app.html vite build",
820
"watch": "cross-env INPUT=mcp-app.html vite build --watch",
921
"serve": "bun server.ts",
1022
"start": "cross-env NODE_ENV=development npm run build && npm run serve",
11-
"dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve'"
23+
"dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve'",
24+
"prepublishOnly": "npm run build"
1225
},
1326
"dependencies": {
14-
"@modelcontextprotocol/ext-apps": "../..",
27+
"@modelcontextprotocol/ext-apps": "^0.2.2",
1528
"@modelcontextprotocol/sdk": "^1.24.0",
1629
"zod": "^4.1.13"
1730
},
@@ -21,7 +34,7 @@
2134
"@types/node": "^22.0.0",
2235
"concurrently": "^9.2.1",
2336
"cors": "^2.8.5",
24-
"cross-env": "^7.0.3",
37+
"cross-env": "^10.1.0",
2538
"express": "^5.1.0",
2639
"typescript": "^5.9.3",
2740
"vite": "^6.0.0",
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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, () => {
58+
console.log(`${name} listening on http://localhost:${port}/mcp`);
59+
});
60+
61+
const shutdown = () => {
62+
console.log("\nShutting down...");
63+
httpServer.close(() => process.exit(0));
64+
};
65+
66+
process.on("SIGINT", shutdown);
67+
process.on("SIGTERM", shutdown);
68+
}

examples/video-resource-server/server.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import {
2121
RESOURCE_MIME_TYPE,
2222
RESOURCE_URI_META_KEY,
2323
} from "@modelcontextprotocol/ext-apps/server";
24-
import { startServer } from "./src/server-utils.js";
24+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
25+
import { startServer } from "./server-utils.js";
2526

2627
const DIST_DIR = path.join(import.meta.dirname, "dist");
2728
const RESOURCE_URI = "ui://video-player/mcp-app.html";
@@ -168,7 +169,12 @@ ${Object.entries(VIDEO_LIBRARY)
168169
}
169170

170171
async function main() {
171-
await startServer(createServer);
172+
if (process.argv.includes("--stdio")) {
173+
await createServer().connect(new StdioServerTransport());
174+
} else {
175+
const port = parseInt(process.env.PORT ?? "3001", 10);
176+
await startServer(createServer, { port, name: "Video Resource Server" });
177+
}
172178
}
173179

174180
main().catch((e) => {

0 commit comments

Comments
 (0)