UNPKG

@prsna_ai/mcp-server

Version:

Model Context Protocol server for PRSNA personality profiles and communication insights

159 lines 5.43 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { config } from 'dotenv'; import { logger } from './utils/logger.js'; import { CONFIG } from './utils/constants.js'; import { allTools, toolHandlers } from './tools/index.js'; import { safeJSONStringify } from './utils/json.js'; // Load environment variables config(); // Validate required environment variables if (!process.env.PRSNA_API_TOKEN) { logger.error('PRSNA_API_TOKEN environment variable is required'); process.exit(1); } // Create MCP server instance const server = new Server({ name: CONFIG.MCP.NAME, version: CONFIG.MCP.VERSION, }, { capabilities: { tools: {}, }, }); // Register tool list handler server.setRequestHandler(ListToolsRequestSchema, async () => { logger.debug('Listing available tools', { toolCount: allTools.length }); try { const toolsResponse = { tools: allTools.map(tool => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema })) }; // Validate the response can be serialized properly const serialized = safeJSONStringify(toolsResponse); if (!serialized || serialized.length === 0) { throw new Error('Failed to serialize tools response'); } logger.debug('Tools list serialized successfully', { responseLength: serialized.length, toolCount: toolsResponse.tools.length }); return toolsResponse; } catch (error) { logger.error('Failed to list tools', { error: error.message }); // Return a minimal response if there's an error return { tools: [] }; } }); // Register tool call handler server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; logger.info('Tool called', { toolName: name, args }); // Find the tool handler const handler = toolHandlers[name]; if (!handler) { logger.error('Unknown tool called', { toolName: name }); throw new Error(`Unknown tool: ${name}`); } try { // Call the tool handler const result = await handler(args); // Validate result format if (!result || typeof result !== 'object' || !result.content || !Array.isArray(result.content)) { throw new Error('Invalid tool response format'); } // Validate content array for (const content of result.content) { if (!content || typeof content !== 'object' || content.type !== 'text' || typeof content.text !== 'string') { throw new Error('Invalid content format in tool response'); } } logger.info('Tool execution completed', { toolName: name, success: true }); return result; } catch (error) { const errorMessage = error.message; logger.error('Tool execution failed', { toolName: name, error: errorMessage }); // Return error response in the expected format using safe JSON stringify return { content: [{ type: "text", text: safeJSONStringify({ success: false, error: errorMessage, tool: name }, 2) }] }; } }); // Handle server lifecycle const transport = new StdioServerTransport(); // Error handlers process.on('uncaughtException', (error) => { logger.error('Uncaught exception', { error: error.message }); process.exit(1); }); process.on('unhandledRejection', (reason, promise) => { logger.error('Unhandled rejection', { reason, promise }); process.exit(1); }); process.on('SIGINT', () => { logger.info('Received SIGINT, shutting down gracefully'); server.close(); process.exit(0); }); process.on('SIGTERM', () => { logger.info('Received SIGTERM, shutting down gracefully'); server.close(); process.exit(0); }); // Start the server async function main() { try { logger.info('Starting PRSNA MCP server', { name: CONFIG.MCP.NAME, version: CONFIG.MCP.VERSION, nodeEnv: process.env.NODE_ENV || 'development', apiUrl: CONFIG.API.BASE_URL, toolCount: allTools.length }); // Log available tools for debugging logger.debug('Available tools:', { tools: allTools.map(tool => ({ name: tool.name, description: tool.description })) }); await server.connect(transport); logger.info('PRSNA MCP server started successfully'); // Keep the process running await new Promise(() => { }); } catch (error) { logger.error('Failed to start MCP server', { error: error.message }); process.exit(1); } } // Start the server main().catch((error) => { logger.error('Server startup failed', { error: error.message }); process.exit(1); }); //# sourceMappingURL=index.js.map