Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ jobs:
shell: bash
run: '! grep -E "\"resolved\": \"https?://" package-lock.json | grep -v registry.npmjs.org'

- name: Verify example dependency versions
shell: bash
run: node scripts/check-versions.mjs

- uses: actions/setup-node@v4
with:
node-version: "20"
Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,47 @@ jobs:
- run: npm publish --provenance --access public ${{ steps.npm-tag.outputs.tag }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}

publish-examples:
runs-on: ubuntu-latest
if: github.event_name == 'release'
environment: Release
needs: [publish]

permissions:
contents: read
id-token: write

strategy:
fail-fast: false
matrix:
example:
- basic-server-react
- basic-server-vanillajs
- budget-allocator-server
- cohort-heatmap-server
- customer-segmentation-server
- scenario-modeler-server
- system-monitor-server
- threejs-server
- wiki-explorer-server

steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
registry-url: "https://registry.npmjs.org"
- run: npm ci

- name: Build example
run: npm run build --workspace examples/${{ matrix.example }}

- name: Publish example
run: npm publish --workspace examples/${{ matrix.example }} --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}
14 changes: 13 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,16 @@ jobs:
cache: npm
- run: npm ci
- run: npm run build
- run: npx pkg-pr-new publish
- run: npm run examples:build
- run: |
npx pkg-pr-new publish \
. \
./examples/basic-server-react \
./examples/basic-server-vanillajs \
./examples/budget-allocator-server \
./examples/cohort-heatmap-server \
./examples/customer-segmentation-server \
./examples/scenario-modeler-server \
./examples/system-monitor-server \
./examples/threejs-server \
./examples/wiki-explorer-server
24 changes: 19 additions & 5 deletions examples/basic-server-react/package.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
{
"name": "basic-server-react",
"version": "1.0.0",
"private": true,
"name": "@modelcontextprotocol/server-basic-react",
"version": "0.1.0",
"type": "module",
"description": "Basic MCP App Server example using React",
"repository": {
"type": "git",
"url": "https://github.com/modelcontextprotocol/ext-apps",
"directory": "examples/basic-server-react"
},
"license": "MIT",
"main": "server.ts",
"files": [
"server.ts",
"server-utils.ts",
"dist"
],
"scripts": {
"build": "tsc --noEmit && cross-env INPUT=mcp-app.html vite build",
"watch": "cross-env INPUT=mcp-app.html vite build --watch",
"serve": "bun server.ts",
"start": "cross-env NODE_ENV=development npm run build && npm run serve",
"dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve'"
"dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve'",
"prepublishOnly": "npm run build"
},
"dependencies": {
"@modelcontextprotocol/ext-apps": "../..",
"@modelcontextprotocol/ext-apps": "^0.2.2",
"@modelcontextprotocol/sdk": "^1.24.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
Expand All @@ -26,6 +39,7 @@
"@vitejs/plugin-react": "^4.3.4",
"concurrently": "^9.2.1",
"cors": "^2.8.5",
"cross-env": "^10.1.0",
"express": "^5.1.0",
"typescript": "^5.9.3",
"vite": "^6.0.0",
Expand Down
68 changes: 68 additions & 0 deletions examples/basic-server-react/server-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Shared utilities for running MCP servers with Streamable HTTP transport.
*/

import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import cors from "cors";
import type { Request, Response } from "express";

export interface ServerOptions {
port: number;
name?: string;
}

/**
* Starts an MCP server with Streamable HTTP transport in stateless mode.
*
* @param createServer - Factory function that creates a new McpServer instance per request.
* @param options - Server configuration options.
*/
export async function startServer(
createServer: () => McpServer,
options: ServerOptions,
): Promise<void> {
const { port, name = "MCP Server" } = options;

const app = createMcpExpressApp({ host: "0.0.0.0" });
app.use(cors());

app.all("/mcp", async (req: Request, res: Response) => {
const server = createServer();
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});

res.on("close", () => {
transport.close().catch(() => {});
server.close().catch(() => {});
});

try {
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
} catch (error) {
console.error("MCP error:", error);
if (!res.headersSent) {
res.status(500).json({
jsonrpc: "2.0",
error: { code: -32603, message: "Internal server error" },
id: null,
});
}
}
});

const httpServer = app.listen(port, () => {
console.log(`${name} listening on http://localhost:${port}/mcp`);
});

const shutdown = () => {
console.log("\nShutting down...");
httpServer.close(() => process.exit(0));
};

process.on("SIGINT", shutdown);
process.on("SIGTERM", shutdown);
}
29 changes: 19 additions & 10 deletions examples/basic-server-react/server.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises";
import path from "node:path";
import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE, RESOURCE_URI_META_KEY } from "@modelcontextprotocol/ext-apps/server";
import { startServer } from "./src/server-utils.js";
import { startServer } from "./server-utils.js";

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

/**
* Creates a new MCP server instance with tools and resources registered.
*/
function createServer(): McpServer {
export function createServer(): McpServer {
const server = new McpServer({
name: "Basic MCP App Server (React)",
version: "1.0.0",
Expand All @@ -26,13 +27,12 @@ function createServer(): McpServer {
"get-time",
{
title: "Get Time",
description: "Returns the current server time as an ISO 8601 string.",
description: "Returns the current server time.",
inputSchema: {},
_meta: { [RESOURCE_URI_META_KEY]: resourceUri },
},
async (): Promise<CallToolResult> => {
const time = new Date().toISOString();
return { content: [{ type: "text", text: time }] };
return { content: [{ type: "text", text: new Date().toISOString() }] };
},
);

Expand All @@ -43,16 +43,25 @@ function createServer(): McpServer {
{ mimeType: RESOURCE_MIME_TYPE },
async (): Promise<ReadResourceResult> => {
const html = await fs.readFile(path.join(DIST_DIR, "mcp-app.html"), "utf-8");

return {
contents: [
{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html },
],
contents: [{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html }],
};
},
);

return server;
}

startServer(createServer);
async function main() {
if (process.argv.includes("--stdio")) {
await createServer().connect(new StdioServerTransport());
} else {
const port = parseInt(process.env.PORT ?? "3001", 10);
await startServer(createServer, { port, name: "Basic MCP App Server (React)" });
}
}

main().catch((e) => {
console.error(e);
process.exit(1);
});
110 changes: 0 additions & 110 deletions examples/basic-server-react/src/server-utils.ts

This file was deleted.

Loading
Loading