Skip to content

Commit c25d464

Browse files
jerome3o-anthropicclaudeochafik
authored
feat: publish example servers to npm (#184)
* feat: publish example servers to npm Make example MCP App servers publishable to npm under the @modelcontextprotocol scope: - server-basic-react - server-basic-vanillajs - server-budget-allocator - server-cohort-heatmap - server-customer-segmentation - server-scenario-modeler - server-system-monitor - server-threejs - server-wiki-explorer Changes: - Remove `private: true` from example package.json files - Add proper npm metadata (description, repository, license, files) - Update ext-apps dependency from relative to ^0.2.2 - Copy server-utils.ts into each package for standalone use - Add publish-examples job to npm-publish.yml workflow The examples will be published automatically when a GitHub release is created, after the main SDK package is published. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: publish examples with pkg-pr-new for PR testing * feat: export createServer from example servers * style: format integration-server/server.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: update e2e snapshots for basic-server examples Update golden screenshots for basic-react and basic-vanillajs after the tool output was simplified from JSON to plain text in #182. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * merge * 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 * update package-lock * fix: update package-lock.json for renamed server packages * feat: add NASA public domain video to video-resource-server Added 'nasa-earth' option - NASA ISS Earth observations (6.4MB). Source: archive.org/details/NASA-Ultra-High-Definition (public domain) * fix: add default videoId for video-resource-server * fix: use existing bunny-1mb as default video, remove nasa-earth * refactor: update new framework examples to publishing pattern Add publishing metadata for: - @modelcontextprotocol/server-basic-preact - @modelcontextprotocol/server-basic-solid - @modelcontextprotocol/server-basic-svelte - @modelcontextprotocol/server-basic-vue Changes: - Move server-utils.ts from src/ to root - Add npm publishing metadata - Update ext-apps dep to versioned ^0.2.2 - Add stdio mode support in main() * fix: add missing @rollup/rollup-win32-arm64-msvc to optionalDependencies This fixes the Windows ARM64 CI build failure caused by npm's optional dependency bug. The package.json was missing this platform-specific rollup binding. --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Olivier Chafik <ochafik@anthropic.com>
1 parent 8acec0e commit c25d464

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+3627
-2776
lines changed

.github/workflows/npm-publish.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,47 @@ jobs:
8383
- run: npm publish --provenance --access public ${{ steps.npm-tag.outputs.tag }}
8484
env:
8585
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}
86+
87+
publish-examples:
88+
runs-on: ubuntu-latest
89+
if: github.event_name == 'release'
90+
environment: Release
91+
needs: [publish]
92+
93+
permissions:
94+
contents: read
95+
id-token: write
96+
97+
strategy:
98+
fail-fast: false
99+
matrix:
100+
example:
101+
- basic-server-react
102+
- basic-server-vanillajs
103+
- budget-allocator-server
104+
- cohort-heatmap-server
105+
- customer-segmentation-server
106+
- scenario-modeler-server
107+
- system-monitor-server
108+
- threejs-server
109+
- wiki-explorer-server
110+
111+
steps:
112+
- uses: actions/checkout@v4
113+
- uses: oven-sh/setup-bun@v2
114+
with:
115+
bun-version: latest
116+
- uses: actions/setup-node@v4
117+
with:
118+
node-version: "22"
119+
cache: npm
120+
registry-url: "https://registry.npmjs.org"
121+
- run: npm ci
122+
123+
- name: Build example
124+
run: npm run build --workspace examples/${{ matrix.example }}
125+
126+
- name: Publish example
127+
run: npm publish --workspace examples/${{ matrix.example }} --provenance --access public
128+
env:
129+
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}

.github/workflows/publish.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,16 @@ jobs:
2020
cache: npm
2121
- run: npm ci
2222
- run: npm run build
23-
- run: npx pkg-pr-new publish
23+
- run: npm run examples:build
24+
- run: |
25+
npx pkg-pr-new publish \
26+
. \
27+
./examples/basic-server-react \
28+
./examples/basic-server-vanillajs \
29+
./examples/budget-allocator-server \
30+
./examples/cohort-heatmap-server \
31+
./examples/customer-segmentation-server \
32+
./examples/scenario-modeler-server \
33+
./examples/system-monitor-server \
34+
./examples/threejs-server \
35+
./examples/wiki-explorer-server

examples/basic-server-preact/package.json

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
11
{
2-
"name": "basic-server-preact",
3-
"version": "1.0.0",
4-
"private": true,
2+
"name": "@modelcontextprotocol/server-basic-preact",
3+
"version": "0.1.0",
54
"type": "module",
5+
"description": "Basic MCP App Server example using Preact",
6+
"repository": {
7+
"type": "git",
8+
"url": "https://github.com/modelcontextprotocol/ext-apps",
9+
"directory": "examples/basic-server-preact"
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",
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
"preact": "^10.0.0",
1730
"zod": "^4.1.13"
@@ -23,7 +36,7 @@
2336
"@types/node": "^22.0.0",
2437
"concurrently": "^9.2.1",
2538
"cors": "^2.8.5",
26-
"cross-env": "^7.0.3",
39+
"cross-env": "^10.1.0",
2740
"express": "^5.1.0",
2841
"typescript": "^5.9.3",
2942
"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/basic-server-preact/server.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
21
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
33
import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
44
import fs from "node:fs/promises";
55
import path from "node:path";
6-
import { startServer } from "./src/server-utils.js";
6+
import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
7+
import { startServer } from "./server-utils.js";
78

89
const DIST_DIR = path.join(import.meta.dirname, "dist");
910

@@ -55,4 +56,16 @@ function createServer(): McpServer {
5556
return server;
5657
}
5758

58-
startServer(createServer);
59+
async function main() {
60+
if (process.argv.includes("--stdio")) {
61+
await createServer().connect(new StdioServerTransport());
62+
} else {
63+
const port = parseInt(process.env.PORT ?? "3001", 10);
64+
await startServer(createServer, { port, name: "Basic MCP App Server (Preact)" });
65+
}
66+
}
67+
68+
main().catch((e) => {
69+
console.error(e);
70+
process.exit(1);
71+
});

examples/basic-server-preact/src/server-utils.ts

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

examples/basic-server-react/package.json

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
11
{
2-
"name": "basic-server-react",
3-
"version": "1.0.0",
4-
"private": true,
2+
"name": "@modelcontextprotocol/server-basic-react",
3+
"version": "0.1.0",
54
"type": "module",
5+
"description": "Basic MCP App Server example using React",
6+
"repository": {
7+
"type": "git",
8+
"url": "https://github.com/modelcontextprotocol/ext-apps",
9+
"directory": "examples/basic-server-react"
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",
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
"react": "^19.2.0",
1730
"react-dom": "^19.2.0",
@@ -26,6 +39,7 @@
2639
"@vitejs/plugin-react": "^4.3.4",
2740
"concurrently": "^9.2.1",
2841
"cors": "^2.8.5",
42+
"cross-env": "^10.1.0",
2943
"express": "^5.1.0",
3044
"typescript": "^5.9.3",
3145
"vite": "^6.0.0",

0 commit comments

Comments
 (0)