UNPKG

@runbook-docs/mcp-server

Version:
158 lines (157 loc) 5.68 kB
#!/usr/bin/env node "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildServer = buildServer; const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js"); const types_js_1 = require("@modelcontextprotocol/sdk/types.js"); const resources_1 = require("./resources/resources"); const tools_1 = require("./tools/tools"); const prompts_1 = require("./prompts/prompts"); const client_1 = require("@runbook-docs/client"); function kebabToPascal(kebab) { if (!kebab) return ''; return kebab .split('-') .filter(Boolean) .map((s) => s[0].toUpperCase() + s.slice(1).toLowerCase()) .join(''); } function getErrorResponse(e) { let text; if (e instanceof client_1.RequestError) { text = JSON.stringify(e.attributes); } else { text = e instanceof Error ? e.message : String(e); } return { content: [ { type: 'text', text } ], isError: true }; } async function buildServer(state) { const server = new index_js_1.Server({ name: kebabToPascal(state.name), version: '1.7.0' }, { capabilities: { resources: {}, tools: {}, prompts: {} }, instructions: ` This MCP server provides access to Runbook for document management and business workflow automation. Always use Runbook MCP tools (e.g. runbook-create-article, runbook-update-article, runbook-list-articles) to interact with Runbook. Never attempt alternative approaches such as direct HTTP requests or curl commands instead of using these tools. This MCP server connects to the Runbook tenant: ${state.baseUrl}. When multiple Runbook MCP servers are configured, each connects to a different tenant. Always confirm which tenant the user intends to operate on before creating or updating content. If unclear, ask the user to specify the target tenant. ` }); const resourceHandlersInstance = (0, resources_1.resourceHandlers)(state); const toolHandlersInstance = (0, tools_1.toolHandlers)(state); const promptHandlersInstance = (0, prompts_1.promptHandlers)(state); server.setRequestHandler(types_js_1.ListResourcesRequestSchema, async () => { try { return { resources: await resourceHandlersInstance.listResources() }; } catch (e) { return getErrorResponse(e); } }); server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) => { const { uri } = request.params; try { const content = await resourceHandlersInstance.readResource(uri); return { contents: [ { uri, text: content, mimeType: 'application/json' } ] }; } catch (e) { return getErrorResponse(e); } }); server.setRequestHandler(types_js_1.ListResourceTemplatesRequestSchema, () => { try { return resourceHandlersInstance.listResourceTemplates(); } catch (e) { return getErrorResponse(e); } }); server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => { return { tools: Object.entries(toolHandlersInstance).map(([name, handler]) => ({ name, description: handler.description, inputSchema: handler.inputSchema, annotations: handler.annotations })) }; }); server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const handler = toolHandlersInstance[name]; if (!handler) { throw new Error(`Unknown tool: ${name}`); } try { return await handler.handler(args); } catch (e) { return getErrorResponse(e); } }); server.setRequestHandler(types_js_1.ListPromptsRequestSchema, async () => { return { prompts: Object.entries(promptHandlersInstance).map(([, handler]) => ({ name: handler.name, title: handler.title || handler.name, description: handler.description, arguments: handler.arguments })) }; }); server.setRequestHandler(types_js_1.GetPromptRequestSchema, async (request) => { const { name, arguments: args } = request.params; const handler = promptHandlersInstance[name]; if (!handler) { throw new Error(`Unknown prompt: ${name}`); } // Simple template replacement let prompt = handler.prompt; if (args) { for (const [key, value] of Object.entries(args)) { const regex = new RegExp(`{{${key}}}`, 'g'); prompt = prompt.replace(regex, String(value)); } // Handle conditional blocks like {{#if runStateUid}} prompt = prompt.replace(/{{#if (\w+)}}([^}]*){{\/if}}/g, (_, condition, content) => { return args[condition] ? content : ''; }); } return { description: handler.description, messages: [ { role: 'user', content: { type: 'text', text: prompt } } ] }; }); return server; }