mcp-quickbase 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +82 -0
- package/LICENSE +21 -0
- package/README.md +301 -0
- package/dist/client/quickbase.d.ts +28 -0
- package/dist/client/quickbase.js +235 -0
- package/dist/client/quickbase.js.map +1 -0
- package/dist/mcp/index.d.ts +4 -0
- package/dist/mcp/index.js +21 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +19 -0
- package/dist/mcp/server.js +102 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp-stdio-server.d.ts +2 -0
- package/dist/mcp-stdio-server.js +168 -0
- package/dist/mcp-stdio-server.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +318 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/apps/create_app.d.ts +87 -0
- package/dist/tools/apps/create_app.js +87 -0
- package/dist/tools/apps/create_app.js.map +1 -0
- package/dist/tools/apps/index.d.ts +9 -0
- package/dist/tools/apps/index.js +40 -0
- package/dist/tools/apps/index.js.map +1 -0
- package/dist/tools/apps/list_tables.d.ts +108 -0
- package/dist/tools/apps/list_tables.js +100 -0
- package/dist/tools/apps/list_tables.js.map +1 -0
- package/dist/tools/apps/update_app.d.ts +91 -0
- package/dist/tools/apps/update_app.js +99 -0
- package/dist/tools/apps/update_app.js.map +1 -0
- package/dist/tools/base.d.ts +47 -0
- package/dist/tools/base.js +63 -0
- package/dist/tools/base.js.map +1 -0
- package/dist/tools/configure_cache.d.ts +81 -0
- package/dist/tools/configure_cache.js +77 -0
- package/dist/tools/configure_cache.js.map +1 -0
- package/dist/tools/fields/create_field.d.ts +121 -0
- package/dist/tools/fields/create_field.js +102 -0
- package/dist/tools/fields/create_field.js.map +1 -0
- package/dist/tools/fields/index.d.ts +8 -0
- package/dist/tools/fields/index.js +37 -0
- package/dist/tools/fields/index.js.map +1 -0
- package/dist/tools/fields/update_field.d.ts +112 -0
- package/dist/tools/fields/update_field.js +114 -0
- package/dist/tools/fields/update_field.js.map +1 -0
- package/dist/tools/files/download_file.d.ts +111 -0
- package/dist/tools/files/download_file.js +173 -0
- package/dist/tools/files/download_file.js.map +1 -0
- package/dist/tools/files/index.d.ts +8 -0
- package/dist/tools/files/index.js +37 -0
- package/dist/tools/files/index.js.map +1 -0
- package/dist/tools/files/upload_file.d.ts +107 -0
- package/dist/tools/files/upload_file.js +211 -0
- package/dist/tools/files/upload_file.js.map +1 -0
- package/dist/tools/index.d.ts +18 -0
- package/dist/tools/index.js +65 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/records/bulk_create_records.d.ts +75 -0
- package/dist/tools/records/bulk_create_records.js +104 -0
- package/dist/tools/records/bulk_create_records.js.map +1 -0
- package/dist/tools/records/bulk_update_records.d.ts +77 -0
- package/dist/tools/records/bulk_update_records.js +102 -0
- package/dist/tools/records/bulk_update_records.js.map +1 -0
- package/dist/tools/records/create_record.d.ts +68 -0
- package/dist/tools/records/create_record.js +123 -0
- package/dist/tools/records/create_record.js.map +1 -0
- package/dist/tools/records/index.d.ts +11 -0
- package/dist/tools/records/index.js +46 -0
- package/dist/tools/records/index.js.map +1 -0
- package/dist/tools/records/query_records.d.ts +164 -0
- package/dist/tools/records/query_records.js +261 -0
- package/dist/tools/records/query_records.js.map +1 -0
- package/dist/tools/records/update_record.d.ts +81 -0
- package/dist/tools/records/update_record.js +99 -0
- package/dist/tools/records/update_record.js.map +1 -0
- package/dist/tools/registry.d.ts +41 -0
- package/dist/tools/registry.js +66 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/reports/index.d.ts +6 -0
- package/dist/tools/reports/index.js +31 -0
- package/dist/tools/reports/index.js.map +1 -0
- package/dist/tools/reports/run_report.d.ts +70 -0
- package/dist/tools/reports/run_report.js +72 -0
- package/dist/tools/reports/run_report.js.map +1 -0
- package/dist/tools/tables/create_table.d.ts +142 -0
- package/dist/tools/tables/create_table.js +119 -0
- package/dist/tools/tables/create_table.js.map +1 -0
- package/dist/tools/tables/get_table_fields.d.ts +108 -0
- package/dist/tools/tables/get_table_fields.js +96 -0
- package/dist/tools/tables/get_table_fields.js.map +1 -0
- package/dist/tools/tables/index.d.ts +9 -0
- package/dist/tools/tables/index.js +40 -0
- package/dist/tools/tables/index.js.map +1 -0
- package/dist/tools/tables/update_table.d.ts +91 -0
- package/dist/tools/tables/update_table.js +99 -0
- package/dist/tools/tables/update_table.js.map +1 -0
- package/dist/tools/test_connection.d.ts +51 -0
- package/dist/tools/test_connection.js +101 -0
- package/dist/tools/test_connection.js.map +1 -0
- package/dist/types/api.d.ts +70 -0
- package/dist/types/api.js +6 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/config.d.ts +49 -0
- package/dist/types/config.js +3 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/mcp.d.ts +55 -0
- package/dist/types/mcp.js +3 -0
- package/dist/types/mcp.js.map +1 -0
- package/dist/utils/cache.d.ts +87 -0
- package/dist/utils/cache.js +211 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/file.d.ts +40 -0
- package/dist/utils/file.js +167 -0
- package/dist/utils/file.js.map +1 -0
- package/dist/utils/logger.d.ts +37 -0
- package/dist/utils/logger.js +144 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/retry.d.ts +39 -0
- package/dist/utils/retry.js +88 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/validation.d.ts +32 -0
- package/dist/utils/validation.js +227 -0
- package/dist/utils/validation.js.map +1 -0
- package/docs/README.md +41 -0
- package/docs/architecture.md +94 -0
- package/docs/claude-prompts.md +218 -0
- package/docs/deployment.md +244 -0
- package/docs/developer-guide.md +537 -0
- package/docs/final-qa-report.md +243 -0
- package/docs/performance-benchmarks.md +306 -0
- package/docs/quick-reference.md +109 -0
- package/docs/quickstart.md +183 -0
- package/docs/security-review.md +263 -0
- package/docs/tools.md +269 -0
- package/package.json +68 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
/**
|
|
18
|
+
* MCP (Model Context Protocol) module exports
|
|
19
|
+
*/
|
|
20
|
+
__exportStar(require("./server"), exports);
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;GAEG;AACH,2CAAyB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Request, Response } from 'express';
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
4
|
+
/**
|
|
5
|
+
* Create and configure an MCP server instance
|
|
6
|
+
*/
|
|
7
|
+
export declare function createMcpServer(): McpServer;
|
|
8
|
+
/**
|
|
9
|
+
* Register tools with an existing MCP server
|
|
10
|
+
*/
|
|
11
|
+
export declare function registerMcpTools(server: McpServer): void;
|
|
12
|
+
/**
|
|
13
|
+
* Create an HTTP transport for the MCP server
|
|
14
|
+
*/
|
|
15
|
+
export declare function createHttpTransport(): StreamableHTTPServerTransport;
|
|
16
|
+
/**
|
|
17
|
+
* Handle an MCP request via HTTP
|
|
18
|
+
*/
|
|
19
|
+
export declare function handleMcpRequest(server: McpServer, transport: StreamableHTTPServerTransport, req: Request, res: Response): Promise<void>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createMcpServer = createMcpServer;
|
|
4
|
+
exports.registerMcpTools = registerMcpTools;
|
|
5
|
+
exports.createHttpTransport = createHttpTransport;
|
|
6
|
+
exports.handleMcpRequest = handleMcpRequest;
|
|
7
|
+
/**
|
|
8
|
+
* MCP Server implementation using the official Model Context Protocol SDK
|
|
9
|
+
*/
|
|
10
|
+
const crypto_1 = require("crypto");
|
|
11
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
12
|
+
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
13
|
+
const logger_1 = require("../utils/logger");
|
|
14
|
+
const tools_1 = require("../tools");
|
|
15
|
+
const validation_1 = require("../utils/validation");
|
|
16
|
+
const logger = (0, logger_1.createLogger)('mcp-server');
|
|
17
|
+
/**
|
|
18
|
+
* Create and configure an MCP server instance
|
|
19
|
+
*/
|
|
20
|
+
function createMcpServer() {
|
|
21
|
+
// Initialize the MCP server with our app info
|
|
22
|
+
const server = new mcp_js_1.McpServer({
|
|
23
|
+
name: 'Quickbase MCP Server',
|
|
24
|
+
version: '2.0.0',
|
|
25
|
+
});
|
|
26
|
+
logger.info('MCP Server created');
|
|
27
|
+
return server;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Register tools with an existing MCP server
|
|
31
|
+
*/
|
|
32
|
+
function registerMcpTools(server) {
|
|
33
|
+
registerTools(server);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Register all tools with the MCP server
|
|
37
|
+
*/
|
|
38
|
+
function registerTools(server) {
|
|
39
|
+
const tools = tools_1.toolRegistry.getAllTools();
|
|
40
|
+
tools.forEach(tool => {
|
|
41
|
+
// Create a Zod schema from our tool's parameter schema
|
|
42
|
+
const schema = (0, validation_1.createMcpZodSchema)(tool.paramSchema);
|
|
43
|
+
// Register the tool with the MCP server
|
|
44
|
+
server.tool(tool.name, tool.description, schema, async (params) => {
|
|
45
|
+
try {
|
|
46
|
+
logger.info(`Executing MCP tool: ${tool.name}`);
|
|
47
|
+
const apiResponse = await tool.execute(params);
|
|
48
|
+
// Handle API response - only return the data if successful
|
|
49
|
+
if (!apiResponse.success || apiResponse.error) {
|
|
50
|
+
const errorMessage = apiResponse.error?.message || 'Tool execution failed';
|
|
51
|
+
logger.error(`Tool ${tool.name} failed`, { error: apiResponse.error });
|
|
52
|
+
throw new Error(errorMessage);
|
|
53
|
+
}
|
|
54
|
+
// Ensure proper JSON formatting by using a standardized response structure
|
|
55
|
+
return {
|
|
56
|
+
content: [{
|
|
57
|
+
type: 'text',
|
|
58
|
+
text: JSON.stringify(apiResponse.data, null, 2)
|
|
59
|
+
}]
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
logger.error(`Error executing MCP tool ${tool.name}`, { error });
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
logger.info(`Registered MCP tool: ${tool.name}`);
|
|
68
|
+
});
|
|
69
|
+
logger.info(`Registered ${tools.length} tools with MCP Server`);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Create an HTTP transport for the MCP server
|
|
73
|
+
*/
|
|
74
|
+
function createHttpTransport() {
|
|
75
|
+
// Create a new transport with proper session management
|
|
76
|
+
// Following the TypeScript SDK examples for HTTP transport
|
|
77
|
+
return new streamableHttp_js_1.StreamableHTTPServerTransport({
|
|
78
|
+
sessionIdGenerator: () => (0, crypto_1.randomUUID)(),
|
|
79
|
+
enableJsonResponse: true
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Handle an MCP request via HTTP
|
|
84
|
+
*/
|
|
85
|
+
async function handleMcpRequest(server, transport, req, res) {
|
|
86
|
+
try {
|
|
87
|
+
logger.info('Handling MCP request');
|
|
88
|
+
await transport.handleRequest(req, res, req.body);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
logger.error('Error handling MCP request', { error });
|
|
92
|
+
res.status(500).json({
|
|
93
|
+
jsonrpc: '2.0',
|
|
94
|
+
error: {
|
|
95
|
+
code: -32000,
|
|
96
|
+
message: error instanceof Error ? error.message : 'Unknown error'
|
|
97
|
+
},
|
|
98
|
+
id: req.body?.id || null
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
/**
|
|
41
|
+
* MCP Stdio Server for Claude CLI integration
|
|
42
|
+
*/
|
|
43
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
44
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
45
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
46
|
+
const logger_1 = require("./utils/logger");
|
|
47
|
+
const quickbase_1 = require("./client/quickbase");
|
|
48
|
+
const cache_1 = require("./utils/cache");
|
|
49
|
+
const tools_1 = require("./tools");
|
|
50
|
+
const validation_1 = require("./utils/validation");
|
|
51
|
+
// Load environment variables
|
|
52
|
+
dotenv_1.default.config();
|
|
53
|
+
const logger = (0, logger_1.createLogger)('mcp-stdio-server');
|
|
54
|
+
/**
|
|
55
|
+
* Main server function
|
|
56
|
+
*/
|
|
57
|
+
async function main() {
|
|
58
|
+
try {
|
|
59
|
+
// Initialize the MCP server
|
|
60
|
+
const server = new mcp_js_1.McpServer({
|
|
61
|
+
name: 'Quickbase MCP Server',
|
|
62
|
+
version: '2.0.0',
|
|
63
|
+
});
|
|
64
|
+
logger.info('MCP Server created');
|
|
65
|
+
// Initialize Quickbase client
|
|
66
|
+
const config = {
|
|
67
|
+
realmHost: process.env.QUICKBASE_REALM_HOST || '',
|
|
68
|
+
userToken: process.env.QUICKBASE_USER_TOKEN || '',
|
|
69
|
+
appId: process.env.QUICKBASE_APP_ID,
|
|
70
|
+
cacheEnabled: process.env.QUICKBASE_CACHE_ENABLED !== 'false',
|
|
71
|
+
cacheTtl: parseInt(process.env.QUICKBASE_CACHE_TTL || '3600', 10),
|
|
72
|
+
debug: process.env.DEBUG === 'true'
|
|
73
|
+
};
|
|
74
|
+
// Validate required configuration
|
|
75
|
+
if (!config.realmHost || !config.userToken) {
|
|
76
|
+
throw new Error('QUICKBASE_REALM_HOST and QUICKBASE_USER_TOKEN are required');
|
|
77
|
+
}
|
|
78
|
+
// Validate realm host format
|
|
79
|
+
if (!config.realmHost.match(/^[a-zA-Z0-9-]+\.quickbase\.com$/)) {
|
|
80
|
+
throw new Error('QUICKBASE_REALM_HOST must be in format: yourcompany.quickbase.com');
|
|
81
|
+
}
|
|
82
|
+
// Validate cache TTL
|
|
83
|
+
if (isNaN(config.cacheTtl) || config.cacheTtl <= 0) {
|
|
84
|
+
throw new Error('QUICKBASE_CACHE_TTL must be a positive number');
|
|
85
|
+
}
|
|
86
|
+
const quickbaseClient = new quickbase_1.QuickbaseClient(config);
|
|
87
|
+
const cacheService = new cache_1.CacheService(config.cacheTtl, config.cacheEnabled);
|
|
88
|
+
// Initialize tools
|
|
89
|
+
(0, tools_1.initializeTools)(quickbaseClient, cacheService);
|
|
90
|
+
// Register tools with MCP server
|
|
91
|
+
const tools = tools_1.toolRegistry.getAllTools();
|
|
92
|
+
tools.forEach(tool => {
|
|
93
|
+
const schema = (0, validation_1.createMcpZodSchema)(tool.paramSchema);
|
|
94
|
+
server.tool(tool.name, tool.description, schema, async (params) => {
|
|
95
|
+
try {
|
|
96
|
+
logger.info(`Executing MCP tool: ${tool.name}`);
|
|
97
|
+
const apiResponse = await tool.execute(params);
|
|
98
|
+
// Handle API response - only return the data if successful
|
|
99
|
+
if (!apiResponse.success || apiResponse.error) {
|
|
100
|
+
const errorMessage = apiResponse.error?.message || 'Tool execution failed';
|
|
101
|
+
logger.error(`Tool ${tool.name} failed`, { error: apiResponse.error });
|
|
102
|
+
throw new Error(errorMessage);
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
content: [{
|
|
106
|
+
type: 'text',
|
|
107
|
+
text: JSON.stringify(apiResponse.data, null, 2)
|
|
108
|
+
}]
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
logger.error(`Error executing MCP tool ${tool.name}`, { error });
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
logger.info(`Registered MCP tool: ${tool.name}`);
|
|
117
|
+
});
|
|
118
|
+
logger.info(`Registered ${tools.length} tools with MCP Server`);
|
|
119
|
+
// Create stdio transport
|
|
120
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
121
|
+
// Connect and run
|
|
122
|
+
await server.connect(transport);
|
|
123
|
+
logger.info('MCP server connected via stdio and ready for requests');
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
logger.error('Failed to start MCP server', { error });
|
|
127
|
+
await gracefulShutdown();
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Graceful shutdown handler
|
|
133
|
+
*/
|
|
134
|
+
async function gracefulShutdown() {
|
|
135
|
+
try {
|
|
136
|
+
logger.info('Initiating graceful shutdown...');
|
|
137
|
+
// Give pending operations time to complete
|
|
138
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
139
|
+
// Cleanup cache instances
|
|
140
|
+
const { CacheService } = await Promise.resolve().then(() => __importStar(require('./utils/cache.js')));
|
|
141
|
+
const stats = CacheService.getStats();
|
|
142
|
+
if (stats.instances > 0) {
|
|
143
|
+
logger.info(`Cleaning up ${stats.instances} cache instances`);
|
|
144
|
+
}
|
|
145
|
+
logger.info('Graceful shutdown completed');
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
logger.error('Error during graceful shutdown', { error });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Install shutdown handlers
|
|
152
|
+
process.on('SIGTERM', async () => {
|
|
153
|
+
logger.info('SIGTERM received');
|
|
154
|
+
await gracefulShutdown();
|
|
155
|
+
process.exit(0);
|
|
156
|
+
});
|
|
157
|
+
process.on('SIGINT', async () => {
|
|
158
|
+
logger.info('SIGINT received');
|
|
159
|
+
await gracefulShutdown();
|
|
160
|
+
process.exit(0);
|
|
161
|
+
});
|
|
162
|
+
// Start the server
|
|
163
|
+
main().catch(async (error) => {
|
|
164
|
+
logger.error('Unhandled error in main', { error });
|
|
165
|
+
await gracefulShutdown();
|
|
166
|
+
process.exit(1);
|
|
167
|
+
});
|
|
168
|
+
//# sourceMappingURL=mcp-stdio-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-stdio-server.js","sourceRoot":"","sources":["../src/mcp-stdio-server.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const express_1 = __importDefault(require("express"));
|
|
7
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
8
|
+
const cors_1 = __importDefault(require("cors"));
|
|
9
|
+
const logger_1 = require("./utils/logger");
|
|
10
|
+
const quickbase_1 = require("./client/quickbase");
|
|
11
|
+
const cache_1 = require("./utils/cache");
|
|
12
|
+
const tools_1 = require("./tools");
|
|
13
|
+
const mcp_1 = require("./mcp");
|
|
14
|
+
// Load environment variables
|
|
15
|
+
dotenv_1.default.config();
|
|
16
|
+
const logger = (0, logger_1.createLogger)('server');
|
|
17
|
+
// Initialize Express app
|
|
18
|
+
const app = (0, express_1.default)();
|
|
19
|
+
app.use(express_1.default.json());
|
|
20
|
+
app.use((0, cors_1.default)());
|
|
21
|
+
// Configuration
|
|
22
|
+
const PORT = process.env.PORT || 3536; // Changed from 3000 to avoid port conflicts
|
|
23
|
+
// Initialize Quickbase client
|
|
24
|
+
let quickbaseClient = null;
|
|
25
|
+
let cacheService = null;
|
|
26
|
+
// Track connector status
|
|
27
|
+
const connectorStatus = {
|
|
28
|
+
status: 'disconnected',
|
|
29
|
+
error: null
|
|
30
|
+
};
|
|
31
|
+
// Initialize MCP server and transport
|
|
32
|
+
const mcpServer = (0, mcp_1.createMcpServer)();
|
|
33
|
+
const mcpTransport = (0, mcp_1.createHttpTransport)();
|
|
34
|
+
/**
|
|
35
|
+
* Initialize Quickbase client from environment variables
|
|
36
|
+
*/
|
|
37
|
+
function initializeClient() {
|
|
38
|
+
try {
|
|
39
|
+
// Validate required environment variables
|
|
40
|
+
const realmHost = process.env.QUICKBASE_REALM_HOST;
|
|
41
|
+
const userToken = process.env.QUICKBASE_USER_TOKEN;
|
|
42
|
+
if (!realmHost) {
|
|
43
|
+
throw new Error('QUICKBASE_REALM_HOST environment variable is required');
|
|
44
|
+
}
|
|
45
|
+
if (!userToken) {
|
|
46
|
+
throw new Error('QUICKBASE_USER_TOKEN environment variable is required');
|
|
47
|
+
}
|
|
48
|
+
// Safely parse cache TTL with validation
|
|
49
|
+
const cacheTtlStr = process.env.QUICKBASE_CACHE_TTL || '3600';
|
|
50
|
+
const cacheTtl = parseInt(cacheTtlStr, 10);
|
|
51
|
+
if (isNaN(cacheTtl) || cacheTtl <= 0) {
|
|
52
|
+
throw new Error(`Invalid QUICKBASE_CACHE_TTL value: ${cacheTtlStr}. Must be a positive integer.`);
|
|
53
|
+
}
|
|
54
|
+
const config = {
|
|
55
|
+
realmHost,
|
|
56
|
+
userToken,
|
|
57
|
+
appId: process.env.QUICKBASE_APP_ID,
|
|
58
|
+
cacheEnabled: process.env.QUICKBASE_CACHE_ENABLED !== 'false',
|
|
59
|
+
cacheTtl,
|
|
60
|
+
debug: process.env.DEBUG === 'true'
|
|
61
|
+
};
|
|
62
|
+
quickbaseClient = new quickbase_1.QuickbaseClient(config);
|
|
63
|
+
cacheService = new cache_1.CacheService(config.cacheTtl, config.cacheEnabled);
|
|
64
|
+
// Initialize MCP tools
|
|
65
|
+
(0, tools_1.initializeTools)(quickbaseClient, cacheService);
|
|
66
|
+
// Register tools with MCP server after initialization
|
|
67
|
+
(0, mcp_1.registerMcpTools)(mcpServer);
|
|
68
|
+
connectorStatus.status = 'connected';
|
|
69
|
+
connectorStatus.error = null;
|
|
70
|
+
logger.info('Quickbase client initialized successfully');
|
|
71
|
+
logger.info(`Registered tools: ${tools_1.toolRegistry.getToolNames().join(', ')}`);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
connectorStatus.status = 'error';
|
|
75
|
+
connectorStatus.error = error instanceof Error ? error.message : 'Unknown error';
|
|
76
|
+
logger.error('Failed to initialize Quickbase client', { error });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// MCP tool execution endpoint
|
|
80
|
+
app.post('/api/:tool', async (req, res) => {
|
|
81
|
+
const toolName = req.params.tool;
|
|
82
|
+
const params = req.body || {};
|
|
83
|
+
logger.info(`Executing tool: ${toolName}`, { params });
|
|
84
|
+
if (!quickbaseClient) {
|
|
85
|
+
return res.status(500).json({
|
|
86
|
+
success: false,
|
|
87
|
+
error: {
|
|
88
|
+
message: 'Quickbase client not initialized',
|
|
89
|
+
type: 'ConfigurationError'
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
const tool = tools_1.toolRegistry.getTool(toolName);
|
|
94
|
+
if (!tool) {
|
|
95
|
+
return res.status(404).json({
|
|
96
|
+
success: false,
|
|
97
|
+
error: {
|
|
98
|
+
message: `Tool ${toolName} not found`,
|
|
99
|
+
type: 'NotFoundError'
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const result = await tool.execute(params);
|
|
105
|
+
res.json(result);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
logger.error(`Error executing tool ${toolName}`, { error });
|
|
109
|
+
res.status(500).json({
|
|
110
|
+
success: false,
|
|
111
|
+
error: {
|
|
112
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
113
|
+
type: error instanceof Error ? error.name : 'UnknownError'
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
// MCP batch tool execution
|
|
119
|
+
app.post('/api/batch', async (req, res) => {
|
|
120
|
+
const requests = req.body.requests || [];
|
|
121
|
+
if (!Array.isArray(requests) || requests.length === 0) {
|
|
122
|
+
return res.status(400).json({
|
|
123
|
+
success: false,
|
|
124
|
+
error: {
|
|
125
|
+
message: 'Invalid batch request format',
|
|
126
|
+
type: 'ValidationError'
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
logger.info(`Executing batch request with ${requests.length} tools`);
|
|
131
|
+
if (!quickbaseClient) {
|
|
132
|
+
return res.status(500).json({
|
|
133
|
+
success: false,
|
|
134
|
+
error: {
|
|
135
|
+
message: 'Quickbase client not initialized',
|
|
136
|
+
type: 'ConfigurationError'
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
const results = await Promise.all(requests.map(async (request) => {
|
|
142
|
+
const tool = tools_1.toolRegistry.getTool(request.tool);
|
|
143
|
+
if (!tool) {
|
|
144
|
+
return {
|
|
145
|
+
tool: request.tool,
|
|
146
|
+
success: false,
|
|
147
|
+
error: {
|
|
148
|
+
message: `Tool ${request.tool} not found`,
|
|
149
|
+
type: 'NotFoundError'
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const result = await tool.execute(request.params || {});
|
|
155
|
+
return {
|
|
156
|
+
tool: request.tool,
|
|
157
|
+
...result
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
return {
|
|
162
|
+
tool: request.tool,
|
|
163
|
+
success: false,
|
|
164
|
+
error: {
|
|
165
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
166
|
+
type: error instanceof Error ? error.name : 'UnknownError'
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}));
|
|
171
|
+
res.json({
|
|
172
|
+
success: true,
|
|
173
|
+
results
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
logger.error('Error executing batch request', { error });
|
|
178
|
+
res.status(500).json({
|
|
179
|
+
success: false,
|
|
180
|
+
error: {
|
|
181
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
182
|
+
type: error instanceof Error ? error.name : 'UnknownError'
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
// MCP schema endpoint
|
|
188
|
+
app.get('/api/schema', (_req, res) => {
|
|
189
|
+
if (!quickbaseClient) {
|
|
190
|
+
return res.status(500).json({
|
|
191
|
+
success: false,
|
|
192
|
+
error: {
|
|
193
|
+
message: 'Quickbase client not initialized',
|
|
194
|
+
type: 'ConfigurationError'
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
const tools = tools_1.toolRegistry.getAllTools().map(tool => ({
|
|
199
|
+
name: tool.name,
|
|
200
|
+
description: tool.description,
|
|
201
|
+
schema: tool.paramSchema
|
|
202
|
+
}));
|
|
203
|
+
res.json({
|
|
204
|
+
success: true,
|
|
205
|
+
data: {
|
|
206
|
+
tools
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
// Status route
|
|
211
|
+
app.get('/status', (_req, res) => {
|
|
212
|
+
res.json({
|
|
213
|
+
name: 'Quickbase MCP Server',
|
|
214
|
+
version: '2.0.0',
|
|
215
|
+
status: connectorStatus.status,
|
|
216
|
+
error: connectorStatus.error,
|
|
217
|
+
tools: quickbaseClient ? tools_1.toolRegistry.getToolNames() : []
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
// MCP Protocol routes
|
|
221
|
+
// POST endpoint for MCP messages
|
|
222
|
+
app.post('/mcp', async (req, res) => {
|
|
223
|
+
if (!quickbaseClient) {
|
|
224
|
+
return res.status(500).json({
|
|
225
|
+
jsonrpc: '2.0',
|
|
226
|
+
error: {
|
|
227
|
+
code: -32000,
|
|
228
|
+
message: 'Quickbase client not initialized'
|
|
229
|
+
},
|
|
230
|
+
id: req.body?.id || null
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
logger.info('Received MCP protocol request');
|
|
235
|
+
await (0, mcp_1.handleMcpRequest)(mcpServer, mcpTransport, req, res);
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
logger.error('Error handling MCP protocol request', { error });
|
|
239
|
+
res.status(500).json({
|
|
240
|
+
jsonrpc: '2.0',
|
|
241
|
+
error: {
|
|
242
|
+
code: -32000,
|
|
243
|
+
message: error instanceof Error ? error.message : 'Unknown error'
|
|
244
|
+
},
|
|
245
|
+
id: req.body?.id || null
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
// GET endpoint for MCP long-polling notifications
|
|
250
|
+
app.get('/mcp', async (req, res) => {
|
|
251
|
+
try {
|
|
252
|
+
logger.info('Received MCP protocol GET request for notifications');
|
|
253
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
254
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
255
|
+
res.setHeader('Connection', 'keep-alive');
|
|
256
|
+
// Keep connection open for server-sent events
|
|
257
|
+
const interval = setInterval(() => {
|
|
258
|
+
res.write(': keepalive\n\n');
|
|
259
|
+
}, 30000);
|
|
260
|
+
req.on('close', () => {
|
|
261
|
+
clearInterval(interval);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
logger.error('Error handling MCP protocol notifications', { error });
|
|
266
|
+
res.status(500).end();
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
// Start server
|
|
270
|
+
app.listen(PORT, async () => {
|
|
271
|
+
logger.info(`Quickbase MCP Server v2 server running on port ${PORT}`);
|
|
272
|
+
// Initialize Quickbase client
|
|
273
|
+
initializeClient();
|
|
274
|
+
// Connect the MCP server to its transport
|
|
275
|
+
try {
|
|
276
|
+
await mcpServer.connect(mcpTransport);
|
|
277
|
+
logger.info('MCP server connected successfully');
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
logger.error('Failed to connect MCP server', { error });
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
// Graceful shutdown handling
|
|
284
|
+
process.on('SIGTERM', () => {
|
|
285
|
+
logger.info('SIGTERM received, shutting down gracefully');
|
|
286
|
+
cleanup();
|
|
287
|
+
});
|
|
288
|
+
process.on('SIGINT', () => {
|
|
289
|
+
logger.info('SIGINT received, shutting down gracefully');
|
|
290
|
+
cleanup();
|
|
291
|
+
});
|
|
292
|
+
process.on('uncaughtException', (error) => {
|
|
293
|
+
logger.error('Uncaught exception', { error });
|
|
294
|
+
cleanup();
|
|
295
|
+
process.exit(1);
|
|
296
|
+
});
|
|
297
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
298
|
+
logger.error('Unhandled rejection', { reason, promise });
|
|
299
|
+
cleanup();
|
|
300
|
+
process.exit(1);
|
|
301
|
+
});
|
|
302
|
+
function cleanup() {
|
|
303
|
+
try {
|
|
304
|
+
// Close cache connections
|
|
305
|
+
if (cacheService) {
|
|
306
|
+
logger.info('Closing cache service');
|
|
307
|
+
// Note: Cache service cleanup should be implemented if it has cleanup methods
|
|
308
|
+
}
|
|
309
|
+
// Close any other resources
|
|
310
|
+
logger.info('Cleanup completed');
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
logger.error('Error during cleanup', { error });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// Export for testing
|
|
317
|
+
exports.default = app;
|
|
318
|
+
//# sourceMappingURL=server.js.map
|