@nexus-lab/create-mcp-server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # create-mcp-server
2
+
3
+ Scaffold a new [Model Context Protocol](https://modelcontextprotocol.io/) server in seconds.
4
+
5
+ ```bash
6
+ npm create mcp-server my-server
7
+ ```
8
+
9
+ ## Features
10
+
11
+ - **TypeScript + ESM** — Modern setup out of the box
12
+ - **Secure defaults** — Zod schema validation for all inputs
13
+ - **Multiple templates** — Choose what fits your use case
14
+ - **Test-ready** — Vitest included in the `full` template
15
+ - **Zero config** — Works immediately after generation
16
+
17
+ ## Templates
18
+
19
+ ### `minimal`
20
+ The simplest possible MCP server. One tool, stdio transport.
21
+
22
+ ```bash
23
+ npm create mcp-server my-server -- --template minimal
24
+ ```
25
+
26
+ ### `full`
27
+ Tools, resources, prompts, and testing all wired up.
28
+
29
+ ```bash
30
+ npm create mcp-server my-server -- --template full
31
+ ```
32
+
33
+ ### `http`
34
+ Streamable HTTP transport with Express. Ready for remote deployment.
35
+
36
+ ```bash
37
+ npm create mcp-server my-server -- --template http
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ```bash
43
+ # Interactive mode
44
+ npm create mcp-server
45
+
46
+ # With project name
47
+ npm create mcp-server my-server
48
+
49
+ # With template
50
+ npm create mcp-server my-server -- --template full
51
+
52
+ # Skip npm install
53
+ npm create mcp-server my-server -- --no-install
54
+
55
+ # Skip git init
56
+ npm create mcp-server my-server -- --no-git
57
+ ```
58
+
59
+ ## What you get
60
+
61
+ ```
62
+ my-server/
63
+ ├── src/
64
+ │ └── index.ts # Server entry point
65
+ ├── package.json
66
+ ├── tsconfig.json
67
+ └── .gitignore
68
+ ```
69
+
70
+ The `full` template also includes:
71
+ ```
72
+ ├── src/
73
+ │ ├── index.ts # Server entry point
74
+ │ ├── tools.ts # Tool definitions
75
+ │ ├── resources.ts # Resource definitions
76
+ │ └── prompts.ts # Prompt definitions
77
+ ├── tests/
78
+ │ └── tools.test.ts # Example tests
79
+ └── vitest.config.ts
80
+ ```
81
+
82
+ ## After scaffolding
83
+
84
+ ```bash
85
+ cd my-server
86
+ npm run build
87
+ node dist/index.js
88
+ ```
89
+
90
+ To use with Claude Code, add to your MCP config:
91
+
92
+ ```json
93
+ {
94
+ "mcpServers": {
95
+ "my-server": {
96
+ "command": "node",
97
+ "args": ["/path/to/my-server/dist/index.js"]
98
+ }
99
+ }
100
+ }
101
+ ```
102
+
103
+ ## License
104
+
105
+ MIT — [Nexus Lab](https://github.com/nexus-lab)
@@ -0,0 +1,2 @@
1
+ import type { ProjectConfig } from "./prompts.js";
2
+ export declare function generateProject(config: ProjectConfig): Promise<void>;
@@ -0,0 +1,61 @@
1
+ import path from "node:path";
2
+ import fs from "fs-extra";
3
+ import chalk from "chalk";
4
+ import { execSync } from "node:child_process";
5
+ import { fileURLToPath } from "node:url";
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ function getTemplatesDir() {
8
+ // In development: src/templates, in production: dist/../templates
9
+ const devPath = path.resolve(__dirname, "..", "templates");
10
+ const srcPath = path.resolve(__dirname, "templates");
11
+ return fs.existsSync(devPath) ? devPath : srcPath;
12
+ }
13
+ export async function generateProject(config) {
14
+ const targetDir = path.resolve(process.cwd(), config.projectName);
15
+ if (await fs.pathExists(targetDir)) {
16
+ const files = await fs.readdir(targetDir);
17
+ if (files.length > 0) {
18
+ throw new Error(`Directory "${config.projectName}" already exists and is not empty`);
19
+ }
20
+ }
21
+ console.log(chalk.dim(` Creating project in ${targetDir}...`));
22
+ // Copy template
23
+ const templatesDir = getTemplatesDir();
24
+ const templateDir = path.join(templatesDir, config.template);
25
+ if (!(await fs.pathExists(templateDir))) {
26
+ throw new Error(`Template "${config.template}" not found at ${templateDir}`);
27
+ }
28
+ await fs.copy(templateDir, targetDir);
29
+ // Update package.json with user config
30
+ const pkgPath = path.join(targetDir, "package.json");
31
+ const pkg = await fs.readJson(pkgPath);
32
+ pkg.name = config.projectName;
33
+ pkg.description = config.description;
34
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
35
+ // Rename _gitignore to .gitignore (npm strips .gitignore from packages)
36
+ const gitignoreSrc = path.join(targetDir, "_gitignore");
37
+ if (await fs.pathExists(gitignoreSrc)) {
38
+ await fs.rename(gitignoreSrc, path.join(targetDir, ".gitignore"));
39
+ }
40
+ // Git init
41
+ if (config.git) {
42
+ try {
43
+ execSync("git init", { cwd: targetDir, stdio: "ignore" });
44
+ console.log(chalk.dim(" Initialized git repository"));
45
+ }
46
+ catch {
47
+ // Git not available, skip
48
+ }
49
+ }
50
+ // npm install
51
+ if (config.install) {
52
+ console.log(chalk.dim(" Installing dependencies..."));
53
+ try {
54
+ execSync("npm install", { cwd: targetDir, stdio: "ignore" });
55
+ console.log(chalk.dim(" Dependencies installed"));
56
+ }
57
+ catch {
58
+ console.log(chalk.yellow(" Could not install dependencies. Run 'npm install' manually."));
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { runPrompts } from "./prompts.js";
4
+ import { generateProject } from "./generator.js";
5
+ import chalk from "chalk";
6
+ const program = new Command();
7
+ program
8
+ .name("create-mcp-server")
9
+ .description("Scaffold a new MCP server project with TypeScript and secure defaults")
10
+ .version("0.1.0")
11
+ .argument("[project-name]", "Name of the project to create")
12
+ .option("-t, --template <template>", "Template to use (minimal, full, http)", "minimal")
13
+ .option("--no-install", "Skip npm install")
14
+ .option("--no-git", "Skip git init")
15
+ .action(async (projectName, options) => {
16
+ console.log();
17
+ console.log(chalk.bold.cyan(" ⚡ create-mcp-server"));
18
+ console.log(chalk.dim(" Scaffold a new MCP server in seconds"));
19
+ console.log();
20
+ try {
21
+ const config = await runPrompts(projectName, options);
22
+ await generateProject(config);
23
+ console.log();
24
+ console.log(chalk.green(" ✓ Project created successfully!"));
25
+ console.log();
26
+ console.log(` ${chalk.dim("$")} cd ${config.projectName}`);
27
+ if (options.install !== false) {
28
+ console.log(` ${chalk.dim("$")} npm run build`);
29
+ }
30
+ else {
31
+ console.log(` ${chalk.dim("$")} npm install`);
32
+ console.log(` ${chalk.dim("$")} npm run build`);
33
+ }
34
+ console.log(` ${chalk.dim("$")} node dist/index.js`);
35
+ console.log();
36
+ }
37
+ catch (error) {
38
+ if (error instanceof Error && error.message === "cancelled") {
39
+ console.log(chalk.yellow("\n Cancelled."));
40
+ process.exit(0);
41
+ }
42
+ console.error(chalk.red(`\n Error: ${error}`));
43
+ process.exit(1);
44
+ }
45
+ });
46
+ program.parse();
@@ -0,0 +1,12 @@
1
+ export interface ProjectConfig {
2
+ projectName: string;
3
+ description: string;
4
+ template: "minimal" | "full" | "http";
5
+ install: boolean;
6
+ git: boolean;
7
+ }
8
+ export declare function runPrompts(projectName: string | undefined, options: {
9
+ template?: string;
10
+ install?: boolean;
11
+ git?: boolean;
12
+ }): Promise<ProjectConfig>;
@@ -0,0 +1,53 @@
1
+ import prompts from "prompts";
2
+ import chalk from "chalk";
3
+ const TEMPLATES = [
4
+ {
5
+ title: `${chalk.bold("minimal")} ${chalk.dim("— Single tool, stdio transport")}`,
6
+ value: "minimal",
7
+ },
8
+ {
9
+ title: `${chalk.bold("full")} ${chalk.dim("— Tools + Resources + Prompts, Vitest included")}`,
10
+ value: "full",
11
+ },
12
+ {
13
+ title: `${chalk.bold("http")} ${chalk.dim("— Streamable HTTP transport with Express")}`,
14
+ value: "http",
15
+ },
16
+ ];
17
+ export async function runPrompts(projectName, options) {
18
+ const questions = [];
19
+ if (!projectName) {
20
+ questions.push({
21
+ type: "text",
22
+ name: "projectName",
23
+ message: "Project name:",
24
+ initial: "my-mcp-server",
25
+ validate: (value) => /^[a-z0-9-_]+$/.test(value) || "Only lowercase letters, numbers, hyphens, and underscores",
26
+ });
27
+ }
28
+ if (!options.template) {
29
+ questions.push({
30
+ type: "select",
31
+ name: "template",
32
+ message: "Template:",
33
+ choices: [...TEMPLATES],
34
+ });
35
+ }
36
+ questions.push({
37
+ type: "text",
38
+ name: "description",
39
+ message: "Description:",
40
+ initial: "A Model Context Protocol server",
41
+ });
42
+ const onCancel = () => {
43
+ throw new Error("cancelled");
44
+ };
45
+ const answers = await prompts(questions, { onCancel });
46
+ return {
47
+ projectName: projectName || answers.projectName,
48
+ description: answers.description || "A Model Context Protocol server",
49
+ template: options.template || answers.template || "minimal",
50
+ install: options.install !== false,
51
+ git: options.git !== false,
52
+ };
53
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@nexus-lab/create-mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold a new MCP server project with TypeScript, testing, and secure defaults",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-mcp-server": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "templates"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "dev": "tsc --watch",
16
+ "test": "vitest",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "mcp",
21
+ "model-context-protocol",
22
+ "claude",
23
+ "scaffold",
24
+ "template",
25
+ "cli",
26
+ "create"
27
+ ],
28
+ "author": "Nexus Lab",
29
+ "license": "MIT",
30
+ "dependencies": {
31
+ "commander": "^13.0.0",
32
+ "prompts": "^2.4.2",
33
+ "chalk": "^5.4.0",
34
+ "fs-extra": "^11.2.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/prompts": "^2.4.9",
38
+ "@types/fs-extra": "^11.0.4",
39
+ "@types/node": "^22.0.0",
40
+ "typescript": "^5.7.0",
41
+ "vitest": "^3.0.0"
42
+ }
43
+ }
@@ -0,0 +1,5 @@
1
+ node_modules
2
+ dist
3
+ *.log
4
+ .env
5
+ coverage
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "my-mcp-server",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "A full-featured MCP server with tools, resources, and prompts",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "dev": "tsc --watch",
10
+ "start": "node dist/index.js",
11
+ "test": "vitest run"
12
+ },
13
+ "dependencies": {
14
+ "@modelcontextprotocol/sdk": "^1.0.0",
15
+ "zod": "^3.23.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^22.0.0",
19
+ "typescript": "^5.6.0",
20
+ "vitest": "^2.1.0"
21
+ }
22
+ }
@@ -0,0 +1,28 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { registerTools } from "./tools.js";
4
+ import { registerResources } from "./resources.js";
5
+ import { registerPrompts } from "./prompts.js";
6
+
7
+ // Create the MCP server instance
8
+ const server = new McpServer({
9
+ name: "my-mcp-server",
10
+ version: "0.1.0",
11
+ });
12
+
13
+ // Register all capabilities
14
+ registerTools(server);
15
+ registerResources(server);
16
+ registerPrompts(server);
17
+
18
+ // Connect via stdio transport
19
+ async function main() {
20
+ const transport = new StdioServerTransport();
21
+ await server.connect(transport);
22
+ console.error("MCP server running on stdio");
23
+ }
24
+
25
+ main().catch((error) => {
26
+ console.error("Fatal error:", error);
27
+ process.exit(1);
28
+ });
@@ -0,0 +1,32 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+
4
+ // Register prompts on the MCP server
5
+ export function registerPrompts(server: McpServer): void {
6
+ server.prompt(
7
+ "review-code",
8
+ "Review the provided code and suggest improvements",
9
+ { code: z.string().describe("The source code to review") },
10
+ ({ code }) => ({
11
+ messages: [
12
+ {
13
+ role: "user" as const,
14
+ content: {
15
+ type: "text" as const,
16
+ text: [
17
+ "Please review the following code and provide feedback on:",
18
+ "1. Code quality and readability",
19
+ "2. Potential bugs or edge cases",
20
+ "3. Performance considerations",
21
+ "4. Suggested improvements",
22
+ "",
23
+ "```",
24
+ code,
25
+ "```",
26
+ ].join("\n"),
27
+ },
28
+ },
29
+ ],
30
+ })
31
+ );
32
+ }
@@ -0,0 +1,34 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+
3
+ // Example application config returned as a resource
4
+ const appConfig = {
5
+ appName: "my-mcp-server",
6
+ version: "0.1.0",
7
+ features: {
8
+ tools: true,
9
+ resources: true,
10
+ prompts: true,
11
+ },
12
+ settings: {
13
+ maxRetries: 3,
14
+ timeoutMs: 5000,
15
+ },
16
+ };
17
+
18
+ // Register resources on the MCP server
19
+ export function registerResources(server: McpServer): void {
20
+ server.resource(
21
+ "app-config",
22
+ "config://app",
23
+ "Application configuration data",
24
+ async () => ({
25
+ contents: [
26
+ {
27
+ uri: "config://app",
28
+ mimeType: "application/json",
29
+ text: JSON.stringify(appConfig, null, 2),
30
+ },
31
+ ],
32
+ })
33
+ );
34
+ }
@@ -0,0 +1,59 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+
4
+ // Greeting logic — exported for direct testing
5
+ export function greet(name: string): string {
6
+ return `Hello, ${name}! Welcome to the MCP server.`;
7
+ }
8
+
9
+ // Safe expression evaluator — exported for direct testing
10
+ export function calculate(expression: string): string {
11
+ // Allow only digits, operators, parentheses, dots, and spaces
12
+ const sanitized = expression.replace(/\s/g, "");
13
+ if (!/^[\d+\-*/().]+$/.test(sanitized)) {
14
+ throw new Error(
15
+ "Invalid expression. Only numbers and +, -, *, /, (, ) are allowed."
16
+ );
17
+ }
18
+
19
+ // Use Function constructor for safe-ish evaluation of arithmetic
20
+ const result = new Function(`"use strict"; return (${sanitized});`)();
21
+
22
+ if (typeof result !== "number" || !Number.isFinite(result)) {
23
+ throw new Error("Expression did not evaluate to a finite number.");
24
+ }
25
+
26
+ return String(result);
27
+ }
28
+
29
+ // Register tools on the MCP server
30
+ export function registerTools(server: McpServer): void {
31
+ server.tool(
32
+ "greet",
33
+ "Generate a greeting for the given name",
34
+ { name: z.string().describe("The name to greet") },
35
+ async ({ name }) => ({
36
+ content: [{ type: "text", text: greet(name) }],
37
+ })
38
+ );
39
+
40
+ server.tool(
41
+ "calculate",
42
+ "Safely evaluate a mathematical expression",
43
+ {
44
+ expression: z
45
+ .string()
46
+ .describe("Arithmetic expression (e.g. '2 + 3 * 4')"),
47
+ },
48
+ async ({ expression }) => {
49
+ try {
50
+ const result = calculate(expression);
51
+ return { content: [{ type: "text", text: result }] };
52
+ } catch (error) {
53
+ const message =
54
+ error instanceof Error ? error.message : "Unknown error";
55
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
56
+ }
57
+ }
58
+ );
59
+ }
@@ -0,0 +1,46 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { greet, calculate } from "../src/tools.js";
3
+
4
+ describe("greet", () => {
5
+ it("returns a greeting with the given name", () => {
6
+ const result = greet("Alice");
7
+ expect(result).toBe("Hello, Alice! Welcome to the MCP server.");
8
+ });
9
+
10
+ it("handles empty string", () => {
11
+ const result = greet("");
12
+ expect(result).toBe("Hello, ! Welcome to the MCP server.");
13
+ });
14
+ });
15
+
16
+ describe("calculate", () => {
17
+ it("evaluates simple addition", () => {
18
+ expect(calculate("2 + 3")).toBe("5");
19
+ });
20
+
21
+ it("evaluates multiplication with precedence", () => {
22
+ expect(calculate("2 + 3 * 4")).toBe("14");
23
+ });
24
+
25
+ it("evaluates expressions with parentheses", () => {
26
+ expect(calculate("(2 + 3) * 4")).toBe("20");
27
+ });
28
+
29
+ it("evaluates decimal numbers", () => {
30
+ expect(calculate("1.5 + 2.5")).toBe("4");
31
+ });
32
+
33
+ it("rejects expressions with invalid characters", () => {
34
+ expect(() => calculate("require('fs')")).toThrow("Invalid expression");
35
+ });
36
+
37
+ it("rejects alphabetic input", () => {
38
+ expect(() => calculate("abc")).toThrow("Invalid expression");
39
+ });
40
+
41
+ it("rejects division by zero (Infinity)", () => {
42
+ expect(() => calculate("1/0")).toThrow(
43
+ "Expression did not evaluate to a finite number"
44
+ );
45
+ });
46
+ });
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "outDir": "dist",
11
+ "rootDir": "src",
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "sourceMap": true
15
+ },
16
+ "include": ["src/**/*.ts"],
17
+ "exclude": ["node_modules", "dist", "tests"]
18
+ }
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: "node",
7
+ },
8
+ });
@@ -0,0 +1,4 @@
1
+ node_modules/
2
+ dist/
3
+ *.log
4
+ .env
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "my-mcp-server",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "tsc",
7
+ "dev": "tsc --watch",
8
+ "start": "node dist/index.js"
9
+ },
10
+ "dependencies": {
11
+ "@modelcontextprotocol/sdk": "^1.12.1",
12
+ "cors": "^2.8.5",
13
+ "express": "^4.21.2",
14
+ "zod": "^3.24.4"
15
+ },
16
+ "devDependencies": {
17
+ "@types/cors": "^2.8.17",
18
+ "@types/express": "^5.0.2",
19
+ "@types/node": "^22.15.3",
20
+ "typescript": "^5.8.3"
21
+ }
22
+ }
@@ -0,0 +1,106 @@
1
+ import express from "express";
2
+ import cors from "cors";
3
+ import { randomUUID } from "node:crypto";
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
6
+ import { z } from "zod";
7
+
8
+ const app = express();
9
+
10
+ app.use(cors());
11
+ app.use(express.json());
12
+
13
+ // Store active transports keyed by session ID
14
+ const transports = new Map<string, StreamableHTTPServerTransport>();
15
+
16
+ /** Create a new MCP server instance and register tools */
17
+ function createServer(): McpServer {
18
+ const server = new McpServer({
19
+ name: "my-mcp-server",
20
+ version: "0.1.0",
21
+ });
22
+
23
+ // Example tool: hello
24
+ server.tool(
25
+ "hello",
26
+ "Returns a greeting for the given name",
27
+ { name: z.string().describe("Name to greet") },
28
+ async ({ name }) => ({
29
+ content: [{ type: "text", text: `Hello, ${name}!` }],
30
+ }),
31
+ );
32
+
33
+ return server;
34
+ }
35
+
36
+ // POST /mcp — handle JSON-RPC requests (initialize + all subsequent calls)
37
+ app.post("/mcp", async (req, res) => {
38
+ try {
39
+ // Check for existing session
40
+ const sessionId = req.headers["mcp-session-id"] as string | undefined;
41
+
42
+ if (sessionId && transports.has(sessionId)) {
43
+ // Route to existing transport
44
+ const transport = transports.get(sessionId)!;
45
+ await transport.handleRequest(req, res);
46
+ return;
47
+ }
48
+
49
+ // No valid session — create a new one (expects an initialize request)
50
+ const newSessionId = randomUUID();
51
+ const transport = new StreamableHTTPServerTransport({
52
+ sessionId: newSessionId,
53
+ });
54
+
55
+ transports.set(newSessionId, transport);
56
+
57
+ // Clean up on close
58
+ transport.onclose = () => {
59
+ transports.delete(newSessionId);
60
+ };
61
+
62
+ // Connect a fresh server to this transport
63
+ const server = createServer();
64
+ await server.connect(transport);
65
+
66
+ // Handle the incoming request
67
+ await transport.handleRequest(req, res);
68
+ } catch (err) {
69
+ console.error("Error handling POST /mcp:", err);
70
+ if (!res.headersSent) {
71
+ res.status(500).json({ error: "Internal server error" });
72
+ }
73
+ }
74
+ });
75
+
76
+ // GET /mcp — SSE stream for server-to-client notifications
77
+ app.get("/mcp", async (req, res) => {
78
+ const sessionId = req.headers["mcp-session-id"] as string | undefined;
79
+
80
+ if (!sessionId || !transports.has(sessionId)) {
81
+ res.status(400).json({ error: "Invalid or missing session ID" });
82
+ return;
83
+ }
84
+
85
+ const transport = transports.get(sessionId)!;
86
+ await transport.handleRequest(req, res);
87
+ });
88
+
89
+ // DELETE /mcp — terminate a session
90
+ app.delete("/mcp", async (req, res) => {
91
+ const sessionId = req.headers["mcp-session-id"] as string | undefined;
92
+
93
+ if (!sessionId || !transports.has(sessionId)) {
94
+ res.status(400).json({ error: "Invalid or missing session ID" });
95
+ return;
96
+ }
97
+
98
+ const transport = transports.get(sessionId)!;
99
+ await transport.handleRequest(req, res);
100
+ });
101
+
102
+ const PORT = parseInt(process.env.PORT || "3000", 10);
103
+
104
+ app.listen(PORT, () => {
105
+ console.log(`MCP server listening on http://localhost:${PORT}/mcp`);
106
+ });
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "outDir": "dist",
11
+ "rootDir": "src",
12
+ "declaration": true
13
+ },
14
+ "include": ["src"]
15
+ }
@@ -0,0 +1,4 @@
1
+ node_modules/
2
+ dist/
3
+ *.log
4
+ .env
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "my-mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "A minimal MCP server",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "dev": "tsc --watch",
10
+ "start": "node dist/index.js"
11
+ },
12
+ "dependencies": {
13
+ "@modelcontextprotocol/sdk": "^1.12.1",
14
+ "zod": "^3.24.4"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^22.15.3",
18
+ "typescript": "^5.8.3"
19
+ }
20
+ }
@@ -0,0 +1,27 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { z } from "zod";
4
+
5
+ // Create the MCP server
6
+ const server = new McpServer({
7
+ name: "my-mcp-server",
8
+ version: "0.1.0",
9
+ });
10
+
11
+ // Register a simple greeting tool
12
+ server.tool(
13
+ "hello",
14
+ "Greet someone by name",
15
+ { name: z.string().describe("Name of the person to greet") },
16
+ async ({ name }) => ({
17
+ content: [{ type: "text", text: `Hello, ${name}!` }],
18
+ })
19
+ );
20
+
21
+ // Start the server with stdio transport
22
+ async function main() {
23
+ const transport = new StdioServerTransport();
24
+ await server.connect(transport);
25
+ }
26
+
27
+ main().catch(console.error);
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "outDir": "dist",
10
+ "rootDir": "src",
11
+ "declaration": true
12
+ },
13
+ "include": ["src"]
14
+ }