From daec74fbc1d15a7a414a5f139db2cf1f55528197 Mon Sep 17 00:00:00 2001 From: cliffhall Date: Fri, 5 Dec 2025 16:09:35 -0500 Subject: [PATCH] [WIP] Refactor everything server to be more modular and use recommended APIs. * Adding prompts (simple, complex, with completions) * Add prompts/simple.ts - in addSimplePrompt() - register a simple prompt with no arguments * Add prompts/complex.ts - in addComplexPrompt() - define promptArgsSchema containing a required city arg of type string and an optional state arg of type state - register the complex prompt with a prompt callback that combines the city and state into a prompt asking for the weather in that location * Add prompts/completions.ts - in addPromptWithCompletions() - define promptArgsSchema containing department and name string fields with completion handlers - register the completable prompt with a prompt callback that combines the inputs into a prompt asking to promote the selected name to head of the selected department * Add prompts/index.ts - import addSimplePrompt, addComplexPrompt, and addPromptWithCompletions - export registerPrompts function - in registerPrompts() - call addSimplePrompt - call addComplexPrompt - call addPromptWithCompletions * In package.json - add prettier devDependency - add prettier:check script - add prettier:fix script - in build script, copy docs folder to dist * All other changes were prettier formatting --- src/everything/prompts/completions.ts | 46 +++++++++++++++++++++++++++ src/everything/prompts/complex.ts | 32 +++++++++++++++++++ src/everything/prompts/index.ts | 14 ++++++++ src/everything/prompts/simple.ts | 22 +++++++++++++ src/everything/server/index.ts | 26 +++++++++------ 5 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 src/everything/prompts/completions.ts create mode 100644 src/everything/prompts/complex.ts create mode 100644 src/everything/prompts/index.ts create mode 100644 src/everything/prompts/simple.ts diff --git a/src/everything/prompts/completions.ts b/src/everything/prompts/completions.ts new file mode 100644 index 00000000..058c35a9 --- /dev/null +++ b/src/everything/prompts/completions.ts @@ -0,0 +1,46 @@ +import { z } from "zod"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { completable } from "@modelcontextprotocol/sdk/server/completable.js"; + +export const addPromptWithCompletions = (server: McpServer) => { + const promptArgsSchema = { + department: completable(z.string(), (value) => { + return ["Engineering", "Sales", "Marketing", "Support"].filter((d) => + d.startsWith(value) + ); + }), + name: completable(z.string(), (value, context) => { + const department = context?.arguments?.["department"]; + if (department === "Engineering") { + return ["Alice", "Bob", "Charlie"].filter((n) => n.startsWith(value)); + } else if (department === "Sales") { + return ["David", "Eve", "Frank"].filter((n) => n.startsWith(value)); + } else if (department === "Marketing") { + return ["Grace", "Henry", "Iris"].filter((n) => n.startsWith(value)); + } else if (department === "Support") { + return ["John", "Kim", "Lee"].filter((n) => n.startsWith(value)); + } + return []; + }), + }; + + server.registerPrompt( + "completable-prompt", + { + title: "Team Management", + description: "Choose a team member to lead their specific department.", + argsSchema: promptArgsSchema, + }, + async ({ department, name }) => ({ + messages: [ + { + role: "user", + content: { + type: "text", + text: `Please promote ${name} to the head of the ${department} team.`, + }, + }, + ], + }) + ); +}; diff --git a/src/everything/prompts/complex.ts b/src/everything/prompts/complex.ts new file mode 100644 index 00000000..a0447a01 --- /dev/null +++ b/src/everything/prompts/complex.ts @@ -0,0 +1,32 @@ +import { z } from "zod"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; + +export const addComplexPrompt = (server: McpServer) => { + const promptArgsSchema = { + city: z.string().describe("Name of the city"), + state: z.string().describe("Name of the state").optional(), + }; + + server.registerPrompt( + "complex-prompt", + { + title: "Complex Prompt", + description: "A prompt with two arguments, one required and one optional", + argsSchema: promptArgsSchema, + }, + (args) => { + const location = `${args?.city}${args?.state ? `, ${args?.state}` : ""}`; + return { + messages: [ + { + role: "user", + content: { + type: "text", + text: `What's weather in ${location}?`, + }, + }, + ], + }; + } + ); +}; diff --git a/src/everything/prompts/index.ts b/src/everything/prompts/index.ts new file mode 100644 index 00000000..c66d6fc6 --- /dev/null +++ b/src/everything/prompts/index.ts @@ -0,0 +1,14 @@ +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { addSimplePrompt } from "./simple.js"; +import { addComplexPrompt } from "./complex.js"; +import { addPromptWithCompletions } from "./completions.js"; + +/** + * Register the prompts with the MCP server. + * @param server + */ +export const registerPrompts = (server: McpServer) => { + addSimplePrompt(server); + addComplexPrompt(server); + addPromptWithCompletions(server); +}; diff --git a/src/everything/prompts/simple.ts b/src/everything/prompts/simple.ts new file mode 100644 index 00000000..c7561f0b --- /dev/null +++ b/src/everything/prompts/simple.ts @@ -0,0 +1,22 @@ +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; + +export const addSimplePrompt = (server: McpServer) => { + server.registerPrompt( + "simple-prompt", + { + title: "Simple Prompt", + description: "A prompt with no arguments", + }, + () => ({ + messages: [ + { + role: "user", + content: { + type: "text", + text: "This is a simple prompt without arguments.", + }, + }, + ], + }) + ); +}; diff --git a/src/everything/server/index.ts b/src/everything/server/index.ts index a4349144..d9489674 100644 --- a/src/everything/server/index.ts +++ b/src/everything/server/index.ts @@ -1,15 +1,17 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { registerTools } from "../tools/index.js"; -import { registerResources } from "../resources/index.js"; import { dirname, join } from "path"; import { readFileSync } from "fs"; import { fileURLToPath } from "url"; -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); -const instructions = readInstructions(); +import { registerTools } from "../tools/index.js"; +import { registerResources } from "../resources/index.js"; +import { registerPrompts } from "../prompts/index.js"; -// Create the MCP resource server +// Everything Server factory export const createServer = () => { + // Read the server instructions + const instructions = readInstructions(); + + // Create the server const server = new McpServer( { name: "mcp-servers/everything", @@ -35,6 +37,9 @@ export const createServer = () => { // Register the resources registerResources(server); + // Register the prompts + registerPrompts(server); + return { server, cleanup: () => {}, @@ -42,14 +47,15 @@ export const createServer = () => { }; }; +// Read the server instructions from a file function readInstructions(): string { + const __filename = fileURLToPath(import.meta.url); + const __dirname = dirname(__filename); + const filePath = join(__dirname, "..", "docs", "server-instructions.md"); let instructions; try { - instructions = readFileSync( - join(__dirname, "..", "docs", "server-instructions.md"), - "utf-8" - ); + instructions = readFileSync(filePath, "utf-8"); } catch (e) { instructions = "Server instructions not loaded: " + e; }