UNPKG

mcp-quickbase

Version:

Work with Quickbase via Model Context Protocol

209 lines 9.23 kB
#!/usr/bin/env node "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); /** * MCP Stdio Server for Claude CLI integration */ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); const dotenv_1 = __importDefault(require("dotenv")); const logger_1 = require("./utils/logger"); const quickbase_1 = require("./client/quickbase"); const cache_1 = require("./utils/cache"); const tools_1 = require("./tools"); const validation_1 = require("./utils/validation"); // Load environment variables dotenv_1.default.config(); const logger = (0, logger_1.createLogger)('mcp-stdio-server'); /** * Main server function */ async function main() { try { // Initialize the MCP server const server = new mcp_js_1.McpServer({ name: 'Quickbase MCP Server', version: '2.0.0', }); logger.info('MCP Server created'); // Check if environment variables are configured const hasConfig = process.env.QUICKBASE_REALM_HOST && process.env.QUICKBASE_USER_TOKEN; if (!hasConfig) { logger.warn('Quickbase configuration not found in environment variables'); logger.info('Server will start but tools will not be functional until configuration is provided'); logger.info('Required environment variables: QUICKBASE_REALM_HOST, QUICKBASE_USER_TOKEN'); } let quickbaseClient = null; let cacheService = null; if (hasConfig) { // Initialize Quickbase client with configuration const config = { realmHost: process.env.QUICKBASE_REALM_HOST || '', userToken: process.env.QUICKBASE_USER_TOKEN || '', appId: process.env.QUICKBASE_APP_ID, cacheEnabled: process.env.QUICKBASE_CACHE_ENABLED !== 'false', cacheTtl: parseInt(process.env.QUICKBASE_CACHE_TTL || '3600', 10), debug: process.env.DEBUG === 'true' }; // Validate realm host format if provided if (config.realmHost && !config.realmHost.match(/^[a-zA-Z0-9-]+\.quickbase\.com$/)) { logger.error('QUICKBASE_REALM_HOST must be in format: yourcompany.quickbase.com'); logger.warn('Continuing without Quickbase client initialization'); } else { // Validate cache TTL if (isNaN(config.cacheTtl) || config.cacheTtl <= 0) { config.cacheTtl = 3600; // Default to 1 hour logger.warn('Invalid QUICKBASE_CACHE_TTL, using default: 3600'); } try { quickbaseClient = new quickbase_1.QuickbaseClient(config); cacheService = new cache_1.CacheService(config.cacheTtl, config.cacheEnabled); // Initialize tools (0, tools_1.initializeTools)(quickbaseClient, cacheService); logger.info('Quickbase client and tools initialized successfully'); } catch (error) { logger.error('Failed to initialize Quickbase client', { error }); logger.warn('Server will continue without functional tools'); } } } // Register a configuration check tool that's always available server.tool('check_configuration', 'Check if Quickbase configuration is properly set up', {}, async () => { const configured = !!quickbaseClient; const status = configured ? 'Quickbase client is configured and ready' : 'Quickbase client is not configured. Please set QUICKBASE_REALM_HOST and QUICKBASE_USER_TOKEN environment variables'; return { content: [{ type: 'text', text: JSON.stringify({ configured, status, requiredVars: ['QUICKBASE_REALM_HOST', 'QUICKBASE_USER_TOKEN'], optionalVars: ['QUICKBASE_APP_ID', 'QUICKBASE_CACHE_ENABLED', 'QUICKBASE_CACHE_TTL', 'DEBUG'] }, null, 2) }] }; }); // Register tools with MCP server if client is initialized if (quickbaseClient) { const tools = tools_1.toolRegistry.getAllTools(); tools.forEach(tool => { const schema = (0, validation_1.createMcpZodSchema)(tool.paramSchema); server.tool(tool.name, tool.description, schema, async (params) => { try { logger.info(`Executing MCP tool: ${tool.name}`); const apiResponse = await tool.execute(params); // Handle API response - only return the data if successful if (!apiResponse.success || apiResponse.error) { const errorMessage = apiResponse.error?.message || 'Tool execution failed'; logger.error(`Tool ${tool.name} failed`, { error: apiResponse.error }); throw new Error(errorMessage); } return { content: [{ type: 'text', text: JSON.stringify(apiResponse.data, null, 2) }] }; } catch (error) { logger.error(`Error executing MCP tool ${tool.name}`, { error }); throw error; } }); logger.info(`Registered MCP tool: ${tool.name}`); }); logger.info(`Registered ${tools.length} Quickbase tools with MCP Server`); } else { logger.info('No Quickbase tools registered (configuration missing)'); } // Create stdio transport const transport = new stdio_js_1.StdioServerTransport(); // Connect and run await server.connect(transport); logger.info('MCP server connected via stdio and ready for requests'); } catch (error) { logger.error('Failed to start MCP server', { error }); await gracefulShutdown(); process.exit(1); } } /** * Graceful shutdown handler */ async function gracefulShutdown() { try { logger.info('Initiating graceful shutdown...'); // Give pending operations time to complete await new Promise(resolve => setTimeout(resolve, 1000)); // Cleanup cache instances const { CacheService } = await Promise.resolve().then(() => __importStar(require('./utils/cache.js'))); const stats = CacheService.getStats(); if (stats.instances > 0) { logger.info(`Cleaning up ${stats.instances} cache instances`); } logger.info('Graceful shutdown completed'); } catch (error) { logger.error('Error during graceful shutdown', { error }); } } // Install shutdown handlers process.on('SIGTERM', async () => { logger.info('SIGTERM received'); await gracefulShutdown(); process.exit(0); }); process.on('SIGINT', async () => { logger.info('SIGINT received'); await gracefulShutdown(); process.exit(0); }); // Start the server main().catch(async (error) => { logger.error('Unhandled error in main', { error }); await gracefulShutdown(); process.exit(1); }); //# sourceMappingURL=mcp-stdio-server.js.map