@rashidazarang/airtable-mcp 3.1.0 → 3.2.8

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.
Files changed (106) hide show
  1. package/README.md +209 -334
  2. package/bin/airtable-mcp.js +12 -32
  3. package/dist/typescript/airtable-mcp-server.js +77 -0
  4. package/dist/typescript/airtable-mcp-server.js.map +1 -0
  5. package/dist/typescript/app/airtable-client.js +327 -0
  6. package/dist/typescript/app/airtable-client.js.map +1 -0
  7. package/dist/typescript/app/config.js +151 -0
  8. package/dist/typescript/app/config.js.map +1 -0
  9. package/dist/typescript/app/context.js +3 -0
  10. package/dist/typescript/app/context.js.map +1 -0
  11. package/dist/typescript/app/exceptions.js +85 -0
  12. package/dist/typescript/app/exceptions.js.map +1 -0
  13. package/dist/typescript/app/governance.js +58 -0
  14. package/dist/typescript/app/governance.js.map +1 -0
  15. package/dist/typescript/app/logger.js +47 -0
  16. package/dist/typescript/app/logger.js.map +1 -0
  17. package/dist/typescript/app/rateLimiter.js +37 -0
  18. package/dist/typescript/app/rateLimiter.js.map +1 -0
  19. package/dist/typescript/app/sanitize.js +95 -0
  20. package/dist/typescript/app/sanitize.js.map +1 -0
  21. package/dist/typescript/app/tools/create.js +55 -0
  22. package/dist/typescript/app/tools/create.js.map +1 -0
  23. package/dist/typescript/app/tools/describe.js +190 -0
  24. package/dist/typescript/app/tools/describe.js.map +1 -0
  25. package/dist/typescript/app/tools/handleError.js +205 -0
  26. package/dist/typescript/app/tools/handleError.js.map +1 -0
  27. package/dist/typescript/app/tools/index.js +24 -0
  28. package/dist/typescript/app/tools/index.js.map +1 -0
  29. package/dist/typescript/app/tools/listBases.js +47 -0
  30. package/dist/typescript/app/tools/listBases.js.map +1 -0
  31. package/dist/typescript/app/tools/listExceptions.js +16 -0
  32. package/dist/typescript/app/tools/listExceptions.js.map +1 -0
  33. package/dist/typescript/app/tools/listGovernance.js +15 -0
  34. package/dist/typescript/app/tools/listGovernance.js.map +1 -0
  35. package/dist/typescript/app/tools/query.js +133 -0
  36. package/dist/typescript/app/tools/query.js.map +1 -0
  37. package/dist/typescript/app/tools/response.js +21 -0
  38. package/dist/typescript/app/tools/response.js.map +1 -0
  39. package/dist/typescript/app/tools/update.js +57 -0
  40. package/dist/typescript/app/tools/update.js.map +1 -0
  41. package/dist/typescript/app/tools/upsert.js +66 -0
  42. package/dist/typescript/app/tools/upsert.js.map +1 -0
  43. package/dist/typescript/app/tools/webhooks.js +45 -0
  44. package/dist/typescript/app/tools/webhooks.js.map +1 -0
  45. package/dist/typescript/app/types.js +291 -0
  46. package/dist/typescript/app/types.js.map +1 -0
  47. package/dist/typescript/app/validateApiKey.js +75 -0
  48. package/dist/typescript/app/validateApiKey.js.map +1 -0
  49. package/dist/typescript/errors.js +75 -0
  50. package/dist/typescript/errors.js.map +1 -0
  51. package/dist/typescript/index.js +27 -0
  52. package/dist/typescript/index.js.map +1 -0
  53. package/package.json +49 -31
  54. package/tsconfig.json +10 -4
  55. package/types/typescript/airtable-mcp-server.d.ts +2 -0
  56. package/types/typescript/app/airtable-client.d.ts +50 -0
  57. package/types/typescript/app/config.d.ts +17 -0
  58. package/types/typescript/app/context.d.ts +12 -0
  59. package/types/typescript/app/exceptions.d.ts +12 -0
  60. package/types/typescript/app/governance.d.ts +18 -0
  61. package/types/typescript/app/logger.d.ts +13 -0
  62. package/types/typescript/app/rateLimiter.d.ts +13 -0
  63. package/types/typescript/app/sanitize.d.ts +50 -0
  64. package/types/typescript/app/tools/create.d.ts +3 -0
  65. package/types/typescript/app/tools/describe.d.ts +3 -0
  66. package/types/typescript/app/tools/handleError.d.ts +8 -0
  67. package/types/typescript/app/tools/index.d.ts +3 -0
  68. package/types/typescript/app/tools/listBases.d.ts +13 -0
  69. package/types/typescript/app/tools/listExceptions.d.ts +3 -0
  70. package/types/typescript/app/tools/listGovernance.d.ts +3 -0
  71. package/types/typescript/app/tools/query.d.ts +3 -0
  72. package/types/typescript/app/tools/response.d.ts +20 -0
  73. package/types/typescript/app/tools/update.d.ts +3 -0
  74. package/types/typescript/app/tools/upsert.d.ts +3 -0
  75. package/types/typescript/app/tools/webhooks.d.ts +3 -0
  76. package/types/typescript/app/types.d.ts +318 -0
  77. package/types/typescript/app/validateApiKey.d.ts +25 -0
  78. package/types/typescript/errors.d.ts +57 -0
  79. package/types/typescript/index.d.ts +10 -0
  80. package/types/typescript/prompt-templates.d.ts +5 -0
  81. package/types/typescript/tools-schemas.d.ts +5 -0
  82. package/airtable_simple.js +0 -1561
  83. package/airtable_simple_production.js +0 -1564
  84. package/dist/airtable-mcp-server.js +0 -660
  85. package/dist/airtable-mcp-server.js.map +0 -1
  86. package/dist/test-suite.js +0 -421
  87. package/dist/test-suite.js.map +0 -1
  88. package/examples/airtable-crud-example.js +0 -203
  89. package/examples/building-mcp.md +0 -6666
  90. package/examples/claude_config.json +0 -4
  91. package/examples/claude_simple_config.json +0 -7
  92. package/examples/env-demo.js +0 -172
  93. package/examples/example-tasks-update.json +0 -23
  94. package/examples/example-tasks.json +0 -26
  95. package/examples/example_usage.md +0 -124
  96. package/examples/python_debug_patch.txt +0 -27
  97. package/examples/sample-transform.js +0 -76
  98. package/examples/typescript/advanced-ai-prompts.ts +0 -447
  99. package/examples/typescript/basic-usage.ts +0 -174
  100. package/examples/typescript/claude-desktop-config.json +0 -29
  101. package/examples/windsurf_mcp_config.json +0 -17
  102. package/types/ai-prompts.d.ts +0 -321
  103. package/types/airtable-mcp-server.d.ts +0 -52
  104. package/types/index.d.ts +0 -357
  105. package/types/tools.d.ts +0 -514
  106. /package/types/{test-suite.d.ts → typescript/test-suite.d.ts} +0 -0
@@ -1,1564 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Airtable MCP Server - Production Ready
5
- * Model Context Protocol server for Airtable integration
6
- *
7
- * Features:
8
- * - Complete MCP 2024-11-05 protocol support
9
- * - OAuth2 authentication with PKCE
10
- * - Enterprise security features
11
- * - Rate limiting and input validation
12
- * - Production monitoring and health checks
13
- *
14
- * Author: Rashid Azarang
15
- * License: MIT
16
- */
17
-
18
- const http = require('http');
19
- const https = require('https');
20
- const fs = require('fs');
21
- const path = require('path');
22
- const crypto = require('crypto');
23
- const url = require('url');
24
- const querystring = require('querystring');
25
-
26
- // Load environment variables
27
- const envPath = path.join(__dirname, '.env');
28
- if (fs.existsSync(envPath)) {
29
- require('dotenv').config({ path: envPath });
30
- }
31
-
32
- // Parse command line arguments
33
- const args = process.argv.slice(2);
34
- let tokenIndex = args.indexOf('--token');
35
- let baseIndex = args.indexOf('--base');
36
-
37
- const token = tokenIndex !== -1 ? args[tokenIndex + 1] : process.env.AIRTABLE_TOKEN || process.env.AIRTABLE_API_TOKEN;
38
- const baseId = baseIndex !== -1 ? args[baseIndex + 1] : process.env.AIRTABLE_BASE_ID || process.env.AIRTABLE_BASE;
39
-
40
- if (!token || !baseId) {
41
- console.error('Error: Missing Airtable credentials');
42
- console.error('\nUsage options:');
43
- console.error(' 1. Command line: node airtable_simple_production.js --token YOUR_TOKEN --base YOUR_BASE_ID');
44
- console.error(' 2. Environment variables: AIRTABLE_TOKEN and AIRTABLE_BASE_ID');
45
- console.error(' 3. .env file with AIRTABLE_TOKEN and AIRTABLE_BASE_ID');
46
- process.exit(1);
47
- }
48
-
49
- // Configuration
50
- const CONFIG = {
51
- PORT: process.env.PORT || 8010,
52
- HOST: process.env.HOST || 'localhost',
53
- MAX_REQUESTS_PER_MINUTE: parseInt(process.env.MAX_REQUESTS_PER_MINUTE) || 60,
54
- LOG_LEVEL: process.env.LOG_LEVEL || 'INFO'
55
- };
56
-
57
- // Logging
58
- const LOG_LEVELS = { ERROR: 0, WARN: 1, INFO: 2, DEBUG: 3, TRACE: 4 };
59
- let currentLogLevel = LOG_LEVELS[CONFIG.LOG_LEVEL] || LOG_LEVELS.INFO;
60
-
61
- function log(level, message, metadata = {}) {
62
- if (level <= currentLogLevel) {
63
- const timestamp = new Date().toISOString();
64
- const levelName = Object.keys(LOG_LEVELS).find(key => LOG_LEVELS[key] === level);
65
- // Sanitize message to prevent format string attacks
66
- const safeMessage = String(message).replace(/%/g, '%%');
67
- const output = `[${timestamp}] [${levelName}] ${safeMessage}`;
68
-
69
- if (Object.keys(metadata).length > 0) {
70
- // Use separate arguments to avoid format string injection
71
- console.log('%s %s', output, JSON.stringify(metadata));
72
- } else {
73
- console.log('%s', output);
74
- }
75
- }
76
- }
77
-
78
- // Rate limiting
79
- const rateLimiter = new Map();
80
-
81
- function checkRateLimit(clientId) {
82
- const now = Date.now();
83
- const windowStart = now - 60000; // 1 minute window
84
-
85
- if (!rateLimiter.has(clientId)) {
86
- rateLimiter.set(clientId, []);
87
- }
88
-
89
- const requests = rateLimiter.get(clientId);
90
- const recentRequests = requests.filter(time => time > windowStart);
91
-
92
- if (recentRequests.length >= CONFIG.MAX_REQUESTS_PER_MINUTE) {
93
- return false;
94
- }
95
-
96
- recentRequests.push(now);
97
- rateLimiter.set(clientId, recentRequests);
98
- return true;
99
- }
100
-
101
- // Input validation and HTML escaping
102
- function sanitizeInput(input) {
103
- if (typeof input === 'string') {
104
- return input.replace(/[<>]/g, '').trim().substring(0, 1000);
105
- }
106
- return input;
107
- }
108
-
109
- function escapeHtml(unsafe) {
110
- if (typeof unsafe !== 'string') {
111
- return String(unsafe);
112
- }
113
- return unsafe
114
- .replace(/&/g, "&amp;")
115
- .replace(/</g, "&lt;")
116
- .replace(/>/g, "&gt;")
117
- .replace(/"/g, "&quot;")
118
- .replace(/'/g, "&#039;")
119
- .replace(/\//g, "&#x2F;");
120
- }
121
-
122
- function validateUrl(url) {
123
- try {
124
- const parsed = new URL(url);
125
- // Only allow http and https protocols
126
- return ['http:', 'https:'].includes(parsed.protocol);
127
- } catch {
128
- return false;
129
- }
130
- }
131
-
132
- // Airtable API integration
133
- function callAirtableAPI(endpoint, method = 'GET', body = null, queryParams = {}) {
134
- return new Promise((resolve, reject) => {
135
- const isBaseEndpoint = !endpoint.startsWith('meta/');
136
- const baseUrl = isBaseEndpoint ? `${baseId}/${endpoint}` : endpoint;
137
-
138
- const queryString = Object.keys(queryParams).length > 0
139
- ? '?' + new URLSearchParams(queryParams).toString()
140
- : '';
141
-
142
- const apiUrl = `https://api.airtable.com/v0/${baseUrl}${queryString}`;
143
- const urlObj = new URL(apiUrl);
144
-
145
- log(LOG_LEVELS.DEBUG, 'API Request', { method, url: apiUrl });
146
-
147
- const options = {
148
- hostname: urlObj.hostname,
149
- path: urlObj.pathname + urlObj.search,
150
- method: method,
151
- headers: {
152
- 'Authorization': `Bearer ${token}`,
153
- 'Content-Type': 'application/json',
154
- 'User-Agent': 'Airtable-MCP-Server/2.1.0'
155
- }
156
- };
157
-
158
- const req = https.request(options, (response) => {
159
- let data = '';
160
-
161
- response.on('data', (chunk) => data += chunk);
162
- response.on('end', () => {
163
- try {
164
- const parsed = data ? JSON.parse(data) : {};
165
-
166
- if (response.statusCode >= 200 && response.statusCode < 300) {
167
- resolve(parsed);
168
- } else {
169
- const error = parsed.error || {};
170
- reject(new Error(`Airtable API error (${response.statusCode}): ${error.message || error.type || 'Unknown error'}`));
171
- }
172
- } catch (e) {
173
- reject(new Error(`Failed to parse Airtable response: ${e.message}`));
174
- }
175
- });
176
- });
177
-
178
- req.on('error', reject);
179
-
180
- if (body) {
181
- req.write(JSON.stringify(body));
182
- }
183
-
184
- req.end();
185
- });
186
- }
187
-
188
- // Tools schema
189
- const TOOLS_SCHEMA = [
190
- {
191
- name: 'list_tables',
192
- description: 'List all tables in the Airtable base',
193
- inputSchema: {
194
- type: 'object',
195
- properties: {
196
- include_schema: { type: 'boolean', description: 'Include field schema information', default: false }
197
- }
198
- }
199
- },
200
- {
201
- name: 'list_records',
202
- description: 'List records from a specific table',
203
- inputSchema: {
204
- type: 'object',
205
- properties: {
206
- table: { type: 'string', description: 'Table name or ID' },
207
- maxRecords: { type: 'number', description: 'Maximum number of records to return' },
208
- view: { type: 'string', description: 'View name or ID' },
209
- filterByFormula: { type: 'string', description: 'Airtable formula to filter records' }
210
- },
211
- required: ['table']
212
- }
213
- },
214
- {
215
- name: 'get_record',
216
- description: 'Get a single record by ID',
217
- inputSchema: {
218
- type: 'object',
219
- properties: {
220
- table: { type: 'string', description: 'Table name or ID' },
221
- recordId: { type: 'string', description: 'Record ID' }
222
- },
223
- required: ['table', 'recordId']
224
- }
225
- },
226
- {
227
- name: 'create_record',
228
- description: 'Create a new record in a table',
229
- inputSchema: {
230
- type: 'object',
231
- properties: {
232
- table: { type: 'string', description: 'Table name or ID' },
233
- fields: { type: 'object', description: 'Field values for the new record' }
234
- },
235
- required: ['table', 'fields']
236
- }
237
- },
238
- {
239
- name: 'update_record',
240
- description: 'Update an existing record',
241
- inputSchema: {
242
- type: 'object',
243
- properties: {
244
- table: { type: 'string', description: 'Table name or ID' },
245
- recordId: { type: 'string', description: 'Record ID to update' },
246
- fields: { type: 'object', description: 'Fields to update' }
247
- },
248
- required: ['table', 'recordId', 'fields']
249
- }
250
- },
251
- {
252
- name: 'delete_record',
253
- description: 'Delete a record from a table',
254
- inputSchema: {
255
- type: 'object',
256
- properties: {
257
- table: { type: 'string', description: 'Table name or ID' },
258
- recordId: { type: 'string', description: 'Record ID to delete' }
259
- },
260
- required: ['table', 'recordId']
261
- }
262
- }
263
- ];
264
-
265
- // Enhanced AI-powered prompts for intelligent Airtable operations
266
- const PROMPTS_SCHEMA = [
267
- {
268
- name: 'analyze_data',
269
- description: 'Advanced AI data analysis with statistical insights, pattern recognition, and predictive modeling',
270
- arguments: [
271
- {
272
- name: 'table',
273
- description: 'Table name or ID to analyze',
274
- required: true
275
- },
276
- {
277
- name: 'analysis_type',
278
- description: 'Type of analysis (trends, statistical, patterns, predictive, anomaly_detection, correlation_matrix)',
279
- required: false
280
- },
281
- {
282
- name: 'field_focus',
283
- description: 'Specific fields to focus the analysis on',
284
- required: false
285
- },
286
- {
287
- name: 'time_dimension',
288
- description: 'Time field for temporal analysis',
289
- required: false
290
- },
291
- {
292
- name: 'confidence_level',
293
- description: 'Statistical confidence level (0.90, 0.95, 0.99)',
294
- required: false
295
- }
296
- ]
297
- },
298
- {
299
- name: 'create_report',
300
- description: 'Generate intelligent reports with AI-powered insights, visualizations, and actionable recommendations',
301
- arguments: [
302
- {
303
- name: 'table',
304
- description: 'Table name or ID for the report',
305
- required: true
306
- },
307
- {
308
- name: 'report_type',
309
- description: 'Type of report (executive_summary, operational_dashboard, analytical_deep_dive, performance_metrics, predictive_forecast)',
310
- required: false
311
- },
312
- {
313
- name: 'time_period',
314
- description: 'Time period for analysis (last_7_days, last_30_days, last_quarter, year_to_date, custom)',
315
- required: false
316
- },
317
- {
318
- name: 'stakeholder_level',
319
- description: 'Target audience (executive, manager, analyst, operational)',
320
- required: false
321
- },
322
- {
323
- name: 'include_recommendations',
324
- description: 'Include AI-generated actionable recommendations (true/false)',
325
- required: false
326
- }
327
- ]
328
- },
329
- {
330
- name: 'data_insights',
331
- description: 'Discover hidden patterns, correlations, and business insights using advanced AI algorithms',
332
- arguments: [
333
- {
334
- name: 'tables',
335
- description: 'Comma-separated list of table names to analyze',
336
- required: true
337
- },
338
- {
339
- name: 'insight_type',
340
- description: 'Type of insights (correlations, outliers, trends, predictions, segmentation, attribution, churn_analysis)',
341
- required: false
342
- },
343
- {
344
- name: 'business_context',
345
- description: 'Business domain context (sales, marketing, operations, finance, customer_success)',
346
- required: false
347
- },
348
- {
349
- name: 'insight_depth',
350
- description: 'Analysis depth (surface, moderate, deep, comprehensive)',
351
- required: false
352
- }
353
- ]
354
- },
355
- {
356
- name: 'optimize_workflow',
357
- description: 'AI-powered workflow optimization with automation recommendations and efficiency improvements',
358
- arguments: [
359
- {
360
- name: 'base_overview',
361
- description: 'Overview of the base structure and current workflows',
362
- required: false
363
- },
364
- {
365
- name: 'optimization_focus',
366
- description: 'Focus area (automation, data_quality, collaboration, performance, integration, user_experience)',
367
- required: false
368
- },
369
- {
370
- name: 'current_pain_points',
371
- description: 'Known issues or bottlenecks in current workflow',
372
- required: false
373
- },
374
- {
375
- name: 'team_size',
376
- description: 'Number of users working with this base',
377
- required: false
378
- }
379
- ]
380
- },
381
- {
382
- name: 'smart_schema_design',
383
- description: 'AI-assisted database schema optimization and field relationship analysis',
384
- arguments: [
385
- {
386
- name: 'use_case',
387
- description: 'Primary use case (crm, project_management, inventory, content_management, hr, finance)',
388
- required: true
389
- },
390
- {
391
- name: 'data_volume',
392
- description: 'Expected data volume (small, medium, large, enterprise)',
393
- required: false
394
- },
395
- {
396
- name: 'integration_needs',
397
- description: 'External systems to integrate with',
398
- required: false
399
- },
400
- {
401
- name: 'compliance_requirements',
402
- description: 'Data compliance needs (gdpr, hipaa, sox, none)',
403
- required: false
404
- }
405
- ]
406
- },
407
- {
408
- name: 'data_quality_audit',
409
- description: 'Comprehensive AI-powered data quality assessment with cleansing recommendations',
410
- arguments: [
411
- {
412
- name: 'tables',
413
- description: 'Tables to audit (comma-separated or "all")',
414
- required: true
415
- },
416
- {
417
- name: 'quality_dimensions',
418
- description: 'Quality aspects to check (completeness, accuracy, consistency, validity, uniqueness, timeliness)',
419
- required: false
420
- },
421
- {
422
- name: 'severity_threshold',
423
- description: 'Minimum severity level to report (low, medium, high, critical)',
424
- required: false
425
- },
426
- {
427
- name: 'auto_fix_suggestions',
428
- description: 'Include automated fix suggestions (true/false)',
429
- required: false
430
- }
431
- ]
432
- },
433
- {
434
- name: 'predictive_analytics',
435
- description: 'Advanced predictive modeling and forecasting using historical Airtable data',
436
- arguments: [
437
- {
438
- name: 'table',
439
- description: 'Table containing historical data',
440
- required: true
441
- },
442
- {
443
- name: 'target_field',
444
- description: 'Field to predict or forecast',
445
- required: true
446
- },
447
- {
448
- name: 'prediction_horizon',
449
- description: 'Forecast period (next_week, next_month, next_quarter, next_year)',
450
- required: false
451
- },
452
- {
453
- name: 'model_type',
454
- description: 'Prediction model (trend_analysis, seasonal_forecast, regression, classification, time_series)',
455
- required: false
456
- },
457
- {
458
- name: 'feature_fields',
459
- description: 'Fields to use as predictive features',
460
- required: false
461
- }
462
- ]
463
- },
464
- {
465
- name: 'natural_language_query',
466
- description: 'Process natural language questions about your data and provide intelligent answers',
467
- arguments: [
468
- {
469
- name: 'question',
470
- description: 'Natural language question about your data',
471
- required: true
472
- },
473
- {
474
- name: 'context_tables',
475
- description: 'Tables that might contain relevant data',
476
- required: false
477
- },
478
- {
479
- name: 'response_format',
480
- description: 'Desired response format (narrative, data_summary, visualization_suggestion, action_items)',
481
- required: false
482
- },
483
- {
484
- name: 'include_confidence',
485
- description: 'Include confidence scores for answers (true/false)',
486
- required: false
487
- }
488
- ]
489
- },
490
- {
491
- name: 'smart_data_transformation',
492
- description: 'AI-assisted data transformation, cleaning, and enrichment with intelligent suggestions',
493
- arguments: [
494
- {
495
- name: 'source_table',
496
- description: 'Source table for transformation',
497
- required: true
498
- },
499
- {
500
- name: 'transformation_goal',
501
- description: 'Goal (normalize, standardize, enrich, cleanse, aggregate, pivot)',
502
- required: true
503
- },
504
- {
505
- name: 'target_format',
506
- description: 'Desired output format or structure',
507
- required: false
508
- },
509
- {
510
- name: 'quality_rules',
511
- description: 'Data quality rules to apply during transformation',
512
- required: false
513
- },
514
- {
515
- name: 'preserve_history',
516
- description: 'Maintain audit trail of changes (true/false)',
517
- required: false
518
- }
519
- ]
520
- },
521
- {
522
- name: 'automation_recommendations',
523
- description: 'Generate intelligent automation suggestions based on workflow patterns and data analysis',
524
- arguments: [
525
- {
526
- name: 'workflow_description',
527
- description: 'Description of current manual processes',
528
- required: false
529
- },
530
- {
531
- name: 'automation_scope',
532
- description: 'Scope (single_table, multi_table, cross_base, external_integration)',
533
- required: false
534
- },
535
- {
536
- name: 'frequency_patterns',
537
- description: 'How often tasks are performed',
538
- required: false
539
- },
540
- {
541
- name: 'complexity_tolerance',
542
- description: 'Acceptable automation complexity (simple, moderate, advanced)',
543
- required: false
544
- },
545
- {
546
- name: 'integration_capabilities',
547
- description: 'Available integration tools (zapier, make, custom_api, native_automations)',
548
- required: false
549
- }
550
- ]
551
- }
552
- ];
553
-
554
- // Roots configuration for filesystem access
555
- const ROOTS_CONFIG = [
556
- {
557
- uri: 'file:///airtable-exports',
558
- name: 'Airtable Exports'
559
- },
560
- {
561
- uri: 'file:///airtable-attachments',
562
- name: 'Airtable Attachments'
563
- }
564
- ];
565
-
566
- // Logging configuration (currentLogLevel is already declared above)
567
-
568
- // HTTP server
569
- const server = http.createServer(async (req, res) => {
570
- // Security headers
571
- res.setHeader('X-Content-Type-Options', 'nosniff');
572
- res.setHeader('X-Frame-Options', 'DENY');
573
- res.setHeader('X-XSS-Protection', '1; mode=block');
574
- res.setHeader('Access-Control-Allow-Origin', process.env.ALLOWED_ORIGINS || '*');
575
- res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
576
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
577
-
578
- // Handle preflight request
579
- if (req.method === 'OPTIONS') {
580
- res.writeHead(200);
581
- res.end();
582
- return;
583
- }
584
-
585
- const parsedUrl = url.parse(req.url, true);
586
- const pathname = parsedUrl.pathname;
587
-
588
- // Health check endpoint
589
- if (pathname === '/health' && req.method === 'GET') {
590
- res.writeHead(200, { 'Content-Type': 'application/json' });
591
- res.end(JSON.stringify({
592
- status: 'healthy',
593
- version: '3.0.0',
594
- timestamp: new Date().toISOString(),
595
- uptime: process.uptime()
596
- }));
597
- return;
598
- }
599
-
600
- // OAuth2 authorization endpoint
601
- if (pathname === '/oauth/authorize' && req.method === 'GET') {
602
- const params = parsedUrl.query;
603
- const clientId = params.client_id;
604
- const redirectUri = params.redirect_uri;
605
- const state = params.state;
606
- const codeChallenge = params.code_challenge;
607
- const codeChallengeMethod = params.code_challenge_method;
608
-
609
- // Validate inputs to prevent XSS
610
- if (!clientId || !redirectUri) {
611
- res.writeHead(400, { 'Content-Type': 'application/json' });
612
- res.end(JSON.stringify({ error: 'invalid_request', error_description: 'Missing required parameters' }));
613
- return;
614
- }
615
-
616
- // Validate redirect URI
617
- if (!validateUrl(redirectUri)) {
618
- res.writeHead(400, { 'Content-Type': 'application/json' });
619
- res.end(JSON.stringify({ error: 'invalid_request', error_description: 'Invalid redirect URI' }));
620
- return;
621
- }
622
-
623
- // Create safe copies of all variables for JavaScript use
624
- const safeRedirectUri = redirectUri.slice(0, 2000); // Limit length
625
- const safeState = (state || '').slice(0, 200); // Limit length
626
- const safeClientId = clientId.slice(0, 200); // Limit length
627
-
628
- // Sanitize for HTML display only
629
- const displayClientId = escapeHtml(safeClientId);
630
- const displayRedirectUri = escapeHtml(safeRedirectUri);
631
-
632
- // Generate authorization code
633
- const authCode = crypto.randomBytes(32).toString('hex');
634
-
635
- // In a real implementation, store the auth code with expiration
636
- // and associate it with the client and PKCE challenge
637
-
638
- res.writeHead(200, {
639
- 'Content-Type': 'text/html',
640
- 'Content-Security-Policy': "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; connect-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none';",
641
- 'X-Content-Type-Options': 'nosniff',
642
- 'X-Frame-Options': 'DENY',
643
- 'X-XSS-Protection': '1; mode=block',
644
- 'Referrer-Policy': 'no-referrer'
645
- });
646
-
647
- // Build HTML with proper escaping and separation of concerns
648
- const htmlContent = `<!DOCTYPE html>
649
- <html>
650
- <head>
651
- <meta charset="UTF-8">
652
- <title>OAuth2 Authorization</title>
653
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
654
- </head>
655
- <body>
656
- <h2>Airtable MCP Server - OAuth2 Authorization</h2>
657
- <p>Client ID: ${displayClientId}</p>
658
- <p>Redirect URI: ${displayRedirectUri}</p>
659
- <div style="margin: 20px 0;">
660
- <button onclick="authorize()" style="background: #18BFFF; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer;">
661
- Authorize Application
662
- </button>
663
- <button onclick="deny()" style="background: #ff4444; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; margin-left: 10px;">
664
- Deny Access
665
- </button>
666
- </div>
667
- <script>
668
- // All variables are safely JSON encoded to prevent XSS
669
- (function() {
670
- var config = ${JSON.stringify({
671
- redirectUri: safeRedirectUri,
672
- code: authCode,
673
- state: safeState
674
- })};
675
-
676
- window.authorize = function() {
677
- try {
678
- var url = new URL(config.redirectUri);
679
- if (url.protocol !== 'http:' && url.protocol !== 'https:') {
680
- throw new Error('Invalid protocol');
681
- }
682
- var finalUrl = config.redirectUri + '?code=' + encodeURIComponent(config.code) + '&state=' + encodeURIComponent(config.state);
683
- window.location.href = finalUrl;
684
- } catch (e) {
685
- console.error('Authorization failed:', e);
686
- alert('Invalid redirect URL');
687
- }
688
- };
689
-
690
- window.deny = function() {
691
- try {
692
- var url = new URL(config.redirectUri);
693
- if (url.protocol !== 'http:' && url.protocol !== 'https:') {
694
- throw new Error('Invalid protocol');
695
- }
696
- var finalUrl = config.redirectUri + '?error=access_denied&state=' + encodeURIComponent(config.state);
697
- window.location.href = finalUrl;
698
- } catch (e) {
699
- console.error('Denial failed:', e);
700
- alert('Invalid redirect URL');
701
- }
702
- };
703
- })();
704
- </script>
705
- </body>
706
- </html>`;
707
-
708
- res.end(htmlContent);
709
- return;
710
- }
711
-
712
- // OAuth2 token endpoint
713
- if (pathname === '/oauth/token' && req.method === 'POST') {
714
- let body = '';
715
- req.on('data', chunk => {
716
- body += chunk.toString();
717
- // Prevent DoS by limiting body size
718
- if (body.length > 10000) {
719
- res.writeHead(413, { 'Content-Type': 'application/json' });
720
- res.end(JSON.stringify({ error: 'payload_too_large', error_description: 'Request body too large' }));
721
- return;
722
- }
723
- });
724
-
725
- req.on('end', () => {
726
- try {
727
- const params = querystring.parse(body);
728
- const grantType = sanitizeInput(params.grant_type);
729
- const code = sanitizeInput(params.code);
730
- const codeVerifier = sanitizeInput(params.code_verifier);
731
- const clientId = sanitizeInput(params.client_id);
732
-
733
- // Validate required parameters
734
- if (!grantType || !code || !clientId) {
735
- res.writeHead(400, { 'Content-Type': 'application/json' });
736
- res.end(JSON.stringify({
737
- error: 'invalid_request',
738
- error_description: 'Missing required parameters'
739
- }));
740
- return;
741
- }
742
-
743
- // In a real implementation, verify the authorization code and PKCE
744
- if (grantType === 'authorization_code' && code) {
745
- // Generate access token
746
- const accessToken = crypto.randomBytes(32).toString('hex');
747
- const refreshToken = crypto.randomBytes(32).toString('hex');
748
-
749
- res.writeHead(200, {
750
- 'Content-Type': 'application/json',
751
- 'Cache-Control': 'no-store',
752
- 'Pragma': 'no-cache'
753
- });
754
- res.end(JSON.stringify({
755
- access_token: accessToken,
756
- token_type: 'Bearer',
757
- expires_in: 3600,
758
- refresh_token: refreshToken,
759
- scope: 'data.records:read data.records:write schema.bases:read'
760
- }));
761
- } else {
762
- res.writeHead(400, { 'Content-Type': 'application/json' });
763
- res.end(JSON.stringify({
764
- error: 'invalid_grant',
765
- error_description: 'Invalid grant type or authorization code'
766
- }));
767
- }
768
- } catch (error) {
769
- log(LOG_LEVELS.WARN, 'OAuth token request parsing failed', { error: error.message });
770
- res.writeHead(400, { 'Content-Type': 'application/json' });
771
- res.end(JSON.stringify({
772
- error: 'invalid_request',
773
- error_description: 'Malformed request body'
774
- }));
775
- }
776
- });
777
- return;
778
- }
779
-
780
- // MCP endpoint
781
- if (pathname === '/mcp' && req.method === 'POST') {
782
- // Rate limiting
783
- const clientId = req.headers['x-client-id'] || req.connection.remoteAddress;
784
- if (!checkRateLimit(clientId)) {
785
- res.writeHead(429, { 'Content-Type': 'application/json' });
786
- res.end(JSON.stringify({
787
- jsonrpc: '2.0',
788
- error: {
789
- code: -32000,
790
- message: 'Rate limit exceeded. Maximum 60 requests per minute.'
791
- }
792
- }));
793
- return;
794
- }
795
-
796
- let body = '';
797
- req.on('data', chunk => body += chunk.toString());
798
-
799
- req.on('end', async () => {
800
- try {
801
- const request = JSON.parse(body);
802
-
803
- // Sanitize inputs
804
- if (request.params) {
805
- Object.keys(request.params).forEach(key => {
806
- request.params[key] = sanitizeInput(request.params[key]);
807
- });
808
- }
809
-
810
- log(LOG_LEVELS.DEBUG, 'MCP request received', {
811
- method: request.method,
812
- id: request.id
813
- });
814
-
815
- let response;
816
-
817
- switch (request.method) {
818
- case 'initialize':
819
- response = {
820
- jsonrpc: '2.0',
821
- id: request.id,
822
- result: {
823
- protocolVersion: '2024-11-05',
824
- capabilities: {
825
- tools: { listChanged: true },
826
- resources: { subscribe: true, listChanged: true },
827
- prompts: { listChanged: true },
828
- sampling: {},
829
- roots: { listChanged: true },
830
- logging: {}
831
- },
832
- serverInfo: {
833
- name: 'Airtable MCP Server - AI Agent Enhanced',
834
- version: '3.0.0',
835
- description: 'Advanced AI-powered MCP server with 10 intelligent prompt templates, predictive analytics, and enterprise automation capabilities'
836
- }
837
- }
838
- };
839
- log(LOG_LEVELS.INFO, 'Client initialized', { clientId: request.id });
840
- break;
841
-
842
- case 'tools/list':
843
- response = {
844
- jsonrpc: '2.0',
845
- id: request.id,
846
- result: {
847
- tools: TOOLS_SCHEMA
848
- }
849
- };
850
- break;
851
-
852
- case 'tools/call':
853
- response = await handleToolCall(request);
854
- break;
855
-
856
- case 'prompts/list':
857
- response = {
858
- jsonrpc: '2.0',
859
- id: request.id,
860
- result: {
861
- prompts: PROMPTS_SCHEMA
862
- }
863
- };
864
- break;
865
-
866
- case 'prompts/get':
867
- response = await handlePromptGet(request);
868
- break;
869
-
870
- case 'roots/list':
871
- response = {
872
- jsonrpc: '2.0',
873
- id: request.id,
874
- result: {
875
- roots: ROOTS_CONFIG
876
- }
877
- };
878
- break;
879
-
880
- case 'logging/setLevel':
881
- const level = request.params?.level;
882
- if (level && LOG_LEVELS[level.toUpperCase()] !== undefined) {
883
- currentLogLevel = LOG_LEVELS[level.toUpperCase()];
884
- log(LOG_LEVELS.INFO, 'Log level updated', { newLevel: level });
885
- }
886
- response = {
887
- jsonrpc: '2.0',
888
- id: request.id,
889
- result: {}
890
- };
891
- break;
892
-
893
- case 'sampling/createMessage':
894
- response = await handleSampling(request);
895
- break;
896
-
897
- default:
898
- log(LOG_LEVELS.WARN, 'Unknown method', { method: request.method });
899
- throw new Error(`Method "${request.method}" not found`);
900
- }
901
-
902
- res.writeHead(200, { 'Content-Type': 'application/json' });
903
- res.end(JSON.stringify(response));
904
-
905
- } catch (error) {
906
- log(LOG_LEVELS.ERROR, 'Request processing failed', { error: error.message });
907
-
908
- const errorResponse = {
909
- jsonrpc: '2.0',
910
- id: request?.id || null,
911
- error: {
912
- code: -32000,
913
- message: error.message || 'Internal server error'
914
- }
915
- };
916
-
917
- res.writeHead(200, { 'Content-Type': 'application/json' });
918
- res.end(JSON.stringify(errorResponse));
919
- }
920
- });
921
- return;
922
- }
923
-
924
- // Default 404
925
- res.writeHead(404, { 'Content-Type': 'application/json' });
926
- res.end(JSON.stringify({ error: 'Not Found' }));
927
- });
928
-
929
- // Tool handlers
930
- async function handleToolCall(request) {
931
- const toolName = request.params.name;
932
- const toolParams = request.params.arguments || {};
933
-
934
- try {
935
- let result;
936
- let responseText;
937
-
938
- switch (toolName) {
939
- case 'list_tables':
940
- const includeSchema = toolParams.include_schema || false;
941
- result = await callAirtableAPI(`meta/bases/${baseId}/tables`);
942
- const tables = result.tables || [];
943
-
944
- responseText = tables.length > 0
945
- ? `Found ${tables.length} table(s): ` +
946
- tables.map((table, i) =>
947
- `${table.name} (ID: ${table.id}, Fields: ${table.fields?.length || 0})`
948
- ).join(', ')
949
- : 'No tables found in this base.';
950
- break;
951
-
952
- case 'list_records':
953
- const { table, maxRecords, view, filterByFormula } = toolParams;
954
-
955
- const queryParams = {};
956
- if (maxRecords) queryParams.maxRecords = maxRecords;
957
- if (view) queryParams.view = view;
958
- if (filterByFormula) queryParams.filterByFormula = filterByFormula;
959
-
960
- result = await callAirtableAPI(table, 'GET', null, queryParams);
961
- const records = result.records || [];
962
-
963
- responseText = records.length > 0
964
- ? `Found ${records.length} record(s) in table "${table}"`
965
- : `No records found in table "${table}".`;
966
- break;
967
-
968
- case 'get_record':
969
- const { table: getTable, recordId } = toolParams;
970
- result = await callAirtableAPI(`${getTable}/${recordId}`);
971
- responseText = `Retrieved record ${recordId} from table "${getTable}"`;
972
- break;
973
-
974
- case 'create_record':
975
- const { table: createTable, fields } = toolParams;
976
- const body = { fields: fields };
977
- result = await callAirtableAPI(createTable, 'POST', body);
978
- responseText = `Successfully created record in table "${createTable}" with ID: ${result.id}`;
979
- break;
980
-
981
- case 'update_record':
982
- const { table: updateTable, recordId: updateRecordId, fields: updateFields } = toolParams;
983
- const updateBody = { fields: updateFields };
984
- result = await callAirtableAPI(`${updateTable}/${updateRecordId}`, 'PATCH', updateBody);
985
- responseText = `Successfully updated record ${updateRecordId} in table "${updateTable}"`;
986
- break;
987
-
988
- case 'delete_record':
989
- const { table: deleteTable, recordId: deleteRecordId } = toolParams;
990
- result = await callAirtableAPI(`${deleteTable}/${deleteRecordId}`, 'DELETE');
991
- responseText = `Successfully deleted record ${deleteRecordId} from table "${deleteTable}"`;
992
- break;
993
-
994
- default:
995
- throw new Error(`Unknown tool: ${toolName}`);
996
- }
997
-
998
- return {
999
- jsonrpc: '2.0',
1000
- id: request.id,
1001
- result: {
1002
- content: [
1003
- {
1004
- type: 'text',
1005
- text: responseText
1006
- }
1007
- ]
1008
- }
1009
- };
1010
-
1011
- } catch (error) {
1012
- log(LOG_LEVELS.ERROR, `Tool ${toolName} failed`, { error: error.message });
1013
-
1014
- return {
1015
- jsonrpc: '2.0',
1016
- id: request.id,
1017
- result: {
1018
- content: [
1019
- {
1020
- type: 'text',
1021
- text: `Error executing ${toolName}: ${error.message}`
1022
- }
1023
- ]
1024
- }
1025
- };
1026
- }
1027
- }
1028
-
1029
- // Enhanced AI-powered prompt handlers
1030
- async function handlePromptGet(request) {
1031
- const promptName = request.params.name;
1032
- const promptArgs = request.params.arguments || {};
1033
-
1034
- try {
1035
- const prompt = PROMPTS_SCHEMA.find(p => p.name === promptName);
1036
- if (!prompt) {
1037
- throw new Error(`Prompt "${promptName}" not found`);
1038
- }
1039
-
1040
- let messages = [];
1041
-
1042
- switch (promptName) {
1043
- case 'analyze_data':
1044
- const { table, analysis_type = 'statistical', field_focus, time_dimension, confidence_level = '0.95' } = promptArgs;
1045
- messages = [
1046
- {
1047
- role: 'user',
1048
- content: {
1049
- type: 'text',
1050
- text: `🤖 ADVANCED DATA ANALYSIS REQUEST
1051
-
1052
- **Table**: ${table}
1053
- **Analysis Type**: ${analysis_type}
1054
- **Confidence Level**: ${confidence_level}
1055
- ${field_focus ? `**Focus Fields**: ${field_focus}` : ''}
1056
- ${time_dimension ? `**Time Dimension**: ${time_dimension}` : ''}
1057
-
1058
- **Instructions**:
1059
- 1. First, examine the table schema and structure using list_tables with include_schema=true
1060
- 2. Retrieve representative sample data using list_records with appropriate filters
1061
- 3. Perform ${analysis_type} analysis with statistical rigor
1062
- 4. Generate insights with confidence intervals and significance testing
1063
- 5. Provide actionable recommendations based on findings
1064
-
1065
- **Expected Deliverables**:
1066
- - Statistical summary with key metrics
1067
- - Pattern identification and trend analysis
1068
- - Anomaly detection if applicable
1069
- - Predictive insights where relevant
1070
- - Visualization recommendations
1071
- - Business impact assessment
1072
-
1073
- Please use the available Airtable tools to gather data and provide comprehensive ${analysis_type} analysis.`
1074
- }
1075
- }
1076
- ];
1077
- break;
1078
-
1079
- case 'create_report':
1080
- const { table: reportTable, report_type = 'executive_summary', time_period = 'last_30_days', stakeholder_level = 'manager', include_recommendations = 'true' } = promptArgs;
1081
- messages = [
1082
- {
1083
- role: 'user',
1084
- content: {
1085
- type: 'text',
1086
- text: `📊 INTELLIGENT REPORT GENERATION
1087
-
1088
- **Target Table**: ${reportTable}
1089
- **Report Type**: ${report_type}
1090
- **Time Period**: ${time_period}
1091
- **Stakeholder Level**: ${stakeholder_level}
1092
- **Include Recommendations**: ${include_recommendations}
1093
-
1094
- **Report Generation Process**:
1095
- 1. Analyze table structure and data types
1096
- 2. Extract relevant data for specified time period
1097
- 3. Calculate key performance indicators
1098
- 4. Identify trends and patterns
1099
- 5. Generate visualizations suggestions
1100
- 6. Create ${stakeholder_level}-appropriate narrative
1101
-
1102
- **Report Sections**:
1103
- - Executive Summary (key findings)
1104
- - Data Overview and Quality Assessment
1105
- - Trend Analysis and Patterns
1106
- - Performance Metrics and KPIs
1107
- - Risk Assessment and Opportunities
1108
- ${include_recommendations === 'true' ? '- AI-Generated Recommendations' : ''}
1109
- - Next Steps and Action Items
1110
-
1111
- Please gather the necessary data and create a comprehensive ${report_type} tailored for ${stakeholder_level} level stakeholders.`
1112
- }
1113
- }
1114
- ];
1115
- break;
1116
-
1117
- case 'data_insights':
1118
- const { tables, insight_type = 'correlations', business_context = 'general', insight_depth = 'moderate' } = promptArgs;
1119
- messages = [
1120
- {
1121
- role: 'user',
1122
- content: {
1123
- type: 'text',
1124
- text: `🔍 ADVANCED DATA INSIGHTS DISCOVERY
1125
-
1126
- **Target Tables**: ${tables}
1127
- **Insight Type**: ${insight_type}
1128
- **Business Context**: ${business_context}
1129
- **Analysis Depth**: ${insight_depth}
1130
-
1131
- **Discovery Framework**:
1132
- 1. Multi-table schema analysis and relationship mapping
1133
- 2. Cross-table data correlation analysis
1134
- 3. Pattern recognition using ${business_context} domain knowledge
1135
- 4. Statistical significance testing
1136
- 5. Business impact quantification
1137
-
1138
- **Insight Categories**:
1139
- - ${insight_type} analysis with statistical validation
1140
- - Hidden patterns and unexpected relationships
1141
- - Segmentation opportunities
1142
- - Predictive indicators
1143
- - Data quality insights
1144
- - Business optimization opportunities
1145
-
1146
- **${business_context.toUpperCase()} CONTEXT ANALYSIS**:
1147
- ${business_context === 'sales' ? '- Revenue drivers and conversion patterns\n- Customer lifetime value indicators\n- Sales cycle optimization opportunities' : ''}
1148
- ${business_context === 'marketing' ? '- Campaign effectiveness and attribution\n- Customer segmentation insights\n- Channel performance analysis' : ''}
1149
- ${business_context === 'operations' ? '- Process efficiency metrics\n- Resource utilization patterns\n- Bottleneck identification' : ''}
1150
-
1151
- Please conduct ${insight_depth} analysis across the specified tables and provide actionable business insights.`
1152
- }
1153
- }
1154
- ];
1155
- break;
1156
-
1157
- case 'optimize_workflow':
1158
- const { base_overview, optimization_focus = 'automation', current_pain_points, team_size } = promptArgs;
1159
- messages = [
1160
- {
1161
- role: 'user',
1162
- content: {
1163
- type: 'text',
1164
- text: `⚡ AI-POWERED WORKFLOW OPTIMIZATION
1165
-
1166
- **Optimization Focus**: ${optimization_focus}
1167
- **Team Size**: ${team_size || 'Not specified'}
1168
- ${base_overview ? `**Base Overview**: ${base_overview}` : ''}
1169
- ${current_pain_points ? `**Current Pain Points**: ${current_pain_points}` : ''}
1170
-
1171
- **Optimization Analysis**:
1172
- 1. Workflow pattern analysis and bottleneck identification
1173
- 2. Automation opportunity assessment
1174
- 3. User experience and efficiency evaluation
1175
- 4. Integration and scaling considerations
1176
- 5. ROI analysis for proposed improvements
1177
-
1178
- **${optimization_focus.toUpperCase()} OPTIMIZATION**:
1179
- ${optimization_focus === 'automation' ? '- Identify repetitive manual tasks\n- Suggest automation workflows\n- Estimate time savings and ROI' : ''}
1180
- ${optimization_focus === 'data_quality' ? '- Data validation and cleansing rules\n- Consistency and accuracy improvements\n- Quality monitoring systems' : ''}
1181
- ${optimization_focus === 'collaboration' ? '- Team workflow improvements\n- Permission and access optimization\n- Communication enhancement strategies' : ''}
1182
-
1183
- **Deliverables**:
1184
- - Workflow efficiency assessment
1185
- - Prioritized improvement recommendations
1186
- - Implementation roadmap with timelines
1187
- - Cost-benefit analysis
1188
- - Change management considerations
1189
-
1190
- Please analyze the current setup and provide comprehensive ${optimization_focus} optimization recommendations.`
1191
- }
1192
- }
1193
- ];
1194
- break;
1195
-
1196
- case 'smart_schema_design':
1197
- const { use_case, data_volume = 'medium', integration_needs, compliance_requirements = 'none' } = promptArgs;
1198
- messages = [
1199
- {
1200
- role: 'user',
1201
- content: {
1202
- type: 'text',
1203
- text: `🏗️ AI-ASSISTED SCHEMA OPTIMIZATION
1204
-
1205
- **Use Case**: ${use_case}
1206
- **Data Volume**: ${data_volume}
1207
- **Compliance**: ${compliance_requirements}
1208
- ${integration_needs ? `**Integrations**: ${integration_needs}` : ''}
1209
-
1210
- **Schema Design Analysis**:
1211
- 1. Current schema evaluation for ${use_case} best practices
1212
- 2. Field type and relationship optimization
1213
- 3. Performance and scalability assessment
1214
- 4. Compliance requirement implementation
1215
- 5. Integration compatibility review
1216
-
1217
- **${use_case.toUpperCase()} OPTIMIZATION**:
1218
- ${use_case === 'crm' ? '- Customer lifecycle tracking\n- Sales pipeline optimization\n- Contact relationship mapping' : ''}
1219
- ${use_case === 'project_management' ? '- Task dependency modeling\n- Resource allocation tracking\n- Timeline and milestone management' : ''}
1220
- ${use_case === 'inventory' ? '- Stock level monitoring\n- Supplier relationship tracking\n- Cost and pricing optimization' : ''}
1221
-
1222
- **Recommendations**:
1223
- - Optimal field types and relationships
1224
- - Indexing and performance suggestions
1225
- - Data validation and integrity rules
1226
- - Automation and workflow triggers
1227
- - Scaling and maintenance considerations
1228
-
1229
- Please analyze the current schema and provide ${use_case}-optimized recommendations.`
1230
- }
1231
- }
1232
- ];
1233
- break;
1234
-
1235
- case 'data_quality_audit':
1236
- const { tables: auditTables, quality_dimensions = 'completeness,accuracy,consistency', severity_threshold = 'medium', auto_fix_suggestions = 'true' } = promptArgs;
1237
- messages = [
1238
- {
1239
- role: 'user',
1240
- content: {
1241
- type: 'text',
1242
- text: `🔍 COMPREHENSIVE DATA QUALITY AUDIT
1243
-
1244
- **Tables to Audit**: ${auditTables}
1245
- **Quality Dimensions**: ${quality_dimensions}
1246
- **Severity Threshold**: ${severity_threshold}
1247
- **Auto-Fix Suggestions**: ${auto_fix_suggestions}
1248
-
1249
- **Audit Framework**:
1250
- 1. Data completeness analysis (missing values, empty fields)
1251
- 2. Accuracy assessment (format validation, range checks)
1252
- 3. Consistency evaluation (cross-field validation, duplicates)
1253
- 4. Validity verification (data type compliance, constraints)
1254
- 5. Uniqueness analysis (duplicate detection, key integrity)
1255
- 6. Timeliness review (data freshness, update patterns)
1256
-
1257
- **Quality Assessment Process**:
1258
- - Statistical analysis of data distribution
1259
- - Pattern recognition for anomalies
1260
- - Cross-table consistency validation
1261
- - Historical trend analysis
1262
- - Business rule compliance checking
1263
-
1264
- **Deliverables**:
1265
- - Quality score by dimension and table
1266
- - Detailed issue identification and classification
1267
- - Impact assessment and prioritization
1268
- ${auto_fix_suggestions === 'true' ? '- Automated fix suggestions and scripts' : ''}
1269
- - Data governance recommendations
1270
- - Monitoring and maintenance strategies
1271
-
1272
- Please conduct a thorough data quality audit focusing on ${quality_dimensions} dimensions.`
1273
- }
1274
- }
1275
- ];
1276
- break;
1277
-
1278
- case 'predictive_analytics':
1279
- const { table: predTable, target_field, prediction_horizon = 'next_month', model_type = 'trend_analysis', feature_fields } = promptArgs;
1280
- messages = [
1281
- {
1282
- role: 'user',
1283
- content: {
1284
- type: 'text',
1285
- text: `🔮 ADVANCED PREDICTIVE ANALYTICS
1286
-
1287
- **Source Table**: ${predTable}
1288
- **Target Field**: ${target_field}
1289
- **Prediction Horizon**: ${prediction_horizon}
1290
- **Model Type**: ${model_type}
1291
- ${feature_fields ? `**Feature Fields**: ${feature_fields}` : ''}
1292
-
1293
- **Predictive Modeling Process**:
1294
- 1. Historical data analysis and trend identification
1295
- 2. Feature engineering and variable selection
1296
- 3. Model development using ${model_type} approach
1297
- 4. Validation and accuracy assessment
1298
- 5. Forecast generation for ${prediction_horizon}
1299
- 6. Confidence intervals and uncertainty quantification
1300
-
1301
- **${model_type.toUpperCase()} ANALYSIS**:
1302
- ${model_type === 'time_series' ? '- Seasonal pattern detection\n- Trend decomposition\n- Cyclical behavior analysis' : ''}
1303
- ${model_type === 'regression' ? '- Variable relationship modeling\n- Predictive factor identification\n- Statistical significance testing' : ''}
1304
- ${model_type === 'classification' ? '- Category prediction modeling\n- Feature importance analysis\n- Classification accuracy metrics' : ''}
1305
-
1306
- **Outputs**:
1307
- - Historical pattern analysis
1308
- - Predictive model performance metrics
1309
- - Forecast values with confidence intervals
1310
- - Key influencing factors identification
1311
- - Model limitations and assumptions
1312
- - Actionable insights and recommendations
1313
-
1314
- Please develop a ${model_type} model to predict ${target_field} over ${prediction_horizon}.`
1315
- }
1316
- }
1317
- ];
1318
- break;
1319
-
1320
- case 'natural_language_query':
1321
- const { question, context_tables, response_format = 'narrative', include_confidence = 'true' } = promptArgs;
1322
- messages = [
1323
- {
1324
- role: 'user',
1325
- content: {
1326
- type: 'text',
1327
- text: `🗣️ NATURAL LANGUAGE DATA QUERY
1328
-
1329
- **Question**: "${question}"
1330
- ${context_tables ? `**Context Tables**: ${context_tables}` : ''}
1331
- **Response Format**: ${response_format}
1332
- **Include Confidence**: ${include_confidence}
1333
-
1334
- **Query Processing Framework**:
1335
- 1. Question analysis and intent recognition
1336
- 2. Relevant table and field identification
1337
- 3. Data retrieval strategy formulation
1338
- 4. Analysis execution and result compilation
1339
- 5. Natural language response generation
1340
-
1341
- **Analysis Approach**:
1342
- - Semantic understanding of the question
1343
- - Automatic table and field mapping
1344
- - Intelligent data filtering and aggregation
1345
- - Statistical analysis where appropriate
1346
- - Context-aware interpretation
1347
-
1348
- **Response Requirements**:
1349
- ${response_format === 'narrative' ? '- Conversational, easy-to-understand explanation\n- Supporting data and evidence\n- Contextual insights and implications' : ''}
1350
- ${response_format === 'data_summary' ? '- Structured data summary\n- Key metrics and statistics\n- Trend identification' : ''}
1351
- ${response_format === 'visualization_suggestion' ? '- Chart and graph recommendations\n- Data visualization best practices\n- Tool-specific guidance' : ''}
1352
- ${include_confidence === 'true' ? '\n- Confidence scores for answers\n- Data quality indicators\n- Uncertainty acknowledgment' : ''}
1353
-
1354
- Please analyze the available data and provide a comprehensive answer to: "${question}"`
1355
- }
1356
- }
1357
- ];
1358
- break;
1359
-
1360
- case 'smart_data_transformation':
1361
- const { source_table, transformation_goal, target_format, quality_rules, preserve_history = 'true' } = promptArgs;
1362
- messages = [
1363
- {
1364
- role: 'user',
1365
- content: {
1366
- type: 'text',
1367
- text: `🔄 INTELLIGENT DATA TRANSFORMATION
1368
-
1369
- **Source Table**: ${source_table}
1370
- **Transformation Goal**: ${transformation_goal}
1371
- ${target_format ? `**Target Format**: ${target_format}` : ''}
1372
- ${quality_rules ? `**Quality Rules**: ${quality_rules}` : ''}
1373
- **Preserve History**: ${preserve_history}
1374
-
1375
- **Transformation Framework**:
1376
- 1. Source data analysis and quality assessment
1377
- 2. Transformation strategy development
1378
- 3. Data mapping and conversion rules
1379
- 4. Quality validation and error handling
1380
- 5. Output optimization and validation
1381
-
1382
- **${transformation_goal.toUpperCase()} PROCESS**:
1383
- ${transformation_goal === 'normalize' ? '- Database normalization principles\n- Redundancy elimination\n- Relationship optimization' : ''}
1384
- ${transformation_goal === 'standardize' ? '- Format standardization\n- Value normalization\n- Consistency enforcement' : ''}
1385
- ${transformation_goal === 'enrich' ? '- Data augmentation strategies\n- External data integration\n- Value-added field creation' : ''}
1386
- ${transformation_goal === 'cleanse' ? '- Data validation and correction\n- Duplicate removal\n- Missing value handling' : ''}
1387
-
1388
- **Deliverables**:
1389
- - Transformation execution plan
1390
- - Data mapping specifications
1391
- - Quality validation results
1392
- - Performance optimization recommendations
1393
- ${preserve_history === 'true' ? '- Change audit trail and versioning' : ''}
1394
- - Post-transformation validation
1395
-
1396
- Please analyze the source data and execute ${transformation_goal} transformation with intelligent optimization.`
1397
- }
1398
- }
1399
- ];
1400
- break;
1401
-
1402
- case 'automation_recommendations':
1403
- const { workflow_description, automation_scope = 'single_table', frequency_patterns, complexity_tolerance = 'moderate', integration_capabilities } = promptArgs;
1404
- messages = [
1405
- {
1406
- role: 'user',
1407
- content: {
1408
- type: 'text',
1409
- text: `🤖 INTELLIGENT AUTOMATION RECOMMENDATIONS
1410
-
1411
- **Automation Scope**: ${automation_scope}
1412
- **Complexity Tolerance**: ${complexity_tolerance}
1413
- ${workflow_description ? `**Current Workflow**: ${workflow_description}` : ''}
1414
- ${frequency_patterns ? `**Frequency Patterns**: ${frequency_patterns}` : ''}
1415
- ${integration_capabilities ? `**Integration Tools**: ${integration_capabilities}` : ''}
1416
-
1417
- **Automation Analysis Framework**:
1418
- 1. Workflow pattern analysis and task identification
1419
- 2. Automation opportunity assessment and prioritization
1420
- 3. Technical feasibility and complexity evaluation
1421
- 4. ROI calculation and benefit quantification
1422
- 5. Implementation roadmap development
1423
-
1424
- **${automation_scope.toUpperCase()} AUTOMATION**:
1425
- ${automation_scope === 'single_table' ? '- Field auto-population rules\n- Data validation automation\n- Notification triggers' : ''}
1426
- ${automation_scope === 'multi_table' ? '- Cross-table data synchronization\n- Workflow orchestration\n- Complex business logic automation' : ''}
1427
- ${automation_scope === 'external_integration' ? '- API integration strategies\n- Data pipeline automation\n- Third-party tool connectivity' : ''}
1428
-
1429
- **Recommendations**:
1430
- - High-impact automation opportunities
1431
- - Implementation complexity assessment
1432
- - Cost-benefit analysis with ROI projections
1433
- - Technical requirements and dependencies
1434
- - Risk assessment and mitigation strategies
1435
- - Success metrics and monitoring approach
1436
-
1437
- Please analyze the workflow patterns and provide ${complexity_tolerance}-level automation recommendations for ${automation_scope} scope.`
1438
- }
1439
- }
1440
- ];
1441
- break;
1442
-
1443
- default:
1444
- throw new Error(`Unsupported prompt: ${promptName}`);
1445
- }
1446
-
1447
- return {
1448
- jsonrpc: '2.0',
1449
- id: request.id,
1450
- result: {
1451
- description: prompt.description,
1452
- messages: messages
1453
- }
1454
- };
1455
-
1456
- } catch (error) {
1457
- log(LOG_LEVELS.ERROR, `Prompt ${promptName} failed`, { error: error.message });
1458
-
1459
- return {
1460
- jsonrpc: '2.0',
1461
- id: request.id,
1462
- error: {
1463
- code: -32000,
1464
- message: `Error getting prompt ${promptName}: ${error.message}`
1465
- }
1466
- };
1467
- }
1468
- }
1469
-
1470
- // Sampling handler
1471
- async function handleSampling(request) {
1472
- const { messages, modelPreferences } = request.params;
1473
-
1474
- try {
1475
- // Note: In a real implementation, this would integrate with an LLM API
1476
- // For now, we'll return a structured response indicating sampling capability
1477
-
1478
- log(LOG_LEVELS.INFO, 'Sampling request received', {
1479
- messageCount: messages?.length,
1480
- model: modelPreferences?.model
1481
- });
1482
-
1483
- return {
1484
- jsonrpc: '2.0',
1485
- id: request.id,
1486
- result: {
1487
- model: modelPreferences?.model || 'claude-3-sonnet',
1488
- role: 'assistant',
1489
- content: {
1490
- type: 'text',
1491
- text: 'Sampling capability is available. This MCP server can request AI assistance for complex data analysis and insights generation. In a full implementation, this would connect to your preferred LLM for intelligent Airtable operations.'
1492
- },
1493
- stopReason: 'end_turn'
1494
- }
1495
- };
1496
-
1497
- } catch (error) {
1498
- log(LOG_LEVELS.ERROR, 'Sampling failed', { error: error.message });
1499
-
1500
- return {
1501
- jsonrpc: '2.0',
1502
- id: request.id,
1503
- error: {
1504
- code: -32000,
1505
- message: `Sampling error: ${error.message}`
1506
- }
1507
- };
1508
- }
1509
- }
1510
-
1511
- // Server startup
1512
- const PORT = CONFIG.PORT;
1513
- const HOST = CONFIG.HOST;
1514
-
1515
- server.listen(PORT, HOST, () => {
1516
- log(LOG_LEVELS.INFO, `Airtable MCP Server started`, {
1517
- host: HOST,
1518
- port: PORT,
1519
- version: '2.1.0'
1520
- });
1521
-
1522
- console.log(`
1523
- ╔═══════════════════════════════════════════════════════════════╗
1524
- ║ Airtable MCP Server v2.1 ║
1525
- ║ Model Context Protocol Implementation ║
1526
- ╠═══════════════════════════════════════════════════════════════╣
1527
- ║ 🌐 MCP Endpoint: http://${HOST}:${PORT}/mcp ║
1528
- ║ 📊 Health Check: http://${HOST}:${PORT}/health ║
1529
- ║ 🔒 Security: Rate limiting, input validation ║
1530
- ║ 📋 Tools: ${TOOLS_SCHEMA.length} available operations ║
1531
- ╠═══════════════════════════════════════════════════════════════╣
1532
- ║ 🔗 Connected to Airtable Base: ${baseId.slice(0, 8)}... ║
1533
- ║ 🚀 Ready for MCP client connections ║
1534
- ╚═══════════════════════════════════════════════════════════════╝
1535
- `);
1536
- });
1537
-
1538
- // Graceful shutdown
1539
- function gracefulShutdown(signal) {
1540
- log(LOG_LEVELS.INFO, 'Graceful shutdown initiated', { signal });
1541
-
1542
- server.close(() => {
1543
- log(LOG_LEVELS.INFO, 'Server stopped');
1544
- process.exit(0);
1545
- });
1546
-
1547
- setTimeout(() => {
1548
- log(LOG_LEVELS.ERROR, 'Force shutdown - server did not close in time');
1549
- process.exit(1);
1550
- }, 10000);
1551
- }
1552
-
1553
- process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
1554
- process.on('SIGINT', () => gracefulShutdown('SIGINT'));
1555
-
1556
- process.on('uncaughtException', (error) => {
1557
- log(LOG_LEVELS.ERROR, 'Uncaught exception', { error: error.message });
1558
- gracefulShutdown('uncaughtException');
1559
- });
1560
-
1561
- process.on('unhandledRejection', (reason) => {
1562
- log(LOG_LEVELS.ERROR, 'Unhandled promise rejection', { reason: reason?.toString() });
1563
- gracefulShutdown('unhandledRejection');
1564
- });