@rashidazarang/airtable-mcp 1.5.0 → 2.1.0
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/.github/ISSUE_TEMPLATE/bug-report.yml +173 -0
- package/.github/ISSUE_TEMPLATE/feature-request.yml +209 -0
- package/.github/ISSUE_TEMPLATE/security-report.yml +216 -0
- package/.github/pull_request_template.md +245 -0
- package/.github/workflows/ci-cd.yml +408 -0
- package/.github/workflows/security-audit.yml +316 -0
- package/API_DOCUMENTATION.md +897 -0
- package/CODE_OF_CONDUCT.md +181 -0
- package/Dockerfile.production +127 -0
- package/README.md +55 -10
- package/RELEASE_NOTES_v1.6.0.md +248 -0
- package/airtable-clipper/CHANGELOG.md +198 -0
- package/airtable-clipper/CHROME_STORE_SUBMISSION.md +343 -0
- package/airtable-clipper/LAUNCH_STRATEGY.md +495 -0
- package/airtable-clipper/LICENSE +21 -0
- package/airtable-clipper/OAUTH_SETUP.md +51 -0
- package/airtable-clipper/PRIVACY_POLICY.md +187 -0
- package/airtable-clipper/README.md +575 -0
- package/airtable-clipper/SUBMIT_TO_CHROME_STORE.md +273 -0
- package/airtable-clipper/build.sh +85 -0
- package/airtable-clipper/docs/QUICK_START.md +99 -0
- package/airtable-clipper/docs/SETUP.md +291 -0
- package/airtable-clipper/extension/background.js +337 -0
- package/airtable-clipper/extension/base-setup.html +324 -0
- package/airtable-clipper/extension/base-setup.js +471 -0
- package/airtable-clipper/extension/content.js +771 -0
- package/airtable-clipper/extension/icons/README.md +69 -0
- package/airtable-clipper/extension/icons/icon-16.png +3 -0
- package/airtable-clipper/extension/manifest.json +73 -0
- package/airtable-clipper/extension/popup.html +144 -0
- package/airtable-clipper/extension/popup.js +475 -0
- package/airtable-clipper/extension/styles/content.css +229 -0
- package/airtable-clipper/extension/styles/popup.css +477 -0
- package/airtable-clipper/privacy-policy.md +63 -0
- package/airtable-clipper/releases/v1.0.0/background.js +337 -0
- package/airtable-clipper/releases/v1.0.0/base-setup.html +324 -0
- package/airtable-clipper/releases/v1.0.0/base-setup.js +471 -0
- package/airtable-clipper/releases/v1.0.0/content.js +771 -0
- package/airtable-clipper/releases/v1.0.0/icons/README.md +69 -0
- package/airtable-clipper/releases/v1.0.0/icons/icon-128.png +2 -0
- package/airtable-clipper/releases/v1.0.0/icons/icon-16.png +3 -0
- package/airtable-clipper/releases/v1.0.0/icons/icon-32.png +2 -0
- package/airtable-clipper/releases/v1.0.0/icons/icon-48.png +2 -0
- package/airtable-clipper/releases/v1.0.0/manifest.json +73 -0
- package/airtable-clipper/releases/v1.0.0/popup.html +144 -0
- package/airtable-clipper/releases/v1.0.0/popup.js +475 -0
- package/airtable-clipper/releases/v1.0.0/sidepanel.html +25 -0
- package/airtable-clipper/releases/v1.0.0/styles/content.css +229 -0
- package/airtable-clipper/releases/v1.0.0/styles/popup.css +477 -0
- package/airtable-clipper/releases/v1.0.1/background.js +337 -0
- package/airtable-clipper/releases/v1.0.1/base-setup.html +324 -0
- package/airtable-clipper/releases/v1.0.1/base-setup.js +471 -0
- package/airtable-clipper/releases/v1.0.1/content.js +771 -0
- package/airtable-clipper/releases/v1.0.1/icons/README.md +69 -0
- package/airtable-clipper/releases/v1.0.1/icons/icon-128.png +2 -0
- package/airtable-clipper/releases/v1.0.1/icons/icon-16.png +3 -0
- package/airtable-clipper/releases/v1.0.1/icons/icon-32.png +2 -0
- package/airtable-clipper/releases/v1.0.1/icons/icon-48.png +2 -0
- package/airtable-clipper/releases/v1.0.1/manifest.json +70 -0
- package/airtable-clipper/releases/v1.0.1/popup.html +157 -0
- package/airtable-clipper/releases/v1.0.1/popup.js +562 -0
- package/airtable-clipper/releases/v1.0.1/sidepanel.html +25 -0
- package/airtable-clipper/releases/v1.0.1/styles/content.css +229 -0
- package/airtable-clipper/releases/v1.0.1/styles/popup.css +647 -0
- package/airtable-clipper/releases/v1.0.2/background.js +337 -0
- package/airtable-clipper/releases/v1.0.2/base-setup.html +324 -0
- package/airtable-clipper/releases/v1.0.2/base-setup.js +471 -0
- package/airtable-clipper/releases/v1.0.2/content.js +771 -0
- package/airtable-clipper/releases/v1.0.2/icons/README.md +69 -0
- package/airtable-clipper/releases/v1.0.2/icons/icon-128.png +2 -0
- package/airtable-clipper/releases/v1.0.2/icons/icon-16.png +3 -0
- package/airtable-clipper/releases/v1.0.2/icons/icon-32.png +2 -0
- package/airtable-clipper/releases/v1.0.2/icons/icon-48.png +2 -0
- package/airtable-clipper/releases/v1.0.2/manifest.json +62 -0
- package/airtable-clipper/releases/v1.0.2/popup.html +157 -0
- package/airtable-clipper/releases/v1.0.2/popup.js +567 -0
- package/airtable-clipper/releases/v1.0.2/sidepanel.html +25 -0
- package/airtable-clipper/releases/v1.0.2/styles/content.css +229 -0
- package/airtable-clipper/releases/v1.0.2/styles/popup.css +647 -0
- package/airtable-clipper/terms-of-service.md +124 -0
- package/airtable-clipper/test-credentials.md +61 -0
- package/airtable-clipper/test-extension/background.js +337 -0
- package/airtable-clipper/test-extension/base-setup.html +324 -0
- package/airtable-clipper/test-extension/base-setup.js +471 -0
- package/airtable-clipper/test-extension/content.js +873 -0
- package/airtable-clipper/test-extension/icons/README.md +69 -0
- package/airtable-clipper/test-extension/icons/icon-128.png +2 -0
- package/airtable-clipper/test-extension/icons/icon-16.png +3 -0
- package/airtable-clipper/test-extension/icons/icon-32.png +2 -0
- package/airtable-clipper/test-extension/icons/icon-48.png +2 -0
- package/airtable-clipper/test-extension/manifest.json +72 -0
- package/airtable-clipper/test-extension/popup.html +274 -0
- package/airtable-clipper/test-extension/popup.js +729 -0
- package/airtable-clipper/test-extension/sidepanel.html +25 -0
- package/airtable-clipper/test-extension/styles/content.css +229 -0
- package/airtable-clipper/test-extension/styles/popup.css +794 -0
- package/airtable_mcp_v2.js +1505 -0
- package/airtable_mcp_v2_oauth.js +1048 -0
- package/airtable_mcp_v3_advanced.js +1161 -0
- package/airtable_simple.js +447 -1
- package/airtable_simple_production.js +532 -0
- package/docker-compose.production.yml +366 -0
- package/helm/airtable-mcp/Chart.yaml +122 -0
- package/helm/airtable-mcp/values.yaml +538 -0
- package/k8s/deployment.yaml +402 -0
- package/k8s/namespace.yaml +108 -0
- package/k8s/service.yaml +194 -0
- package/monitoring/alerts.yml +289 -0
- package/monitoring/prometheus.yml +224 -0
- package/package.json +6 -6
- package/test_v1.6.0_comprehensive.sh +187 -0
- package/.claude/settings.local.json +0 -12
- package/airtable-mcp-1.1.0.tgz +0 -0
- package/airtable_enhanced.js +0 -499
- package/airtable_simple_v1.2.4_backup.js +0 -277
- package/airtable_v1.4.0.js +0 -654
- package/rashidazarang-airtable-mcp-1.1.0.tgz +0 -0
- package/rashidazarang-airtable-mcp-1.2.0.tgz +0 -0
- package/rashidazarang-airtable-mcp-1.2.1.tgz +0 -0
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const http = require('http');
|
|
4
|
-
const https = require('https');
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
|
|
8
|
-
// Load environment variables from .env file if it exists
|
|
9
|
-
const envPath = path.join(__dirname, '.env');
|
|
10
|
-
if (fs.existsSync(envPath)) {
|
|
11
|
-
require('dotenv').config({ path: envPath });
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Parse command line arguments with environment variable fallback
|
|
15
|
-
const args = process.argv.slice(2);
|
|
16
|
-
let tokenIndex = args.indexOf('--token');
|
|
17
|
-
let baseIndex = args.indexOf('--base');
|
|
18
|
-
|
|
19
|
-
// Use environment variables as fallback
|
|
20
|
-
const token = tokenIndex !== -1 ? args[tokenIndex + 1] : process.env.AIRTABLE_TOKEN || process.env.AIRTABLE_API_TOKEN;
|
|
21
|
-
const baseId = baseIndex !== -1 ? args[baseIndex + 1] : process.env.AIRTABLE_BASE_ID || process.env.AIRTABLE_BASE;
|
|
22
|
-
|
|
23
|
-
if (!token || !baseId) {
|
|
24
|
-
console.error('Error: Missing Airtable credentials');
|
|
25
|
-
console.error('\nUsage options:');
|
|
26
|
-
console.error(' 1. Command line: node airtable_simple.js --token YOUR_TOKEN --base YOUR_BASE_ID');
|
|
27
|
-
console.error(' 2. Environment variables: AIRTABLE_TOKEN and AIRTABLE_BASE_ID');
|
|
28
|
-
console.error(' 3. .env file with AIRTABLE_TOKEN and AIRTABLE_BASE_ID');
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Configure logging levels
|
|
33
|
-
const LOG_LEVELS = {
|
|
34
|
-
ERROR: 0,
|
|
35
|
-
WARN: 1,
|
|
36
|
-
INFO: 2,
|
|
37
|
-
DEBUG: 3
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const currentLogLevel = process.env.LOG_LEVEL ? LOG_LEVELS[process.env.LOG_LEVEL.toUpperCase()] || LOG_LEVELS.INFO : LOG_LEVELS.INFO;
|
|
41
|
-
|
|
42
|
-
function log(level, message, ...args) {
|
|
43
|
-
const levelName = Object.keys(LOG_LEVELS).find(key => LOG_LEVELS[key] === level);
|
|
44
|
-
const timestamp = new Date().toISOString();
|
|
45
|
-
|
|
46
|
-
if (level <= currentLogLevel) {
|
|
47
|
-
const prefix = `[${timestamp}] [${levelName}]`;
|
|
48
|
-
if (level === LOG_LEVELS.ERROR) {
|
|
49
|
-
console.error(prefix, message, ...args);
|
|
50
|
-
} else if (level === LOG_LEVELS.WARN) {
|
|
51
|
-
console.warn(prefix, message, ...args);
|
|
52
|
-
} else {
|
|
53
|
-
console.log(prefix, message, ...args);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
log(LOG_LEVELS.INFO, `Starting Airtable MCP server with token ${token.slice(0, 5)}...${token.slice(-5)} and base ${baseId}`);
|
|
59
|
-
|
|
60
|
-
// Create HTTP server
|
|
61
|
-
const server = http.createServer(async (req, res) => {
|
|
62
|
-
// Enable CORS
|
|
63
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
64
|
-
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
|
65
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
66
|
-
|
|
67
|
-
// Handle preflight request
|
|
68
|
-
if (req.method === 'OPTIONS') {
|
|
69
|
-
res.writeHead(200);
|
|
70
|
-
res.end();
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Only handle POST requests to /mcp
|
|
75
|
-
if (req.method !== 'POST' || !req.url.endsWith('/mcp')) {
|
|
76
|
-
res.writeHead(404);
|
|
77
|
-
res.end();
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
let body = '';
|
|
82
|
-
req.on('data', chunk => {
|
|
83
|
-
body += chunk.toString();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
req.on('end', async () => {
|
|
87
|
-
try {
|
|
88
|
-
const request = JSON.parse(body);
|
|
89
|
-
|
|
90
|
-
// Handle JSON-RPC methods
|
|
91
|
-
if (request.method === 'resources/list') {
|
|
92
|
-
const response = {
|
|
93
|
-
jsonrpc: '2.0',
|
|
94
|
-
id: request.id,
|
|
95
|
-
result: {
|
|
96
|
-
resources: [
|
|
97
|
-
{
|
|
98
|
-
id: 'airtable_tables',
|
|
99
|
-
name: 'Airtable Tables',
|
|
100
|
-
description: 'Tables in your Airtable base'
|
|
101
|
-
}
|
|
102
|
-
]
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
106
|
-
res.end(JSON.stringify(response));
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (request.method === 'prompts/list') {
|
|
111
|
-
const response = {
|
|
112
|
-
jsonrpc: '2.0',
|
|
113
|
-
id: request.id,
|
|
114
|
-
result: {
|
|
115
|
-
prompts: [
|
|
116
|
-
{
|
|
117
|
-
id: 'tables_prompt',
|
|
118
|
-
name: 'List Tables',
|
|
119
|
-
description: 'List all tables'
|
|
120
|
-
}
|
|
121
|
-
]
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
125
|
-
res.end(JSON.stringify(response));
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Handle tool calls
|
|
130
|
-
if (request.method === 'tools/call') {
|
|
131
|
-
const toolName = request.params.name;
|
|
132
|
-
|
|
133
|
-
if (toolName === 'list_tables') {
|
|
134
|
-
// Call Airtable API to list tables
|
|
135
|
-
const result = await callAirtableAPI(`meta/bases/${baseId}/tables`);
|
|
136
|
-
const tables = result.tables || [];
|
|
137
|
-
|
|
138
|
-
const tableList = tables.map((table, i) =>
|
|
139
|
-
`${i+1}. ${table.name} (ID: ${table.id})`
|
|
140
|
-
).join('\n');
|
|
141
|
-
|
|
142
|
-
const response = {
|
|
143
|
-
jsonrpc: '2.0',
|
|
144
|
-
id: request.id,
|
|
145
|
-
result: {
|
|
146
|
-
content: [
|
|
147
|
-
{
|
|
148
|
-
type: 'text',
|
|
149
|
-
text: tables.length > 0
|
|
150
|
-
? `Tables in this base:\n${tableList}`
|
|
151
|
-
: 'No tables found in this base.'
|
|
152
|
-
}
|
|
153
|
-
]
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
158
|
-
res.end(JSON.stringify(response));
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (toolName === 'list_records') {
|
|
163
|
-
const tableName = request.params.arguments.table_name;
|
|
164
|
-
const maxRecords = request.params.arguments.max_records || 100;
|
|
165
|
-
|
|
166
|
-
// Call Airtable API to list records
|
|
167
|
-
const result = await callAirtableAPI(`${baseId}/${tableName}`, { maxRecords });
|
|
168
|
-
const records = result.records || [];
|
|
169
|
-
|
|
170
|
-
const recordList = records.map((record, i) => {
|
|
171
|
-
const fields = Object.entries(record.fields || {})
|
|
172
|
-
.map(([k, v]) => `${k}: ${v}`)
|
|
173
|
-
.join(', ');
|
|
174
|
-
return `${i+1}. ID: ${record.id} - ${fields}`;
|
|
175
|
-
}).join('\n');
|
|
176
|
-
|
|
177
|
-
const response = {
|
|
178
|
-
jsonrpc: '2.0',
|
|
179
|
-
id: request.id,
|
|
180
|
-
result: {
|
|
181
|
-
content: [
|
|
182
|
-
{
|
|
183
|
-
type: 'text',
|
|
184
|
-
text: records.length > 0
|
|
185
|
-
? `Records:\n${recordList}`
|
|
186
|
-
: 'No records found in this table.'
|
|
187
|
-
}
|
|
188
|
-
]
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
193
|
-
res.end(JSON.stringify(response));
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Tool not found
|
|
198
|
-
const response = {
|
|
199
|
-
jsonrpc: '2.0',
|
|
200
|
-
id: request.id,
|
|
201
|
-
error: {
|
|
202
|
-
code: -32601,
|
|
203
|
-
message: `Tool ${toolName} not found`
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
207
|
-
res.end(JSON.stringify(response));
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Method not found
|
|
212
|
-
const response = {
|
|
213
|
-
jsonrpc: '2.0',
|
|
214
|
-
id: request.id,
|
|
215
|
-
error: {
|
|
216
|
-
code: -32601,
|
|
217
|
-
message: `Method ${request.method} not found`
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
221
|
-
res.end(JSON.stringify(response));
|
|
222
|
-
|
|
223
|
-
} catch (error) {
|
|
224
|
-
console.error('Error processing request:', error);
|
|
225
|
-
const response = {
|
|
226
|
-
jsonrpc: '2.0',
|
|
227
|
-
id: request.id || null,
|
|
228
|
-
error: {
|
|
229
|
-
code: -32000,
|
|
230
|
-
message: error.message || 'Unknown error'
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
234
|
-
res.end(JSON.stringify(response));
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
// Helper function to call Airtable API
|
|
240
|
-
function callAirtableAPI(endpoint, params = {}) {
|
|
241
|
-
return new Promise((resolve, reject) => {
|
|
242
|
-
const queryParams = new URLSearchParams(params).toString();
|
|
243
|
-
const url = `https://api.airtable.com/v0/${endpoint}${queryParams ? '?' + queryParams : ''}`;
|
|
244
|
-
|
|
245
|
-
const options = {
|
|
246
|
-
headers: {
|
|
247
|
-
'Authorization': `Bearer ${token}`,
|
|
248
|
-
'Content-Type': 'application/json'
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
https.get(url, options, (response) => {
|
|
253
|
-
let data = '';
|
|
254
|
-
|
|
255
|
-
response.on('data', (chunk) => {
|
|
256
|
-
data += chunk;
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
response.on('end', () => {
|
|
260
|
-
try {
|
|
261
|
-
resolve(JSON.parse(data));
|
|
262
|
-
} catch (e) {
|
|
263
|
-
reject(new Error(`Failed to parse Airtable response: ${e.message}`));
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
}).on('error', (error) => {
|
|
267
|
-
reject(new Error(`Airtable API request failed: ${error.message}`));
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Start the server on port 8010
|
|
273
|
-
const PORT = 8010;
|
|
274
|
-
server.listen(PORT, () => {
|
|
275
|
-
console.log(`Airtable MCP server running at http://localhost:${PORT}/mcp`);
|
|
276
|
-
console.log(`For Claude, use this URL: http://localhost:${PORT}/mcp`);
|
|
277
|
-
});
|