@rashidazarang/airtable-mcp 1.2.4 ā 1.4.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/.claude/settings.local.json +4 -1
- package/CAPABILITY_REPORT.md +118 -0
- package/DEVELOPMENT.md +1 -0
- package/IMPROVEMENT_PROPOSAL.md +371 -0
- package/MCP_REVIEW_SUMMARY.md +1 -0
- package/RELEASE_NOTES_v1.2.3.md +1 -0
- package/RELEASE_NOTES_v1.4.0.md +104 -0
- package/airtable_enhanced.js +499 -0
- package/airtable_simple.js +459 -83
- package/airtable_simple_v1.2.4_backup.js +277 -0
- package/airtable_v1.4.0.js +654 -0
- package/cleanup.sh +1 -0
- package/package.json +1 -1
- package/quick_test.sh +1 -0
- package/test_all_features.sh +146 -0
- package/test_all_operations.sh +120 -0
- package/test_enhanced_features.js +389 -0
- package/test_mcp_comprehensive.js +1 -0
- package/test_mock_server.js +180 -0
- package/test_v1.4.0_final.sh +131 -0
- package/test_webhooks.sh +105 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
echo "šÆ COMPREHENSIVE TEST - AIRTABLE MCP v1.4.0"
|
|
4
|
+
echo "==========================================="
|
|
5
|
+
echo ""
|
|
6
|
+
|
|
7
|
+
PASSED=0
|
|
8
|
+
FAILED=0
|
|
9
|
+
TOTAL=0
|
|
10
|
+
|
|
11
|
+
# Test function
|
|
12
|
+
test_feature() {
|
|
13
|
+
local name=$1
|
|
14
|
+
local result=$2
|
|
15
|
+
((TOTAL++))
|
|
16
|
+
|
|
17
|
+
if [ "$result" = "PASS" ]; then
|
|
18
|
+
echo "ā
$name"
|
|
19
|
+
((PASSED++))
|
|
20
|
+
else
|
|
21
|
+
echo "ā $name"
|
|
22
|
+
((FAILED++))
|
|
23
|
+
fi
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
echo "š TESTING ALL 12 TOOLS"
|
|
27
|
+
echo "======================="
|
|
28
|
+
echo ""
|
|
29
|
+
|
|
30
|
+
# 1. List tables
|
|
31
|
+
result=$(curl -s -X POST http://localhost:8010/mcp -H "Content-Type: application/json" \
|
|
32
|
+
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "list_tables"}}')
|
|
33
|
+
if [[ "$result" == *"table"* ]]; then
|
|
34
|
+
test_feature "list_tables" "PASS"
|
|
35
|
+
else
|
|
36
|
+
test_feature "list_tables" "FAIL"
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# 2. Create record
|
|
40
|
+
result=$(curl -s -X POST http://localhost:8010/mcp -H "Content-Type: application/json" \
|
|
41
|
+
-d '{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "create_record", "arguments": {"table": "tblH7TnJxYpNqhQYK", "fields": {"Name": "Final Test", "Status": "Active"}}}}')
|
|
42
|
+
if [[ "$result" == *"Successfully created"* ]]; then
|
|
43
|
+
test_feature "create_record" "PASS"
|
|
44
|
+
RECORD_ID=$(echo "$result" | grep -o 'rec[a-zA-Z0-9]\{10,20\}' | head -1)
|
|
45
|
+
else
|
|
46
|
+
test_feature "create_record" "FAIL"
|
|
47
|
+
RECORD_ID=""
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# 3. Get record
|
|
51
|
+
if [ ! -z "$RECORD_ID" ]; then
|
|
52
|
+
result=$(curl -s -X POST http://localhost:8010/mcp -H "Content-Type: application/json" \
|
|
53
|
+
-d "{\"jsonrpc\": \"2.0\", \"id\": 3, \"method\": \"tools/call\", \"params\": {\"name\": \"get_record\", \"arguments\": {\"table\": \"tblH7TnJxYpNqhQYK\", \"recordId\": \"$RECORD_ID\"}}}")
|
|
54
|
+
[[ "$result" == *"Record $RECORD_ID"* ]] && test_feature "get_record" "PASS" || test_feature "get_record" "FAIL"
|
|
55
|
+
else
|
|
56
|
+
test_feature "get_record" "SKIP"
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# 4. Update record
|
|
60
|
+
if [ ! -z "$RECORD_ID" ]; then
|
|
61
|
+
result=$(curl -s -X POST http://localhost:8010/mcp -H "Content-Type: application/json" \
|
|
62
|
+
-d "{\"jsonrpc\": \"2.0\", \"id\": 4, \"method\": \"tools/call\", \"params\": {\"name\": \"update_record\", \"arguments\": {\"table\": \"tblH7TnJxYpNqhQYK\", \"recordId\": \"$RECORD_ID\", \"fields\": {\"Status\": \"Completed\"}}}}")
|
|
63
|
+
[[ "$result" == *"Successfully updated"* ]] && test_feature "update_record" "PASS" || test_feature "update_record" "FAIL"
|
|
64
|
+
else
|
|
65
|
+
test_feature "update_record" "SKIP"
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# 5. List records
|
|
69
|
+
result=$(curl -s -X POST http://localhost:8010/mcp -H "Content-Type: application/json" \
|
|
70
|
+
-d '{"jsonrpc": "2.0", "id": 5, "method": "tools/call", "params": {"name": "list_records", "arguments": {"table": "tblH7TnJxYpNqhQYK", "maxRecords": 3}}}')
|
|
71
|
+
[[ "$result" == *"record"* ]] && test_feature "list_records" "PASS" || test_feature "list_records" "FAIL"
|
|
72
|
+
|
|
73
|
+
# 6. Search records
|
|
74
|
+
result=$(curl -s -X POST http://localhost:8010/mcp -H "Content-Type: application/json" \
|
|
75
|
+
-d '{"jsonrpc": "2.0", "id": 6, "method": "tools/call", "params": {"name": "search_records", "arguments": {"table": "tblH7TnJxYpNqhQYK", "maxRecords": 3}}}')
|
|
76
|
+
[[ "$result" == *"record"* ]] && test_feature "search_records" "PASS" || test_feature "search_records" "FAIL"
|
|
77
|
+
|
|
78
|
+
# 7. Delete record
|
|
79
|
+
if [ ! -z "$RECORD_ID" ]; then
|
|
80
|
+
result=$(curl -s -X POST http://localhost:8010/mcp -H "Content-Type: application/json" \
|
|
81
|
+
-d "{\"jsonrpc\": \"2.0\", \"id\": 7, \"method\": \"tools/call\", \"params\": {\"name\": \"delete_record\", \"arguments\": {\"table\": \"tblH7TnJxYpNqhQYK\", \"recordId\": \"$RECORD_ID\"}}}")
|
|
82
|
+
[[ "$result" == *"Successfully deleted"* ]] && test_feature "delete_record" "PASS" || test_feature "delete_record" "FAIL"
|
|
83
|
+
else
|
|
84
|
+
test_feature "delete_record" "SKIP"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# 8. List webhooks
|
|
88
|
+
result=$(curl -s -X POST http://localhost:8010/mcp -H "Content-Type: application/json" \
|
|
89
|
+
-d '{"jsonrpc": "2.0", "id": 8, "method": "tools/call", "params": {"name": "list_webhooks"}}')
|
|
90
|
+
[[ "$result" == *"webhook"* ]] && test_feature "list_webhooks" "PASS" || test_feature "list_webhooks" "FAIL"
|
|
91
|
+
|
|
92
|
+
# 9. Create webhook
|
|
93
|
+
result=$(curl -s -X POST http://localhost:8010/mcp -H "Content-Type: application/json" \
|
|
94
|
+
-d '{"jsonrpc": "2.0", "id": 9, "method": "tools/call", "params": {"name": "create_webhook", "arguments": {"notificationUrl": "https://webhook.site/test-final"}}}')
|
|
95
|
+
if [[ "$result" == *"Successfully created"* ]]; then
|
|
96
|
+
test_feature "create_webhook" "PASS"
|
|
97
|
+
WEBHOOK_ID=$(echo "$result" | grep -o 'ach[a-zA-Z0-9]*' | head -1)
|
|
98
|
+
else
|
|
99
|
+
test_feature "create_webhook" "FAIL"
|
|
100
|
+
WEBHOOK_ID=""
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
# 10. Get webhook payloads
|
|
104
|
+
if [ ! -z "$WEBHOOK_ID" ]; then
|
|
105
|
+
result=$(curl -s -X POST http://localhost:8010/mcp -H "Content-Type: application/json" \
|
|
106
|
+
-d "{\"jsonrpc\": \"2.0\", \"id\": 10, \"method\": \"tools/call\", \"params\": {\"name\": \"get_webhook_payloads\", \"arguments\": {\"webhookId\": \"$WEBHOOK_ID\"}}}")
|
|
107
|
+
[[ "$result" == *"payload"* ]] && test_feature "get_webhook_payloads" "PASS" || test_feature "get_webhook_payloads" "FAIL"
|
|
108
|
+
else
|
|
109
|
+
test_feature "get_webhook_payloads" "SKIP"
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
# 11. Refresh webhook
|
|
113
|
+
if [ ! -z "$WEBHOOK_ID" ]; then
|
|
114
|
+
result=$(curl -s -X POST http://localhost:8010/mcp -H "Content-Type: application/json" \
|
|
115
|
+
-d "{\"jsonrpc\": \"2.0\", \"id\": 11, \"method\": \"tools/call\", \"params\": {\"name\": \"refresh_webhook\", \"arguments\": {\"webhookId\": \"$WEBHOOK_ID\"}}}")
|
|
116
|
+
[[ "$result" == *"refreshed"* ]] && test_feature "refresh_webhook" "PASS" || test_feature "refresh_webhook" "FAIL"
|
|
117
|
+
else
|
|
118
|
+
test_feature "refresh_webhook" "SKIP"
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
# 12. Delete webhook
|
|
122
|
+
if [ ! -z "$WEBHOOK_ID" ]; then
|
|
123
|
+
result=$(curl -s -X POST http://localhost:8010/mcp -H "Content-Type: application/json" \
|
|
124
|
+
-d "{\"jsonrpc\": \"2.0\", \"id\": 12, \"method\": \"tools/call\", \"params\": {\"name\": \"delete_webhook\", \"arguments\": {\"webhookId\": \"$WEBHOOK_ID\"}}}")
|
|
125
|
+
[[ "$result" == *"deleted"* ]] && test_feature "delete_webhook" "PASS" || test_feature "delete_webhook" "FAIL"
|
|
126
|
+
else
|
|
127
|
+
test_feature "delete_webhook" "SKIP"
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
echo ""
|
|
131
|
+
echo "š FINAL RESULTS"
|
|
132
|
+
echo "==============="
|
|
133
|
+
echo "Total Tests: $TOTAL"
|
|
134
|
+
echo "ā
Passed: $PASSED"
|
|
135
|
+
echo "ā Failed: $FAILED"
|
|
136
|
+
echo "Success Rate: $(( PASSED * 100 / TOTAL ))%"
|
|
137
|
+
|
|
138
|
+
if [ $FAILED -eq 0 ]; then
|
|
139
|
+
echo ""
|
|
140
|
+
echo "š ALL TESTS PASSED! v1.4.0 is ready for production!"
|
|
141
|
+
exit 0
|
|
142
|
+
else
|
|
143
|
+
echo ""
|
|
144
|
+
echo "ā ļø $FAILED test(s) failed. Please review."
|
|
145
|
+
exit 1
|
|
146
|
+
fi
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
echo "š§Ŗ COMPREHENSIVE TEST OF AIRTABLE MCP v1.3.0"
|
|
4
|
+
echo "============================================"
|
|
5
|
+
echo ""
|
|
6
|
+
|
|
7
|
+
# Configuration
|
|
8
|
+
BASE_ID="appTV04Fyu1Gvbunq"
|
|
9
|
+
TABLE_ID="tblH7TnJxYpNqhQYK"
|
|
10
|
+
CREATED_RECORD_ID=""
|
|
11
|
+
|
|
12
|
+
# Helper function for API calls
|
|
13
|
+
call_mcp() {
|
|
14
|
+
local tool_name=$1
|
|
15
|
+
local args=$2
|
|
16
|
+
curl -s -X POST http://localhost:8010/mcp \
|
|
17
|
+
-H "Content-Type: application/json" \
|
|
18
|
+
-d "{\"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"tools/call\", \"params\": {\"name\": \"$tool_name\", \"arguments\": $args}}"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# Test 1: List tables
|
|
22
|
+
echo "1ļøā£ TEST: list_tables"
|
|
23
|
+
echo "---------------------"
|
|
24
|
+
result=$(call_mcp "list_tables" "{}")
|
|
25
|
+
if echo "$result" | grep -q "Found.*table"; then
|
|
26
|
+
echo "ā
PASSED: Tables listed successfully"
|
|
27
|
+
echo "$result" | python3 -c "import sys, json; print(json.load(sys.stdin)['result']['content'][0]['text'][:200])"
|
|
28
|
+
else
|
|
29
|
+
echo "ā FAILED: Could not list tables"
|
|
30
|
+
fi
|
|
31
|
+
echo ""
|
|
32
|
+
|
|
33
|
+
# Test 2: Create record
|
|
34
|
+
echo "2ļøā£ TEST: create_record"
|
|
35
|
+
echo "----------------------"
|
|
36
|
+
result=$(call_mcp "create_record" "{\"table\": \"$TABLE_ID\", \"fields\": {\"Name\": \"Test Record $(date +%s)\", \"Notes\": \"Created by test suite\", \"Status\": \"Testing\"}}")
|
|
37
|
+
if echo "$result" | grep -q "Successfully created"; then
|
|
38
|
+
echo "ā
PASSED: Record created successfully"
|
|
39
|
+
CREATED_RECORD_ID=$(echo "$result" | grep -o 'rec[a-zA-Z0-9]*' | head -1)
|
|
40
|
+
echo " Created Record ID: $CREATED_RECORD_ID"
|
|
41
|
+
else
|
|
42
|
+
echo "ā FAILED: Could not create record"
|
|
43
|
+
echo "$result" | python3 -m json.tool
|
|
44
|
+
fi
|
|
45
|
+
echo ""
|
|
46
|
+
|
|
47
|
+
# Test 3: Get record
|
|
48
|
+
echo "3ļøā£ TEST: get_record"
|
|
49
|
+
echo "-------------------"
|
|
50
|
+
if [ ! -z "$CREATED_RECORD_ID" ]; then
|
|
51
|
+
result=$(call_mcp "get_record" "{\"table\": \"$TABLE_ID\", \"recordId\": \"$CREATED_RECORD_ID\"}")
|
|
52
|
+
if echo "$result" | grep -q "Record $CREATED_RECORD_ID"; then
|
|
53
|
+
echo "ā
PASSED: Record retrieved successfully"
|
|
54
|
+
echo "$result" | python3 -c "import sys, json; print(json.load(sys.stdin)['result']['content'][0]['text'][:200])"
|
|
55
|
+
else
|
|
56
|
+
echo "ā FAILED: Could not retrieve record"
|
|
57
|
+
fi
|
|
58
|
+
else
|
|
59
|
+
echo "ā ļø SKIPPED: No record ID available"
|
|
60
|
+
fi
|
|
61
|
+
echo ""
|
|
62
|
+
|
|
63
|
+
# Test 4: Update record
|
|
64
|
+
echo "4ļøā£ TEST: update_record"
|
|
65
|
+
echo "----------------------"
|
|
66
|
+
if [ ! -z "$CREATED_RECORD_ID" ]; then
|
|
67
|
+
result=$(call_mcp "update_record" "{\"table\": \"$TABLE_ID\", \"recordId\": \"$CREATED_RECORD_ID\", \"fields\": {\"Status\": \"Updated\", \"Notes\": \"Updated at $(date)\"}}")
|
|
68
|
+
if echo "$result" | grep -q "Successfully updated"; then
|
|
69
|
+
echo "ā
PASSED: Record updated successfully"
|
|
70
|
+
else
|
|
71
|
+
echo "ā FAILED: Could not update record"
|
|
72
|
+
fi
|
|
73
|
+
else
|
|
74
|
+
echo "ā ļø SKIPPED: No record ID available"
|
|
75
|
+
fi
|
|
76
|
+
echo ""
|
|
77
|
+
|
|
78
|
+
# Test 5: Search records
|
|
79
|
+
echo "5ļøā£ TEST: search_records"
|
|
80
|
+
echo "-----------------------"
|
|
81
|
+
result=$(call_mcp "search_records" "{\"table\": \"$TABLE_ID\", \"maxRecords\": 5}")
|
|
82
|
+
if echo "$result" | grep -q "Found.*record\\|No records"; then
|
|
83
|
+
echo "ā
PASSED: Search executed successfully"
|
|
84
|
+
echo "$result" | python3 -c "import sys, json; t=json.load(sys.stdin)['result']['content'][0]['text']; print(t[:200] if len(t)>200 else t)"
|
|
85
|
+
else
|
|
86
|
+
echo "ā FAILED: Search failed"
|
|
87
|
+
fi
|
|
88
|
+
echo ""
|
|
89
|
+
|
|
90
|
+
# Test 6: List records
|
|
91
|
+
echo "6ļøā£ TEST: list_records"
|
|
92
|
+
echo "---------------------"
|
|
93
|
+
result=$(call_mcp "list_records" "{\"table\": \"$TABLE_ID\", \"maxRecords\": 3}")
|
|
94
|
+
if echo "$result" | grep -q "record\\|No records"; then
|
|
95
|
+
echo "ā
PASSED: Records listed successfully"
|
|
96
|
+
count=$(echo "$result" | grep -o "ID: rec" | wc -l)
|
|
97
|
+
echo " Found $count records"
|
|
98
|
+
else
|
|
99
|
+
echo "ā FAILED: Could not list records"
|
|
100
|
+
fi
|
|
101
|
+
echo ""
|
|
102
|
+
|
|
103
|
+
# Test 7: Delete record
|
|
104
|
+
echo "7ļøā£ TEST: delete_record"
|
|
105
|
+
echo "----------------------"
|
|
106
|
+
if [ ! -z "$CREATED_RECORD_ID" ]; then
|
|
107
|
+
result=$(call_mcp "delete_record" "{\"table\": \"$TABLE_ID\", \"recordId\": \"$CREATED_RECORD_ID\"}")
|
|
108
|
+
if echo "$result" | grep -q "Successfully deleted"; then
|
|
109
|
+
echo "ā
PASSED: Record deleted successfully"
|
|
110
|
+
else
|
|
111
|
+
echo "ā FAILED: Could not delete record"
|
|
112
|
+
fi
|
|
113
|
+
else
|
|
114
|
+
echo "ā ļø SKIPPED: No record ID available"
|
|
115
|
+
fi
|
|
116
|
+
echo ""
|
|
117
|
+
|
|
118
|
+
echo "š TEST SUMMARY"
|
|
119
|
+
echo "=============="
|
|
120
|
+
echo "All 7 tools tested with real Airtable API"
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test Script for Enhanced Airtable MCP v1.3.0
|
|
5
|
+
* Tests all CRUD operations and new features
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const http = require('http');
|
|
9
|
+
|
|
10
|
+
// Test configuration - using environment variables
|
|
11
|
+
const MCP_SERVER_URL = 'http://localhost:8010/mcp';
|
|
12
|
+
const TEST_TOKEN = process.env.AIRTABLE_TOKEN || 'YOUR_AIRTABLE_TOKEN';
|
|
13
|
+
const TEST_BASE_ID = process.env.AIRTABLE_BASE_ID || 'YOUR_BASE_ID';
|
|
14
|
+
|
|
15
|
+
if (TEST_TOKEN === 'YOUR_AIRTABLE_TOKEN' || TEST_BASE_ID === 'YOUR_BASE_ID') {
|
|
16
|
+
console.error('ā Error: Please set AIRTABLE_TOKEN and AIRTABLE_BASE_ID environment variables');
|
|
17
|
+
console.error('Example:');
|
|
18
|
+
console.error(' export AIRTABLE_TOKEN=your_token_here');
|
|
19
|
+
console.error(' export AIRTABLE_BASE_ID=your_base_id_here');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Test data
|
|
24
|
+
let createdRecordId = null;
|
|
25
|
+
const testTableName = 'Tasks'; // Change this to your actual table name
|
|
26
|
+
|
|
27
|
+
// Helper function to make MCP requests
|
|
28
|
+
function makeMCPRequest(method, params = {}) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
const postData = JSON.stringify({
|
|
31
|
+
jsonrpc: '2.0',
|
|
32
|
+
id: Date.now(),
|
|
33
|
+
method: method,
|
|
34
|
+
params: params
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const options = {
|
|
38
|
+
hostname: 'localhost',
|
|
39
|
+
port: 8010,
|
|
40
|
+
path: '/mcp',
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: {
|
|
43
|
+
'Content-Type': 'application/json',
|
|
44
|
+
'Content-Length': Buffer.byteLength(postData)
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const req = http.request(options, (res) => {
|
|
49
|
+
let data = '';
|
|
50
|
+
res.on('data', (chunk) => {
|
|
51
|
+
data += chunk;
|
|
52
|
+
});
|
|
53
|
+
res.on('end', () => {
|
|
54
|
+
try {
|
|
55
|
+
const response = JSON.parse(data);
|
|
56
|
+
resolve(response);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
reject(new Error(`Failed to parse response: ${e.message}\nRaw data: ${data}`));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
req.on('error', (e) => {
|
|
64
|
+
reject(new Error(`Request failed: ${e.message}`));
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
req.write(postData);
|
|
68
|
+
req.end();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Test functions
|
|
73
|
+
async function testListTools() {
|
|
74
|
+
console.log('\nš TEST 1: List Available Tools');
|
|
75
|
+
console.log('================================');
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const response = await makeMCPRequest('tools/list');
|
|
79
|
+
|
|
80
|
+
if (response.result && response.result.tools) {
|
|
81
|
+
console.log(`ā
Found ${response.result.tools.length} tools:`);
|
|
82
|
+
response.result.tools.forEach(tool => {
|
|
83
|
+
console.log(` - ${tool.name}: ${tool.description}`);
|
|
84
|
+
});
|
|
85
|
+
return true;
|
|
86
|
+
} else {
|
|
87
|
+
console.error('ā No tools found in response');
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error('ā Error:', error.message);
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function testListTables() {
|
|
97
|
+
console.log('\nš TEST 2: List Tables');
|
|
98
|
+
console.log('======================');
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const response = await makeMCPRequest('tools/call', {
|
|
102
|
+
name: 'list_tables'
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (response.result && response.result.content) {
|
|
106
|
+
console.log('ā
Response:', response.result.content[0].text);
|
|
107
|
+
return true;
|
|
108
|
+
} else {
|
|
109
|
+
console.error('ā No content in response');
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error('ā Error:', error.message);
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function testCreateRecord() {
|
|
119
|
+
console.log('\nā TEST 3: Create Record');
|
|
120
|
+
console.log('========================');
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const response = await makeMCPRequest('tools/call', {
|
|
124
|
+
name: 'create_record',
|
|
125
|
+
arguments: {
|
|
126
|
+
table: testTableName,
|
|
127
|
+
fields: {
|
|
128
|
+
'Name': 'Test Task from MCP v1.3.0',
|
|
129
|
+
'Notes': 'This is a test record created by the enhanced MCP',
|
|
130
|
+
'Status': 'In progress'
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
if (response.result && response.result.content) {
|
|
136
|
+
console.log('ā
Created record:', response.result.content[0].text);
|
|
137
|
+
|
|
138
|
+
// Extract record ID from response
|
|
139
|
+
const match = response.result.content[0].text.match(/Record ID: (rec\w+)/);
|
|
140
|
+
if (match) {
|
|
141
|
+
createdRecordId = match[1];
|
|
142
|
+
console.log(`š Saved record ID: ${createdRecordId}`);
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
} else {
|
|
146
|
+
console.error('ā Failed to create record');
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error('ā Error:', error.message);
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function testGetRecord() {
|
|
156
|
+
console.log('\nš TEST 4: Get Single Record');
|
|
157
|
+
console.log('=============================');
|
|
158
|
+
|
|
159
|
+
if (!createdRecordId) {
|
|
160
|
+
console.log('ā ļø Skipping: No record ID available');
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const response = await makeMCPRequest('tools/call', {
|
|
166
|
+
name: 'get_record',
|
|
167
|
+
arguments: {
|
|
168
|
+
table: testTableName,
|
|
169
|
+
recordId: createdRecordId
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
if (response.result && response.result.content) {
|
|
174
|
+
console.log('ā
Retrieved record:', response.result.content[0].text);
|
|
175
|
+
return true;
|
|
176
|
+
} else {
|
|
177
|
+
console.error('ā Failed to retrieve record');
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error('ā Error:', error.message);
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function testUpdateRecord() {
|
|
187
|
+
console.log('\nāļø TEST 5: Update Record');
|
|
188
|
+
console.log('=========================');
|
|
189
|
+
|
|
190
|
+
if (!createdRecordId) {
|
|
191
|
+
console.log('ā ļø Skipping: No record ID available');
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
const response = await makeMCPRequest('tools/call', {
|
|
197
|
+
name: 'update_record',
|
|
198
|
+
arguments: {
|
|
199
|
+
table: testTableName,
|
|
200
|
+
recordId: createdRecordId,
|
|
201
|
+
fields: {
|
|
202
|
+
'Status': 'Done',
|
|
203
|
+
'Notes': 'Updated by MCP test suite at ' + new Date().toISOString()
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
if (response.result && response.result.content) {
|
|
209
|
+
console.log('ā
Updated record:', response.result.content[0].text);
|
|
210
|
+
return true;
|
|
211
|
+
} else {
|
|
212
|
+
console.error('ā Failed to update record');
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error('ā Error:', error.message);
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function testSearchRecords() {
|
|
222
|
+
console.log('\nš TEST 6: Search Records');
|
|
223
|
+
console.log('=========================');
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
const response = await makeMCPRequest('tools/call', {
|
|
227
|
+
name: 'search_records',
|
|
228
|
+
arguments: {
|
|
229
|
+
table: testTableName,
|
|
230
|
+
filterByFormula: "{Status} = 'Done'",
|
|
231
|
+
maxRecords: 5,
|
|
232
|
+
sort: [{field: 'Name', direction: 'asc'}]
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
if (response.result && response.result.content) {
|
|
237
|
+
console.log('ā
Search results:', response.result.content[0].text);
|
|
238
|
+
return true;
|
|
239
|
+
} else {
|
|
240
|
+
console.error('ā No search results');
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.error('ā Error:', error.message);
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async function testListRecords() {
|
|
250
|
+
console.log('\nš TEST 7: List Records');
|
|
251
|
+
console.log('=======================');
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
const response = await makeMCPRequest('tools/call', {
|
|
255
|
+
name: 'list_records',
|
|
256
|
+
arguments: {
|
|
257
|
+
table: testTableName,
|
|
258
|
+
maxRecords: 3
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
if (response.result && response.result.content) {
|
|
263
|
+
console.log('ā
Listed records:', response.result.content[0].text.substring(0, 200) + '...');
|
|
264
|
+
return true;
|
|
265
|
+
} else {
|
|
266
|
+
console.error('ā Failed to list records');
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
} catch (error) {
|
|
270
|
+
console.error('ā Error:', error.message);
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function testDeleteRecord() {
|
|
276
|
+
console.log('\nšļø TEST 8: Delete Record');
|
|
277
|
+
console.log('=========================');
|
|
278
|
+
|
|
279
|
+
if (!createdRecordId) {
|
|
280
|
+
console.log('ā ļø Skipping: No record ID available');
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
const response = await makeMCPRequest('tools/call', {
|
|
286
|
+
name: 'delete_record',
|
|
287
|
+
arguments: {
|
|
288
|
+
table: testTableName,
|
|
289
|
+
recordId: createdRecordId
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
if (response.result && response.result.content) {
|
|
294
|
+
console.log('ā
Deleted record:', response.result.content[0].text);
|
|
295
|
+
return true;
|
|
296
|
+
} else {
|
|
297
|
+
console.error('ā Failed to delete record');
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
} catch (error) {
|
|
301
|
+
console.error('ā Error:', error.message);
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Main test runner
|
|
307
|
+
async function runTests() {
|
|
308
|
+
console.log('š Enhanced Airtable MCP v1.3.0 Test Suite');
|
|
309
|
+
console.log('==========================================');
|
|
310
|
+
console.log(`š Testing server at: ${MCP_SERVER_URL}`);
|
|
311
|
+
console.log(`š Token: ${TEST_TOKEN.slice(0, 5)}...${TEST_TOKEN.slice(-5)}`);
|
|
312
|
+
console.log(`š¦ Base ID: ${TEST_BASE_ID}`);
|
|
313
|
+
console.log(`š Test table: ${testTableName}`);
|
|
314
|
+
|
|
315
|
+
const tests = [
|
|
316
|
+
{ name: 'List Tools', fn: testListTools },
|
|
317
|
+
{ name: 'List Tables', fn: testListTables },
|
|
318
|
+
{ name: 'Create Record', fn: testCreateRecord },
|
|
319
|
+
{ name: 'Get Record', fn: testGetRecord },
|
|
320
|
+
{ name: 'Update Record', fn: testUpdateRecord },
|
|
321
|
+
{ name: 'Search Records', fn: testSearchRecords },
|
|
322
|
+
{ name: 'List Records', fn: testListRecords },
|
|
323
|
+
{ name: 'Delete Record', fn: testDeleteRecord }
|
|
324
|
+
];
|
|
325
|
+
|
|
326
|
+
let passed = 0;
|
|
327
|
+
let failed = 0;
|
|
328
|
+
|
|
329
|
+
for (const test of tests) {
|
|
330
|
+
try {
|
|
331
|
+
const result = await test.fn();
|
|
332
|
+
if (result) {
|
|
333
|
+
passed++;
|
|
334
|
+
} else {
|
|
335
|
+
failed++;
|
|
336
|
+
}
|
|
337
|
+
} catch (error) {
|
|
338
|
+
console.error(`ā Test "${test.name}" crashed:`, error.message);
|
|
339
|
+
failed++;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Small delay between tests
|
|
343
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
console.log('\n' + '='.repeat(50));
|
|
347
|
+
console.log('š TEST RESULTS SUMMARY');
|
|
348
|
+
console.log('='.repeat(50));
|
|
349
|
+
console.log(`ā
Passed: ${passed}/${tests.length}`);
|
|
350
|
+
console.log(`ā Failed: ${failed}/${tests.length}`);
|
|
351
|
+
console.log(`š Success Rate: ${Math.round((passed/tests.length) * 100)}%`);
|
|
352
|
+
|
|
353
|
+
if (failed === 0) {
|
|
354
|
+
console.log('\nš All tests passed! The enhanced MCP is working perfectly!');
|
|
355
|
+
} else {
|
|
356
|
+
console.log('\nā ļø Some tests failed. Please check the errors above.');
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
process.exit(failed === 0 ? 0 : 1);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Check if server is running
|
|
363
|
+
async function checkServer() {
|
|
364
|
+
try {
|
|
365
|
+
await makeMCPRequest('tools/list');
|
|
366
|
+
return true;
|
|
367
|
+
} catch (error) {
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Main
|
|
373
|
+
(async () => {
|
|
374
|
+
console.log('š Checking if MCP server is running...');
|
|
375
|
+
|
|
376
|
+
const serverRunning = await checkServer();
|
|
377
|
+
|
|
378
|
+
if (!serverRunning) {
|
|
379
|
+
console.error('ā MCP server is not running at ' + MCP_SERVER_URL);
|
|
380
|
+
console.error('\nPlease start the server first:');
|
|
381
|
+
console.error(' node airtable_enhanced.js --token YOUR_TOKEN --base YOUR_BASE_ID');
|
|
382
|
+
process.exit(1);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
console.log('ā
Server is running!\n');
|
|
386
|
+
|
|
387
|
+
// Run tests
|
|
388
|
+
await runTests();
|
|
389
|
+
})();
|