feat: compile example servers to JavaScript for npm consumption (#259)

* feat: compile example servers to JavaScript for npm consumption

Add TypeScript compilation for server code so the example packages can be
imported and used by consumers without requiring TypeScript compilation.

Changes:
- Add tsconfig.server.json to each example for compiling server code
- Update package.json exports to point to compiled dist/server/server.js
- Add types field pointing to dist/server/server.d.ts
- Update build scripts to include server compilation step
- Fix .ts import extensions to .js in customer-segmentation-server

This enables importing the createServer function from published packages:
  import { createServer } from '@modelcontextprotocol/server-basic-react';

* fix: add type assertion for Nominatim API response

* chore: regenerate schemas

* refactor: split server examples into library and binary entry points

- server.ts: exports createServer() only (library entry point)
- main.ts: entry point with main() that runs the server (binary)
- package.json: adds bin field for npx support, uses inline bun build
- tsconfig.server.json: emitDeclarationOnly, outputs to dist/

This allows:
1. Importing the server as a library: import { createServer } from '@modelcontextprotocol/server-basic-react'
2. Running with npx: npx @modelcontextprotocol/server-basic-react [--stdio]

Added migration script at scripts/migrate-server-examples.ts

* refactor: migrate remaining server examples to library/binary structure

Apply the same refactoring to all 13 remaining server examples:
- scenario-modeler-server, shadertoy-server, map-server
- cohort-heatmap-server, customer-segmentation-server, video-resource-server
- system-monitor-server, budget-allocator-server, sheet-music-server
- threejs-server, integration-server, transcript-server, wiki-explorer-server

Each now has:
- server.ts: exports createServer() (library)
- main.ts: runs the server (binary with shebang)
- bin field in package.json for npx support

* chore: remove unused server-utils.ts from video-resource-server/src

* update

* fix(build): make Windows-compatible by removing chmod and fixing bun externals

- Remove 'chmod +x dist/index.js' which doesn't exist on Windows
- Add '--external ./server.js' to prevent bun from treating the import as a second entry point

* fix(build): use .ts import for bun compatibility

- Change import from './server.js' to './server.ts' in main.ts files
- Remove --external flag since bun should inline everything
- Fixes Windows build where bun couldn't resolve .js to .ts properly

* fix(build): use --external for Windows bun compatibility

Bun on Windows incorrectly treats imported .ts files as entry points.
Use --external './server.js' to work around this, creating a runtime
dependency between index.js and server.js.

* style: fix prettier formatting in pdf-server/main.ts

* fix(build): remove quotes from --external for Windows cmd.exe compatibility

* fix(build): use glob pattern for --external to work on all platforms

The literal path --external ./server.js was not matching the import.
Using glob pattern --external "./server*" correctly externalizes the server module.

* fix(build): use */server.js pattern for --external

The "./server*" pattern was not matching on Windows.
Using "*/server.js" matches any path ending in /server.js which works cross-platform.

* fix(build): use *server.js pattern without slash for Windows compatibility

* update package-lock

* chore: regenerate package-lock.json for cross-platform compatibility

* fix(build): use ./server.js instead of glob pattern for Windows compatibility

* fix(build): use double quotes for banner to fix Windows cmd.exe compatibility

---------

Co-authored-by: Olivier Chafik <ochafik@anthropic.com>
This commit is contained in:
Jerome 2026-01-14 23:46:17 +00:00 • committed by GitHub
commit 65a62a9fae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
99 changed files with 2378 additions and 1740 deletions

View file

@ -0,0 +1,94 @@
/**
* Entry point for running the MCP server.
* Run with: npx mcp-server-basic-preact
* Or: node dist/index.js [--stdio]
*/
/**
* Shared utilities for running MCP servers with Streamable HTTP transport.
*/
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
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";
import { createServer } from "./server.js";
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, (err) => {
if (err) {
console.error("Failed to start server:", err);
process.exit(1);
}
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);
}
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 (Preact)" });
}
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

View file

@ -9,16 +9,14 @@
"directory": "examples/basic-server-preact" "directory": "examples/basic-server-preact"
}, },
"license": "MIT", "license": "MIT",
"main": "server.ts", "main": "dist/server.js",
"files": [ "files": [
"server.ts",
"server-utils.ts",
"dist" "dist"
], ],
"scripts": { "scripts": {
"build": "tsc --noEmit && cross-env INPUT=mcp-app.html vite build", "build": "tsc --noEmit && cross-env INPUT=mcp-app.html vite build && tsc -p tsconfig.server.json && bun build server.ts --outdir dist --target node && bun build main.ts --outfile dist/index.js --target node --external \"./server.js\" --banner \"#!/usr/bin/env node\"",
"watch": "cross-env INPUT=mcp-app.html vite build --watch", "watch": "cross-env INPUT=mcp-app.html vite build --watch",
"serve": "bun --watch server.ts", "serve": "bun --watch main.ts",
"start": "cross-env NODE_ENV=development npm run build && npm run serve", "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" "prepublishOnly": "npm run build"
@ -27,7 +25,9 @@
"@modelcontextprotocol/ext-apps": "^0.4.0", "@modelcontextprotocol/ext-apps": "^0.4.0",
"@modelcontextprotocol/sdk": "^1.24.0", "@modelcontextprotocol/sdk": "^1.24.0",
"preact": "^10.0.0", "preact": "^10.0.0",
"zod": "^4.1.13" "zod": "^4.1.13",
"cors": "^2.8.5",
"express": "^5.1.0"
}, },
"devDependencies": { "devDependencies": {
"@preact/preset-vite": "^2.0.0", "@preact/preset-vite": "^2.0.0",
@ -35,11 +35,19 @@
"@types/express": "^5.0.0", "@types/express": "^5.0.0",
"@types/node": "^22.0.0", "@types/node": "^22.0.0",
"concurrently": "^9.2.1", "concurrently": "^9.2.1",
"cors": "^2.8.5",
"cross-env": "^10.1.0", "cross-env": "^10.1.0",
"express": "^5.1.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vite": "^6.0.0", "vite": "^6.0.0",
"vite-plugin-singlefile": "^2.3.0" "vite-plugin-singlefile": "^2.3.0"
},
"types": "dist/server.d.ts",
"exports": {
".": {
"types": "./dist/server.d.ts",
"default": "./dist/server.js"
}
},
"bin": {
"mcp-server-basic-preact": "dist/index.js"
} }
} }

View file

@ -1,17 +1,14 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 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 type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises"; import fs from "node:fs/promises";
import path from "node:path"; import path from "node:path";
import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server"; import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
import { startServer } from "./server-utils.js";
const DIST_DIR = path.join(import.meta.dirname, "dist"); const DIST_DIR = path.join(import.meta.dirname, "dist");
/** /**
* Creates a new MCP server instance with tools and resources registered. * Creates a new MCP server instance with tools and resources registered.
*/ */
function createServer(): McpServer { export function createServer(): McpServer {
const server = new McpServer({ const server = new McpServer({
name: "Basic MCP App Server (Preact)", name: "Basic MCP App Server (Preact)",
version: "1.0.0", version: "1.0.0",
@ -55,17 +52,3 @@ function createServer(): McpServer {
return server; return server;
} }
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 (Preact)" });
}
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

View file

@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "./dist",
"rootDir": ".",
"strict": true,
"skipLibCheck": true,
"esModuleInterop": true,
"resolveJsonModule": true
},
"include": ["server.ts"]
}

View file

@ -0,0 +1,90 @@
/**
* Entry point for running the MCP server.
* Run with: npx @modelcontextprotocol/server-basic-react
* Or: node dist/index.js [--stdio]
*/
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import cors from "cors";
import type { Request, Response } from "express";
import { createServer } from "./server.js";
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, (err) => {
if (err) {
console.error("Failed to start server:", err);
process.exit(1);
}
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);
}
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);
});

View file

@ -9,16 +9,24 @@
"directory": "examples/basic-server-react" "directory": "examples/basic-server-react"
}, },
"license": "MIT", "license": "MIT",
"main": "server.ts", "main": "dist/server.js",
"types": "dist/server.d.ts",
"bin": {
"mcp-server-basic-react": "dist/index.js"
},
"files": [ "files": [
"server.ts",
"server-utils.ts",
"dist" "dist"
], ],
"exports": {
".": {
"types": "./dist/server.d.ts",
"default": "./dist/server.js"
}
},
"scripts": { "scripts": {
"build": "tsc --noEmit && cross-env INPUT=mcp-app.html vite build", "build": "tsc --noEmit && cross-env INPUT=mcp-app.html vite build && tsc -p tsconfig.server.json && bun build server.ts --outdir dist --target node && bun build main.ts --outfile dist/index.js --target node --external \"./server.js\" --banner \"#!/usr/bin/env node\"",
"watch": "cross-env INPUT=mcp-app.html vite build --watch", "watch": "cross-env INPUT=mcp-app.html vite build --watch",
"serve": "bun --watch server.ts", "serve": "bun --watch main.ts",
"start": "cross-env NODE_ENV=development npm run build && npm run serve", "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" "prepublishOnly": "npm run build"
@ -26,6 +34,8 @@
"dependencies": { "dependencies": {
"@modelcontextprotocol/ext-apps": "^0.4.0", "@modelcontextprotocol/ext-apps": "^0.4.0",
"@modelcontextprotocol/sdk": "^1.24.0", "@modelcontextprotocol/sdk": "^1.24.0",
"cors": "^2.8.5",
"express": "^5.1.0",
"react": "^19.2.0", "react": "^19.2.0",
"react-dom": "^19.2.0", "react-dom": "^19.2.0",
"zod": "^4.1.13" "zod": "^4.1.13"
@ -38,9 +48,7 @@
"@types/react-dom": "^19.2.2", "@types/react-dom": "^19.2.2",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^4.3.4",
"concurrently": "^9.2.1", "concurrently": "^9.2.1",
"cors": "^2.8.5",
"cross-env": "^10.1.0", "cross-env": "^10.1.0",
"express": "^5.1.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vite": "^6.0.0", "vite": "^6.0.0",
"vite-plugin-singlefile": "^2.3.0" "vite-plugin-singlefile": "^2.3.0"

View file

@ -1,10 +1,8 @@
import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server"; import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 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 type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises"; import fs from "node:fs/promises";
import path from "node:path"; import path from "node:path";
import { startServer } from "./server-utils.js";
const DIST_DIR = path.join(import.meta.dirname, "dist"); const DIST_DIR = path.join(import.meta.dirname, "dist");
@ -52,17 +50,3 @@ export function createServer(): McpServer {
return server; return server;
} }
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);
});

View file

@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "./dist",
"rootDir": ".",
"strict": true,
"skipLibCheck": true,
"esModuleInterop": true,
"resolveJsonModule": true
},
"include": ["server.ts"]
}

View file

@ -1,12 +1,20 @@
/**
* Entry point for running the MCP server.
* Run with: npx mcp-server-basic-solid
* Or: node dist/index.js [--stdio]
*/
/** /**
* Shared utilities for running MCP servers with Streamable HTTP transport. * Shared utilities for running MCP servers with Streamable HTTP transport.
*/ */
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import cors from "cors"; import cors from "cors";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import { createServer } from "./server.js";
export interface ServerOptions { export interface ServerOptions {
port: number; port: number;
@ -70,3 +78,17 @@ export async function startServer(
process.on("SIGINT", shutdown); process.on("SIGINT", shutdown);
process.on("SIGTERM", shutdown); process.on("SIGTERM", shutdown);
} }
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 (Solid)" });
}
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

View file

@ -9,16 +9,14 @@
"directory": "examples/basic-server-solid" "directory": "examples/basic-server-solid"
}, },
"license": "MIT", "license": "MIT",
"main": "server.ts", "main": "dist/server.js",
"files": [ "files": [
"server.ts",
"server-utils.ts",
"dist" "dist"
], ],
"scripts": { "scripts": {
"build": "tsc --noEmit && cross-env INPUT=mcp-app.html vite build", "build": "tsc --noEmit && cross-env INPUT=mcp-app.html vite build && tsc -p tsconfig.server.json && bun build server.ts --outdir dist --target node && bun build main.ts --outfile dist/index.js --target node --external \"./server.js\" --banner \"#!/usr/bin/env node\"",
"watch": "cross-env INPUT=mcp-app.html vite build --watch", "watch": "cross-env INPUT=mcp-app.html vite build --watch",
"serve": "bun --watch server.ts", "serve": "bun --watch main.ts",
"start": "cross-env NODE_ENV=development npm run build && npm run serve", "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" "prepublishOnly": "npm run build"
@ -27,19 +25,29 @@
"@modelcontextprotocol/ext-apps": "^0.4.0", "@modelcontextprotocol/ext-apps": "^0.4.0",
"@modelcontextprotocol/sdk": "^1.24.0", "@modelcontextprotocol/sdk": "^1.24.0",
"solid-js": "^1.9.0", "solid-js": "^1.9.0",
"zod": "^4.1.13" "zod": "^4.1.13",
"cors": "^2.8.5",
"express": "^5.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/cors": "^2.8.19", "@types/cors": "^2.8.19",
"@types/express": "^5.0.0", "@types/express": "^5.0.0",
"@types/node": "^22.0.0", "@types/node": "^22.0.0",
"concurrently": "^9.2.1", "concurrently": "^9.2.1",
"cors": "^2.8.5",
"cross-env": "^10.1.0", "cross-env": "^10.1.0",
"express": "^5.1.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vite": "^6.0.0", "vite": "^6.0.0",
"vite-plugin-singlefile": "^2.3.0", "vite-plugin-singlefile": "^2.3.0",
"vite-plugin-solid": "^2.0.0" "vite-plugin-solid": "^2.0.0"
},
"types": "dist/server.d.ts",
"exports": {
".": {
"types": "./dist/server.d.ts",
"default": "./dist/server.js"
}
},
"bin": {
"mcp-server-basic-solid": "dist/index.js"
} }
} }

View file

@ -1,17 +1,14 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 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 type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises"; import fs from "node:fs/promises";
import path from "node:path"; import path from "node:path";
import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server"; import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
import { startServer } from "./server-utils.js";
const DIST_DIR = path.join(import.meta.dirname, "dist"); const DIST_DIR = path.join(import.meta.dirname, "dist");
/** /**
* Creates a new MCP server instance with tools and resources registered. * Creates a new MCP server instance with tools and resources registered.
*/ */
function createServer(): McpServer { export function createServer(): McpServer {
const server = new McpServer({ const server = new McpServer({
name: "Basic MCP App Server (Solid)", name: "Basic MCP App Server (Solid)",
version: "1.0.0", version: "1.0.0",
@ -55,17 +52,3 @@ function createServer(): McpServer {
return server; return server;
} }
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 (Solid)" });
}
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

View file

@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "./dist",
"rootDir": ".",
"strict": true,
"skipLibCheck": true,
"esModuleInterop": true,
"resolveJsonModule": true
},
"include": ["server.ts"]
}

View file

@ -0,0 +1,94 @@
/**
* Entry point for running the MCP server.
* Run with: npx mcp-server-basic-svelte
* Or: node dist/index.js [--stdio]
*/
/**
* Shared utilities for running MCP servers with Streamable HTTP transport.
*/
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
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";
import { createServer } from "./server.js";
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, (err) => {
if (err) {
console.error("Failed to start server:", err);
process.exit(1);
}
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);
}
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 (Svelte)" });
}
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

View file

@ -9,16 +9,14 @@
"directory": "examples/basic-server-svelte" "directory": "examples/basic-server-svelte"
}, },
"license": "MIT", "license": "MIT",
"main": "server.ts", "main": "dist/server.js",
"files": [ "files": [
"server.ts",
"server-utils.ts",
"dist" "dist"
], ],
"scripts": { "scripts": {
"build": "tsc --noEmit && cross-env INPUT=mcp-app.html vite build", "build": "tsc --noEmit && cross-env INPUT=mcp-app.html vite build && tsc -p tsconfig.server.json && bun build server.ts --outdir dist --target node && bun build main.ts --outfile dist/index.js --target node --external \"./server.js\" --banner \"#!/usr/bin/env node\"",
"watch": "cross-env INPUT=mcp-app.html vite build --watch", "watch": "cross-env INPUT=mcp-app.html vite build --watch",
"serve": "bun --watch server.ts", "serve": "bun --watch main.ts",
"start": "cross-env NODE_ENV=development npm run build && npm run serve", "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" "prepublishOnly": "npm run build"
@ -27,7 +25,9 @@
"@modelcontextprotocol/ext-apps": "^0.4.0", "@modelcontextprotocol/ext-apps": "^0.4.0",
"@modelcontextprotocol/sdk": "^1.24.0", "@modelcontextprotocol/sdk": "^1.24.0",
"svelte": "^5.0.0", "svelte": "^5.0.0",
"zod": "^4.1.13" "zod": "^4.1.13",
"cors": "^2.8.5",
"express": "^5.1.0"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/vite-plugin-svelte": "^5.0.0", "@sveltejs/vite-plugin-svelte": "^5.0.0",
@ -35,11 +35,19 @@
"@types/express": "^5.0.0", "@types/express": "^5.0.0",
"@types/node": "^22.0.0", "@types/node": "^22.0.0",
"concurrently": "^9.2.1", "concurrently": "^9.2.1",
"cors": "^2.8.5",
"cross-env": "^10.1.0", "cross-env": "^10.1.0",
"express": "^5.1.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vite": "^6.0.0", "vite": "^6.0.0",
"vite-plugin-singlefile": "^2.3.0" "vite-plugin-singlefile": "^2.3.0"
},
"types": "dist/server.d.ts",
"exports": {
".": {
"types": "./dist/server.d.ts",
"default": "./dist/server.js"
}
},
"bin": {
"mcp-server-basic-svelte": "dist/index.js"
} }
} }

View file

@ -1,17 +1,14 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 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 type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises"; import fs from "node:fs/promises";
import path from "node:path"; import path from "node:path";
import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server"; import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
import { startServer } from "./server-utils.js";
const DIST_DIR = path.join(import.meta.dirname, "dist"); const DIST_DIR = path.join(import.meta.dirname, "dist");
/** /**
* Creates a new MCP server instance with tools and resources registered. * Creates a new MCP server instance with tools and resources registered.
*/ */
function createServer(): McpServer { export function createServer(): McpServer {
const server = new McpServer({ const server = new McpServer({
name: "Basic MCP App Server (Svelte)", name: "Basic MCP App Server (Svelte)",
version: "1.0.0", version: "1.0.0",
@ -55,17 +52,3 @@ function createServer(): McpServer {
return server; return server;
} }
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 (Svelte)" });
}
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

View file

@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "./dist",
"rootDir": ".",
"strict": true,
"skipLibCheck": true,
"esModuleInterop": true,
"resolveJsonModule": true
},
"include": ["server.ts"]
}

View file

@ -0,0 +1,94 @@
/**
* Entry point for running the MCP server.
* Run with: npx mcp-server-basic-vanillajs
* Or: node dist/index.js [--stdio]
*/
/**
* Shared utilities for running MCP servers with Streamable HTTP transport.
*/
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
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";
import { createServer } from "./server.js";
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, (err) => {
if (err) {
console.error("Failed to start server:", err);
process.exit(1);
}
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);
}
async function main() {
if (process.argv.includes("--stdio")) {
await createServer().connect(new StdioServerTransport());
} else {
const port = parseInt(process.env.PORT ?? "3102", 10);
await startServer(createServer, { port, name: "Basic MCP App Server (Vanilla JS)" });
}
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

View file

@ -9,16 +9,14 @@
"directory": "examples/basic-server-vanillajs" "directory": "examples/basic-server-vanillajs"
}, },
"license": "MIT", "license": "MIT",
"main": "server.ts", "main": "dist/server.js",
"files": [ "files": [
"server.ts",
"server-utils.ts",
"dist" "dist"
], ],
"scripts": { "scripts": {
"build": "tsc --noEmit && cross-env INPUT=mcp-app.html vite build", "build": "tsc --noEmit && cross-env INPUT=mcp-app.html vite build && tsc -p tsconfig.server.json && bun build server.ts --outdir dist --target node && bun build main.ts --outfile dist/index.js --target node --external \"./server.js\" --banner \"#!/usr/bin/env node\"",
"watch": "cross-env INPUT=mcp-app.html vite build --watch", "watch": "cross-env INPUT=mcp-app.html vite build --watch",
"serve": "bun --watch server.ts", "serve": "bun --watch main.ts",
"start": "cross-env NODE_ENV=development npm run build && npm run serve", "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" "prepublishOnly": "npm run build"
@ -26,18 +24,28 @@
"dependencies": { "dependencies": {
"@modelcontextprotocol/ext-apps": "^0.4.0", "@modelcontextprotocol/ext-apps": "^0.4.0",
"@modelcontextprotocol/sdk": "^1.24.0", "@modelcontextprotocol/sdk": "^1.24.0",
"zod": "^4.1.13" "zod": "^4.1.13",
"cors": "^2.8.5",
"express": "^5.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/cors": "^2.8.19", "@types/cors": "^2.8.19",
"@types/express": "^5.0.0", "@types/express": "^5.0.0",
"@types/node": "^22.0.0", "@types/node": "^22.0.0",
"concurrently": "^9.2.1", "concurrently": "^9.2.1",
"cors": "^2.8.5",
"cross-env": "^10.1.0", "cross-env": "^10.1.0",
"express": "^5.1.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vite": "^6.0.0", "vite": "^6.0.0",
"vite-plugin-singlefile": "^2.3.0" "vite-plugin-singlefile": "^2.3.0"
},
"types": "dist/server.d.ts",
"exports": {
".": {
"types": "./dist/server.d.ts",
"default": "./dist/server.js"
}
},
"bin": {
"mcp-server-basic-vanillajs": "dist/index.js"
} }
} }

View file

@ -1,12 +1,9 @@
import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server"; import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 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 type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises"; import fs from "node:fs/promises";
import path from "node:path"; import path from "node:path";
import { z } from "zod"; import { z } from "zod";
import { startServer } from "./server-utils.js";
const DIST_DIR = path.join(import.meta.dirname, "dist"); const DIST_DIR = path.join(import.meta.dirname, "dist");
/** /**
@ -62,17 +59,3 @@ export function createServer(): McpServer {
return server; return server;
} }
async function main() {
if (process.argv.includes("--stdio")) {
await createServer().connect(new StdioServerTransport());
} else {
const port = parseInt(process.env.PORT ?? "3102", 10);
await startServer(createServer, { port, name: "Basic MCP App Server (Vanilla JS)" });
}
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

Some files were not shown because too many files have changed in this diff Show more