UNPKG

mcp-quickbase

Version:

Work with Quickbase via Model Context Protocol

224 lines 9.68 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