Skip to main content
Glama
LawrenceCirillo

QuickBase MCP Server

index.ts13.7 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { QuickBaseClient } from './quickbase/client.js'; import { quickbaseTools } from './tools/index.js'; import { QuickBaseConfig } from './types/quickbase.js'; import dotenv from 'dotenv'; // Load environment variables dotenv.config(); class QuickBaseMCPServer { private server: Server; private qbClient: QuickBaseClient; constructor() { // Validate environment variables const config: QuickBaseConfig = { realm: process.env.QB_REALM || '', userToken: process.env.QB_USER_TOKEN || '', appId: process.env.QB_APP_ID || '', timeout: parseInt(process.env.QB_DEFAULT_TIMEOUT || '30000'), maxRetries: parseInt(process.env.QB_MAX_RETRIES || '3') }; if (!config.realm || !config.userToken || !config.appId) { throw new Error('Missing required environment variables: QB_REALM, QB_USER_TOKEN, QB_APP_ID'); } this.qbClient = new QuickBaseClient(config); this.server = new Server( { name: process.env.MCP_SERVER_NAME || 'quickbase-mcp', version: process.env.MCP_SERVER_VERSION || '1.0.0', } ); this.setupHandlers(); } private setupHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: quickbaseTools, }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { // ========== APPLICATION TOOLS ========== case 'quickbase_get_app_info': return { content: [ { type: 'text', text: JSON.stringify(await this.qbClient.getAppInfo(), null, 2), }, ], }; case 'quickbase_get_tables': return { content: [ { type: 'text', text: JSON.stringify(await this.qbClient.getAppTables(), null, 2), }, ], }; case 'quickbase_test_connection': const isConnected = await this.qbClient.testConnection(); return { content: [ { type: 'text', text: `Connection ${isConnected ? 'successful' : 'failed'}`, }, ], }; // ========== TABLE TOOLS ========== case 'quickbase_create_table': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } const tableId = await this.qbClient.createTable({ name: args.name as string, description: args.description as string }); return { content: [ { type: 'text', text: `Table created with ID: ${tableId}`, }, ], }; case 'quickbase_get_table_info': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } return { content: [ { type: 'text', text: JSON.stringify(await this.qbClient.getTableInfo(args.tableId as string), null, 2), }, ], }; case 'quickbase_delete_table': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } await this.qbClient.deleteTable(args.tableId as string); return { content: [ { type: 'text', text: `Table ${args.tableId} deleted successfully`, }, ], }; // ========== FIELD TOOLS ========== case 'quickbase_get_table_fields': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } return { content: [ { type: 'text', text: JSON.stringify(await this.qbClient.getTableFields(args.tableId as string), null, 2), }, ], }; case 'quickbase_create_field': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } const fieldId = await this.qbClient.createField(args.tableId as string, { label: args.label as string, fieldType: args.fieldType as any, required: (args.required as boolean) || false, unique: (args.unique as boolean) || false, choices: args.choices as string[], formula: args.formula as string, lookupReference: args.lookupTableId ? { tableId: args.lookupTableId as string, fieldId: args.lookupFieldId as number } : undefined }); return { content: [ { type: 'text', text: `Field created with ID: ${fieldId}`, }, ], }; case 'quickbase_update_field': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } await this.qbClient.updateField(args.tableId as string, args.fieldId as number, { label: args.label as string, required: args.required as boolean, choices: args.choices as string[] }); return { content: [ { type: 'text', text: `Field ${args.fieldId} updated successfully`, }, ], }; case 'quickbase_delete_field': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } await this.qbClient.deleteField(args.tableId as string, args.fieldId as number); return { content: [ { type: 'text', text: `Field ${args.fieldId} deleted successfully`, }, ], }; // ========== RECORD TOOLS ========== case 'quickbase_query_records': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } const records = await this.qbClient.getRecords(args.tableId as string, { select: args.select as number[], where: args.where as string, sortBy: args.sortBy as any[], top: args.top as number, skip: args.skip as number }); return { content: [ { type: 'text', text: JSON.stringify(records, null, 2), }, ], }; case 'quickbase_get_record': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } const record = await this.qbClient.getRecord( args.tableId as string, args.recordId as number, args.fieldIds as number[] ); return { content: [ { type: 'text', text: JSON.stringify(record, null, 2), }, ], }; case 'quickbase_create_record': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } const newRecordId = await this.qbClient.createRecord(args.tableId as string, { fields: args.fields as Record<string, any> }); return { content: [ { type: 'text', text: `Record created with ID: ${newRecordId}`, }, ], }; case 'quickbase_update_record': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } await this.qbClient.updateRecord( args.tableId as string, args.recordId as number, args.fields as Record<string, any> ); return { content: [ { type: 'text', text: `Record ${args.recordId} updated successfully`, }, ], }; case 'quickbase_delete_record': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } await this.qbClient.deleteRecord(args.tableId as string, args.recordId as number); return { content: [ { type: 'text', text: `Record ${args.recordId} deleted successfully`, }, ], }; case 'quickbase_bulk_create_records': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } const recordIds = await this.qbClient.createRecords( args.tableId as string, args.records as any[] ); return { content: [ { type: 'text', text: `Created ${recordIds.length} records: ${recordIds.join(', ')}`, }, ], }; case 'quickbase_search_records': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } const searchResults = await this.qbClient.searchRecords( args.tableId as string, args.searchTerm as string, args.fieldIds as number[] ); return { content: [ { type: 'text', text: JSON.stringify(searchResults, null, 2), }, ], }; // ========== RELATIONSHIP TOOLS ========== case 'quickbase_create_relationship': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } await this.qbClient.createRelationship( args.parentTableId as string, args.childTableId as string, args.foreignKeyFieldId as number ); return { content: [ { type: 'text', text: `Relationship created between ${args.parentTableId} and ${args.childTableId}`, }, ], }; case 'quickbase_get_relationships': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } return { content: [ { type: 'text', text: JSON.stringify(await this.qbClient.getRelationships(args.tableId as string), null, 2), }, ], }; // ========== UTILITY TOOLS ========== case 'quickbase_get_reports': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } return { content: [ { type: 'text', text: JSON.stringify(await this.qbClient.getReports(args.tableId as string), null, 2), }, ], }; case 'quickbase_run_report': if (!args || typeof args !== 'object') { throw new Error('Invalid arguments'); } return { content: [ { type: 'text', text: JSON.stringify( await this.qbClient.runReport(args.reportId as string, args.tableId as string), null, 2 ), }, ], }; default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${name}` ); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; console.error(`Error executing tool ${name}:`, error); throw new McpError( ErrorCode.InternalError, `Error executing ${name}: ${errorMessage}` ); } }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('QuickBase MCP server running on stdio'); } } // Start the server async function main() { try { const server = new QuickBaseMCPServer(); await server.run(); } catch (error) { console.error('Failed to start server:', error); process.exit(1); } } main().catch(console.error);

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/LawrenceCirillo/QuickBase-MCP-Server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server