mcp-quickbase
Version:
Work with Quickbase via Model Context Protocol
209 lines • 9.23 kB
JavaScript
;
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