SystemPrompt MCP Server

by Ejb503
Verified
  • src
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ErrorCode, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import { ConfluenceClient } from "./client/confluence-client.js"; import { handleGetSpace, handleListSpaces } from "./handlers/space-handlers.js"; import { handleCreatePage, handleGetPage, handleListPages, handleUpdatePage, } from "./handlers/page-handlers.js"; import { handleAddLabel, handleGetLabels, handleRemoveLabel, handleSearchContent, } from "./handlers/search-label-handlers.js"; import { toolSchemas } from "./schemas/tool-schemas.js"; // Required environment variables const requiredEnvVars = [ "CONFLUENCE_DOMAIN", "CONFLUENCE_EMAIL", "CONFLUENCE_API_TOKEN", ] as const; // Validate environment variables for (const envVar of requiredEnvVars) { if (!process.env[envVar]) { throw new Error(`Missing required environment variable: ${envVar}`); } } class ConfluenceServer { private server: Server; private confluenceClient: ConfluenceClient; constructor() { console.error("Loading tool schemas..."); console.error("Available schemas:", Object.keys(toolSchemas)); // Convert tool schemas to the format expected by the MCP SDK const tools = Object.entries(toolSchemas).map(([key, schema]) => { console.error(`Registering tool: ${key}`); const inputSchema = { type: "object", properties: schema.inputSchema.properties, } as const; // Only add required field if it exists in the schema if ("required" in schema.inputSchema) { Object.assign(inputSchema, { required: schema.inputSchema.required }); } return { name: key, description: schema.description, inputSchema, }; }); console.error("Initializing server with tools:", JSON.stringify(tools, null, 2)); this.server = new Server( { name: "confluence-cloud", version: "0.1.0", }, { capabilities: { tools: { schemas: tools, }, resources: { schemas: [], // Explicitly define empty resources }, }, } ); this.confluenceClient = new ConfluenceClient({ domain: process.env.CONFLUENCE_DOMAIN!, email: process.env.CONFLUENCE_EMAIL!, apiToken: process.env.CONFLUENCE_API_TOKEN!, }); this.setupHandlers(); this.server.onerror = (error) => console.error("[MCP Error]", error); process.on("SIGINT", async () => { await this.server.close(); process.exit(0); }); } private setupHandlers() { // Set up required MCP protocol handlers this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: Object.entries(toolSchemas).map(([key, schema]) => ({ name: key, description: schema.description, inputSchema: { type: "object", properties: schema.inputSchema.properties, ...("required" in schema.inputSchema ? { required: schema.inputSchema.required } : {}), }, })), })); this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [], // No resources provided by this server })); this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({ resourceTemplates: [], // No resource templates provided by this server })); this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => { throw new McpError( ErrorCode.InvalidRequest, `No resources available: ${request.params.uri}` ); }); // Set up tool handlers this.server.setRequestHandler(CallToolRequestSchema, async (request) => { console.error("Received request:", JSON.stringify(request, null, 2)); const { name, arguments: args } = request.params; console.error(`Handling tool request: ${name}`); try { switch (name) { // Space operations case "list_spaces": { const { limit, start } = (args || {}) as { limit?: number; start?: number }; return await handleListSpaces(this.confluenceClient, { limit, start }); } case "get_space": { const { spaceId } = (args || {}) as { spaceId: string }; if (!spaceId) throw new McpError(ErrorCode.InvalidParams, "spaceId is required"); return await handleGetSpace(this.confluenceClient, { spaceId }); } // Page operations case "list_pages": { const { spaceId, limit, start } = (args || {}) as { spaceId: string; limit?: number; start?: number }; if (!spaceId) throw new McpError(ErrorCode.InvalidParams, "spaceId is required"); return await handleListPages(this.confluenceClient, { spaceId, limit, start }); } case "get_page": { const { pageId } = (args || {}) as { pageId: string }; if (!pageId) throw new McpError(ErrorCode.InvalidParams, "pageId is required"); return await handleGetPage(this.confluenceClient, { pageId }); } case "create_page": { const { spaceId, title, content, parentId } = (args || {}) as { spaceId: string; title: string; content: string; parentId?: string }; if (!spaceId || !title || !content) { throw new McpError(ErrorCode.InvalidParams, "spaceId, title, and content are required"); } return await handleCreatePage(this.confluenceClient, { spaceId, title, content, parentId }); } case "update_page": { const { pageId, title, content, version } = (args || {}) as { pageId: string; title: string; content: string; version: number; }; if (!pageId || !title || !content || version === undefined) { throw new McpError(ErrorCode.InvalidParams, "pageId, title, content, and version are required"); } return await handleUpdatePage(this.confluenceClient, { pageId, title, content, version }); } // Search operation case "search_content": { const { query, limit, start } = (args || {}) as { query: string; limit?: number; start?: number }; if (!query) throw new McpError(ErrorCode.InvalidParams, "query is required"); return await handleSearchContent(this.confluenceClient, { query, limit, start }); } // Label operations case "get_labels": { const { pageId } = (args || {}) as { pageId: string }; if (!pageId) throw new McpError(ErrorCode.InvalidParams, "pageId is required"); return await handleGetLabels(this.confluenceClient, { pageId }); } case "add_label": { const { pageId, label } = (args || {}) as { pageId: string; label: string }; if (!pageId || !label) throw new McpError(ErrorCode.InvalidParams, "pageId and label are required"); return await handleAddLabel(this.confluenceClient, { pageId, label }); } case "remove_label": { const { pageId, label } = (args || {}) as { pageId: string; label: string }; if (!pageId || !label) throw new McpError(ErrorCode.InvalidParams, "pageId and label are required"); return await handleRemoveLabel(this.confluenceClient, { pageId, label }); } default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } } catch (error) { console.error("Error handling request:", error); if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Internal server error: ${error instanceof Error ? error.message : String(error)}` ); } }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error("Confluence Cloud MCP server running on stdio"); } } const server = new ConfluenceServer(); server.run().catch(console.error);