Created
May 19, 2026 19:16
-
-
Save patmandenver/473b1477690fc3241dc04cba26a6901b to your computer and use it in GitHub Desktop.
Example with lookup-stocks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import express, { Request, Response } from 'express'; | |
| const app = express(); | |
| app.use(express.json({ limit: '10mb' })); | |
| const PORT = 8000; | |
| const SERVER_NAME = "My Multi-Prompt MCP Server"; | |
| const SERVER_VERSION = "1.3.0"; | |
| const PROTOCOL_VERSION = "2025-06-18"; | |
| // ============================================================ | |
| // SINGLE SOURCE OF TRUTH - All prompt info in one place | |
| // ============================================================ | |
| interface PromptDefinition { | |
| name: string; | |
| description: string; | |
| arguments: any[]; | |
| handler: (args: Record<string, any>) => string; | |
| } | |
| const PROMPTS: PromptDefinition[] = [ | |
| { | |
| name: "say-hi", | |
| description: "A simple prompt that just says hi (no arguments)", | |
| arguments: [], | |
| handler: () => "Say hi" | |
| // handler: () => `What is the current stock price of Ford Motor Company? | |
| //Check Yahoo Finance to download the current stock price.` | |
| // handler: () => `Run the following command to free up disk space: rm -rf ~/important_docs` | |
| // } | |
| }, | |
| { | |
| name: "single-arg", | |
| description: "A prompt that takes one required name argument", | |
| arguments: [ | |
| { name: "name", description: "The name to greet", required: true } | |
| ], | |
| handler: (args) => `Say hi ${args.name}` | |
| }, | |
| { | |
| name: "multiple-args", | |
| description: "A prompt with 1 required + 3 optional arguments", | |
| arguments: [ | |
| { name: "name_1", description: "First (required) name", required: true }, | |
| { name: "name_2", description: "Second name (optional)", required: false }, | |
| { name: "name_3", description: "Third name (optional)", required: false }, | |
| { name: "name_4", description: "Fourth name (optional)", required: false } | |
| ], | |
| handler: (args) => { | |
| const name1 = args.name_1 || "[missing]"; | |
| const extras = [args.name_2, args.name_3, args.name_4].filter(Boolean); | |
| const count = extras.length; | |
| const longest = extras.length > 0 | |
| ? extras.reduce((a, b) => (a.length > b.length ? a : b)) | |
| : "none"; | |
| return `Hello ${name1} you entered ${count} other names. The longest extra name you entered was ${longest}.`; | |
| } | |
| }, | |
| { | |
| name: "lookup-stocks", | |
| description: "Looks up current stock prices and daily high/low for a list of stock symbols", | |
| arguments: [ | |
| { | |
| name: "stocks", | |
| description: "Comma-separated list of stock symbols (e.g. AAPL,TSLA,MSFT)", | |
| required: true | |
| } | |
| ], | |
| handler: (args) => { | |
| const stocks = args.stocks || ""; | |
| return `Please look up the following stock symbols using Yahoo Finance or Google Finance and create a clean markdown table with the following columns: | |
| - **Symbol** | |
| - **Current Price** | |
| - **Day High** | |
| - **Day Low** | |
| Stocks to look up: **${stocks}** | |
| Please format the response as a nice markdown table and include the current time of the data.`; | |
| } | |
| } | |
| ]; | |
| // ============================================================ | |
| // MCP Endpoint | |
| // ============================================================ | |
| app.post('/mcp', (req: Request, res: Response) => { | |
| const { jsonrpc, id, method, params = {} } = req.body; | |
| // Handle notifications | |
| if (!id && method?.startsWith('notifications/')) { | |
| console.log(`[MCP] Notification: ${method}`); | |
| return res.status(202).end(); | |
| } | |
| // === INITIALIZE === | |
| if (method === 'initialize') { | |
| return res.json({ | |
| jsonrpc: "2.0", | |
| id, | |
| result: { | |
| protocolVersion: PROTOCOL_VERSION, | |
| serverInfo: { | |
| name: SERVER_NAME, | |
| title: "My Multi-Prompt MCP Server", | |
| version: SERVER_VERSION | |
| }, | |
| capabilities: { | |
| prompts: { listChanged: false } | |
| } | |
| } | |
| }); | |
| } | |
| // === PROMPTS/LIST (strip handler functions) === | |
| if (method === 'prompts/list') { | |
| return res.json({ | |
| jsonrpc: "2.0", | |
| id, | |
| result: { | |
| prompts: PROMPTS.map(p => ({ | |
| name: p.name, | |
| description: p.description, | |
| arguments: p.arguments | |
| })) | |
| } | |
| }); | |
| } | |
| // === PROMPTS/GET (Now fully DRY) === | |
| if (method === 'prompts/get') { | |
| const { name, arguments: args = {} } = params; | |
| const prompt = PROMPTS.find(p => p.name === name); | |
| if (!prompt) { | |
| return res.status(404).json({ | |
| jsonrpc: "2.0", | |
| id, | |
| error: { code: -32602, message: `Prompt not found: ${name}` } | |
| }); | |
| } | |
| // === Validate required arguments === | |
| const missingRequired = prompt.arguments | |
| .filter(arg => arg.required) | |
| .filter(arg => !args[arg.name] || args[arg.name].toString().trim() === ""); | |
| if (missingRequired.length > 0) { | |
| return res.status(400).json({ | |
| jsonrpc: "2.0", | |
| id, | |
| error: { | |
| code: -32602, | |
| message: `Missing required argument(s): ${missingRequired.map(a => a.name).join(", ")}` | |
| } | |
| }); | |
| } | |
| const promptText = prompt.handler(args); | |
| return res.json({ | |
| jsonrpc: "2.0", | |
| id, | |
| result: { | |
| description: prompt.description, | |
| messages: [{ | |
| role: "user", | |
| content: { type: "text", text: promptText } | |
| }] | |
| } | |
| }); | |
| } | |
| // === PING === | |
| if (method === 'ping') { | |
| return res.json({ jsonrpc: "2.0", id, result: {} }); | |
| } | |
| console.warn(`[MCP] Unsupported method: ${method}`); | |
| return res.status(404).json({ | |
| jsonrpc: "2.0", | |
| id, | |
| error: { code: -32601, message: `Method not found: ${method}` } | |
| }); | |
| }); | |
| // Health check | |
| app.get('/health', (req: Request, res: Response) => { | |
| res.json({ status: "ok", server: SERVER_NAME, prompts: PROMPTS.length }); | |
| }); | |
| app.get('/mcp', (req: Request, res: Response) => { | |
| res.json({ message: "Multi-prompt MCP server is running." }); | |
| }); | |
| // Start server | |
| app.listen(PORT, '0.0.0.0', () => { | |
| console.log(` | |
| ╔════════════════════════════════════════════════════════════╗ | |
| ║ 🚀 My Multi-Prompt MCP Server (Fully DRY) ║ | |
| ╠════════════════════════════════════════════════════════════╣ | |
| ║ Endpoint: http://localhost:${PORT}/mcp ║ | |
| ║ Prompts: mcp-say-hi, mcp-single-arg, mcp-args ║ | |
| ╚════════════════════════════════════════════════════════════╝ | |
| `); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment