@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.
- package/README.md +209 -334
- package/bin/airtable-mcp.js +12 -32
- package/dist/typescript/airtable-mcp-server.js +77 -0
- package/dist/typescript/airtable-mcp-server.js.map +1 -0
- package/dist/typescript/app/airtable-client.js +327 -0
- package/dist/typescript/app/airtable-client.js.map +1 -0
- package/dist/typescript/app/config.js +151 -0
- package/dist/typescript/app/config.js.map +1 -0
- package/dist/typescript/app/context.js +3 -0
- package/dist/typescript/app/context.js.map +1 -0
- package/dist/typescript/app/exceptions.js +85 -0
- package/dist/typescript/app/exceptions.js.map +1 -0
- package/dist/typescript/app/governance.js +58 -0
- package/dist/typescript/app/governance.js.map +1 -0
- package/dist/typescript/app/logger.js +47 -0
- package/dist/typescript/app/logger.js.map +1 -0
- package/dist/typescript/app/rateLimiter.js +37 -0
- package/dist/typescript/app/rateLimiter.js.map +1 -0
- package/dist/typescript/app/sanitize.js +95 -0
- package/dist/typescript/app/sanitize.js.map +1 -0
- package/dist/typescript/app/tools/create.js +55 -0
- package/dist/typescript/app/tools/create.js.map +1 -0
- package/dist/typescript/app/tools/describe.js +190 -0
- package/dist/typescript/app/tools/describe.js.map +1 -0
- package/dist/typescript/app/tools/handleError.js +205 -0
- package/dist/typescript/app/tools/handleError.js.map +1 -0
- package/dist/typescript/app/tools/index.js +24 -0
- package/dist/typescript/app/tools/index.js.map +1 -0
- package/dist/typescript/app/tools/listBases.js +47 -0
- package/dist/typescript/app/tools/listBases.js.map +1 -0
- package/dist/typescript/app/tools/listExceptions.js +16 -0
- package/dist/typescript/app/tools/listExceptions.js.map +1 -0
- package/dist/typescript/app/tools/listGovernance.js +15 -0
- package/dist/typescript/app/tools/listGovernance.js.map +1 -0
- package/dist/typescript/app/tools/query.js +133 -0
- package/dist/typescript/app/tools/query.js.map +1 -0
- package/dist/typescript/app/tools/response.js +21 -0
- package/dist/typescript/app/tools/response.js.map +1 -0
- package/dist/typescript/app/tools/update.js +57 -0
- package/dist/typescript/app/tools/update.js.map +1 -0
- package/dist/typescript/app/tools/upsert.js +66 -0
- package/dist/typescript/app/tools/upsert.js.map +1 -0
- package/dist/typescript/app/tools/webhooks.js +45 -0
- package/dist/typescript/app/tools/webhooks.js.map +1 -0
- package/dist/typescript/app/types.js +291 -0
- package/dist/typescript/app/types.js.map +1 -0
- package/dist/typescript/app/validateApiKey.js +75 -0
- package/dist/typescript/app/validateApiKey.js.map +1 -0
- package/dist/typescript/errors.js +75 -0
- package/dist/typescript/errors.js.map +1 -0
- package/dist/typescript/index.js +27 -0
- package/dist/typescript/index.js.map +1 -0
- package/package.json +49 -31
- package/tsconfig.json +10 -4
- package/types/typescript/airtable-mcp-server.d.ts +2 -0
- package/types/typescript/app/airtable-client.d.ts +50 -0
- package/types/typescript/app/config.d.ts +17 -0
- package/types/typescript/app/context.d.ts +12 -0
- package/types/typescript/app/exceptions.d.ts +12 -0
- package/types/typescript/app/governance.d.ts +18 -0
- package/types/typescript/app/logger.d.ts +13 -0
- package/types/typescript/app/rateLimiter.d.ts +13 -0
- package/types/typescript/app/sanitize.d.ts +50 -0
- package/types/typescript/app/tools/create.d.ts +3 -0
- package/types/typescript/app/tools/describe.d.ts +3 -0
- package/types/typescript/app/tools/handleError.d.ts +8 -0
- package/types/typescript/app/tools/index.d.ts +3 -0
- package/types/typescript/app/tools/listBases.d.ts +13 -0
- package/types/typescript/app/tools/listExceptions.d.ts +3 -0
- package/types/typescript/app/tools/listGovernance.d.ts +3 -0
- package/types/typescript/app/tools/query.d.ts +3 -0
- package/types/typescript/app/tools/response.d.ts +20 -0
- package/types/typescript/app/tools/update.d.ts +3 -0
- package/types/typescript/app/tools/upsert.d.ts +3 -0
- package/types/typescript/app/tools/webhooks.d.ts +3 -0
- package/types/typescript/app/types.d.ts +318 -0
- package/types/typescript/app/validateApiKey.d.ts +25 -0
- package/types/typescript/errors.d.ts +57 -0
- package/types/typescript/index.d.ts +10 -0
- package/types/typescript/prompt-templates.d.ts +5 -0
- package/types/typescript/tools-schemas.d.ts +5 -0
- package/airtable_simple.js +0 -1561
- package/airtable_simple_production.js +0 -1564
- package/dist/airtable-mcp-server.js +0 -660
- package/dist/airtable-mcp-server.js.map +0 -1
- package/dist/test-suite.js +0 -421
- package/dist/test-suite.js.map +0 -1
- package/examples/airtable-crud-example.js +0 -203
- package/examples/building-mcp.md +0 -6666
- package/examples/claude_config.json +0 -4
- package/examples/claude_simple_config.json +0 -7
- package/examples/env-demo.js +0 -172
- package/examples/example-tasks-update.json +0 -23
- package/examples/example-tasks.json +0 -26
- package/examples/example_usage.md +0 -124
- package/examples/python_debug_patch.txt +0 -27
- package/examples/sample-transform.js +0 -76
- package/examples/typescript/advanced-ai-prompts.ts +0 -447
- package/examples/typescript/basic-usage.ts +0 -174
- package/examples/typescript/claude-desktop-config.json +0 -29
- package/examples/windsurf_mcp_config.json +0 -17
- package/types/ai-prompts.d.ts +0 -321
- package/types/airtable-mcp-server.d.ts +0 -52
- package/types/index.d.ts +0 -357
- package/types/tools.d.ts +0 -514
- /package/types/{test-suite.d.ts → typescript/test-suite.d.ts} +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExceptionStore = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
function mapCategory(code) {
|
|
6
|
+
switch (code) {
|
|
7
|
+
case 'RateLimited':
|
|
8
|
+
return 'rate_limit';
|
|
9
|
+
case 'ValidationError':
|
|
10
|
+
return 'validation';
|
|
11
|
+
case 'AuthError':
|
|
12
|
+
return 'auth';
|
|
13
|
+
case 'ConflictError':
|
|
14
|
+
return 'conflict';
|
|
15
|
+
case 'GovernanceError':
|
|
16
|
+
return 'schema_drift';
|
|
17
|
+
default:
|
|
18
|
+
return 'other';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function mapSeverity(code) {
|
|
22
|
+
switch (code) {
|
|
23
|
+
case 'RateLimited':
|
|
24
|
+
case 'AuthError':
|
|
25
|
+
case 'ConflictError':
|
|
26
|
+
case 'GovernanceError':
|
|
27
|
+
return 'error';
|
|
28
|
+
case 'ValidationError':
|
|
29
|
+
return 'warning';
|
|
30
|
+
default:
|
|
31
|
+
return 'error';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
class ExceptionStore {
|
|
35
|
+
constructor(capacity, logger) {
|
|
36
|
+
this.items = [];
|
|
37
|
+
this.capacity = capacity;
|
|
38
|
+
this.logger = logger.child({ component: 'exception_store' });
|
|
39
|
+
}
|
|
40
|
+
record(error, summary, details, proposedFix) {
|
|
41
|
+
const item = {
|
|
42
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
43
|
+
timestamp: new Date().toISOString(),
|
|
44
|
+
severity: mapSeverity(error.code),
|
|
45
|
+
category: mapCategory(error.code),
|
|
46
|
+
summary,
|
|
47
|
+
details,
|
|
48
|
+
proposedFix
|
|
49
|
+
};
|
|
50
|
+
this.items.unshift(item);
|
|
51
|
+
if (this.items.length > this.capacity) {
|
|
52
|
+
this.items.pop();
|
|
53
|
+
}
|
|
54
|
+
this.logger.debug('Recorded exception', { code: error.code });
|
|
55
|
+
}
|
|
56
|
+
list(params) {
|
|
57
|
+
const limit = params.limit ?? 100;
|
|
58
|
+
const cursorIndex = this.parseCursor(params.cursor);
|
|
59
|
+
let filtered = this.items;
|
|
60
|
+
if (params.since) {
|
|
61
|
+
filtered = filtered.filter((item) => item.timestamp > params.since);
|
|
62
|
+
}
|
|
63
|
+
if (params.severity) {
|
|
64
|
+
filtered = filtered.filter((item) => item.severity === params.severity);
|
|
65
|
+
}
|
|
66
|
+
const slice = filtered.slice(cursorIndex, cursorIndex + limit);
|
|
67
|
+
const nextCursor = cursorIndex + limit < filtered.length ? String(cursorIndex + limit) : undefined;
|
|
68
|
+
return {
|
|
69
|
+
items: slice,
|
|
70
|
+
cursor: nextCursor
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
parseCursor(cursor) {
|
|
74
|
+
if (!cursor) {
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
const parsed = Number.parseInt(cursor, 10);
|
|
78
|
+
if (Number.isNaN(parsed) || parsed < 0) {
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
return parsed;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.ExceptionStore = ExceptionStore;
|
|
85
|
+
//# sourceMappingURL=exceptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exceptions.js","sourceRoot":"","sources":["../../../src/typescript/app/exceptions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GovernanceService = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
class GovernanceService {
|
|
6
|
+
constructor(snapshot) {
|
|
7
|
+
this.snapshot = snapshot;
|
|
8
|
+
this.tablesByBase = this.buildTableIndex(snapshot);
|
|
9
|
+
}
|
|
10
|
+
ensureBaseAllowed(baseId) {
|
|
11
|
+
// If allowedBases is empty, allow all bases (user will use list_bases to discover)
|
|
12
|
+
if (this.snapshot.allowedBases.length > 0 && !this.snapshot.allowedBases.includes(baseId)) {
|
|
13
|
+
throw new errors_1.GovernanceError(`Base ${baseId} is not in the allow-list`, {
|
|
14
|
+
context: { baseId, governanceRule: 'allowedBases' }
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
ensureOperationAllowed(operation) {
|
|
19
|
+
if (!this.snapshot.allowedOperations.includes(operation)) {
|
|
20
|
+
throw new errors_1.GovernanceError(`Operation ${operation} is not permitted`, {
|
|
21
|
+
context: { governanceRule: 'allowedOperations' }
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
ensureTableAllowed(baseId, table) {
|
|
26
|
+
if (!this.isTableAllowed(baseId, table)) {
|
|
27
|
+
throw new errors_1.GovernanceError(`Table ${table} is not allowed in base ${baseId}`, {
|
|
28
|
+
context: { baseId, table, governanceRule: 'allowedTables' }
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
listPiiPolicies(baseId, table) {
|
|
33
|
+
return this.snapshot.piiFields
|
|
34
|
+
?.filter((field) => field.baseId === baseId && field.table === table)
|
|
35
|
+
.map((field) => ({ field: field.field, policy: field.policy })) ?? [];
|
|
36
|
+
}
|
|
37
|
+
getSnapshot() {
|
|
38
|
+
return this.snapshot;
|
|
39
|
+
}
|
|
40
|
+
isTableAllowed(baseId, table) {
|
|
41
|
+
const allowedTables = this.tablesByBase.get(baseId);
|
|
42
|
+
if (!allowedTables || allowedTables.size === 0) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
return allowedTables.has(table);
|
|
46
|
+
}
|
|
47
|
+
buildTableIndex(snapshot) {
|
|
48
|
+
const map = new Map();
|
|
49
|
+
for (const item of snapshot.allowedTables ?? []) {
|
|
50
|
+
const baseTables = map.get(item.baseId) ?? new Set();
|
|
51
|
+
baseTables.add(item.table);
|
|
52
|
+
map.set(item.baseId, baseTables);
|
|
53
|
+
}
|
|
54
|
+
return map;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.GovernanceService = GovernanceService;
|
|
58
|
+
//# sourceMappingURL=governance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"governance.js","sourceRoot":"","sources":["../../../src/typescript/app/governance.ts"],"names":[],"mappings":";;;AACA,sCAA4C;AAI5C,MAAa,iBAAiB;IAI5B,YAAY,QAA4B;QACtC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC;IAED,iBAAiB,CAAC,MAAc;QAC9B,mFAAmF;QACnF,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1F,MAAM,IAAI,wBAAe,CAAC,QAAQ,MAAM,2BAA2B,EAAE;gBACnE,OAAO,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE;aACpD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,sBAAsB,CAAC,SAAoB;QACzC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YACzD,MAAM,IAAI,wBAAe,CAAC,aAAa,SAAS,mBAAmB,EAAE;gBACnE,OAAO,EAAE,EAAE,cAAc,EAAE,mBAAmB,EAAE;aACjD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,kBAAkB,CAAC,MAAc,EAAE,KAAa;QAC9C,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;YACxC,MAAM,IAAI,wBAAe,CAAC,SAAS,KAAK,2BAA2B,MAAM,EAAE,EAAE;gBAC3E,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE;aAC5D,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,eAAe,CAAC,MAAc,EAAE,KAAa;QAC3C,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS;YAC5B,EAAE,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC;aACpE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1E,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,cAAc,CAAC,MAAc,EAAE,KAAa;QAC1C,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC/C,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAEO,eAAe,CAAC,QAA4B;QAClD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAuB,CAAC;QAC3C,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,aAAa,IAAI,EAAE,EAAE,CAAC;YAChD,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;YAC7D,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC3B,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;CACF;AA7DD,8CA6DC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Logger = void 0;
|
|
4
|
+
const LEVEL_ORDER = {
|
|
5
|
+
error: 0,
|
|
6
|
+
warn: 1,
|
|
7
|
+
info: 2,
|
|
8
|
+
debug: 3
|
|
9
|
+
};
|
|
10
|
+
class Logger {
|
|
11
|
+
constructor(level, context = {}) {
|
|
12
|
+
this.level = level;
|
|
13
|
+
this.context = context;
|
|
14
|
+
}
|
|
15
|
+
child(context) {
|
|
16
|
+
return new Logger(this.level, { ...this.context, ...context });
|
|
17
|
+
}
|
|
18
|
+
error(message, metadata = {}) {
|
|
19
|
+
this.log('error', message, metadata);
|
|
20
|
+
}
|
|
21
|
+
warn(message, metadata = {}) {
|
|
22
|
+
this.log('warn', message, metadata);
|
|
23
|
+
}
|
|
24
|
+
info(message, metadata = {}) {
|
|
25
|
+
this.log('info', message, metadata);
|
|
26
|
+
}
|
|
27
|
+
debug(message, metadata = {}) {
|
|
28
|
+
this.log('debug', message, metadata);
|
|
29
|
+
}
|
|
30
|
+
log(level, message, metadata) {
|
|
31
|
+
if (LEVEL_ORDER[level] > LEVEL_ORDER[this.level]) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const timestamp = new Date().toISOString();
|
|
35
|
+
const output = {
|
|
36
|
+
timestamp,
|
|
37
|
+
level,
|
|
38
|
+
message,
|
|
39
|
+
...this.context,
|
|
40
|
+
...(Object.keys(metadata).length > 0 ? { metadata } : {})
|
|
41
|
+
};
|
|
42
|
+
// Write logs to stderr so we don't corrupt the MCP stdio protocol stream.
|
|
43
|
+
process.stderr.write(`${JSON.stringify(output)}\n`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.Logger = Logger;
|
|
47
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../src/typescript/app/logger.ts"],"names":[],"mappings":";;;AAEA,MAAM,WAAW,GAA6B;IAC5C,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AAIF,MAAa,MAAM;IAIjB,YAAY,KAAe,EAAE,UAAuB,EAAE;QACpD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAoB;QACxB,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,WAAwB,EAAE;QAC/C,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,WAAwB,EAAE;QAC9C,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,WAAwB,EAAE;QAC9C,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,WAAwB,EAAE;QAC/C,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IACvC,CAAC;IAEO,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,QAAqB;QACjE,IAAI,WAAW,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,OAAO;QACT,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG;YACb,SAAS;YACT,KAAK;YACL,OAAO;YACP,GAAG,IAAI,CAAC,OAAO;YACf,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1D,CAAC;QAEF,0EAA0E;QAC1E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtD,CAAC;CACF;AA9CD,wBA8CC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RateLimiter = void 0;
|
|
4
|
+
const promises_1 = require("node:timers/promises");
|
|
5
|
+
/**
|
|
6
|
+
* Lightweight token-based rate limiter to enforce Airtable quotas.
|
|
7
|
+
* Maintains per-key queues to preserve ordering and fairness.
|
|
8
|
+
*/
|
|
9
|
+
class RateLimiter {
|
|
10
|
+
constructor({ maxRequestsPerSecond }) {
|
|
11
|
+
this.lockByKey = new Map();
|
|
12
|
+
this.nextAvailableByKey = new Map();
|
|
13
|
+
if (maxRequestsPerSecond <= 0) {
|
|
14
|
+
throw new Error('maxRequestsPerSecond must be greater than zero');
|
|
15
|
+
}
|
|
16
|
+
this.minIntervalMs = Math.ceil(1000 / maxRequestsPerSecond);
|
|
17
|
+
}
|
|
18
|
+
async schedule(key) {
|
|
19
|
+
const previous = this.lockByKey.get(key) ?? Promise.resolve();
|
|
20
|
+
let release = () => undefined;
|
|
21
|
+
const current = new Promise((resolve) => {
|
|
22
|
+
release = resolve;
|
|
23
|
+
});
|
|
24
|
+
this.lockByKey.set(key, previous.then(() => current));
|
|
25
|
+
await previous;
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
const availableAt = this.nextAvailableByKey.get(key) ?? now;
|
|
28
|
+
const waitMs = Math.max(availableAt - now, 0);
|
|
29
|
+
if (waitMs > 0) {
|
|
30
|
+
await (0, promises_1.setTimeout)(waitMs);
|
|
31
|
+
}
|
|
32
|
+
this.nextAvailableByKey.set(key, Date.now() + this.minIntervalMs);
|
|
33
|
+
release();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.RateLimiter = RateLimiter;
|
|
37
|
+
//# sourceMappingURL=rateLimiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimiter.js","sourceRoot":"","sources":["../../../src/typescript/app/rateLimiter.ts"],"names":[],"mappings":";;;AAAA,mDAA2D;AAE3D;;;GAGG;AACH,MAAa,WAAW;IAKtB,YAAY,EAAE,oBAAoB,EAAoC;QAHrD,cAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;QAC7C,uBAAkB,GAAG,IAAI,GAAG,EAAkB,CAAC;QAG9D,IAAI,oBAAoB,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACpE,CAAC;QACD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC,CAAC;IAC9D,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,GAAW;QACxB,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAC9D,IAAI,OAAO,GAAe,GAAG,EAAE,CAAC,SAAS,CAAC;QAC1C,MAAM,OAAO,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAC5C,OAAO,GAAG,OAAO,CAAC;QACpB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,CAAC,GAAG,CAChB,GAAG,EACH,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAC7B,CAAC;QAEF,MAAM,QAAQ,CAAC;QAEf,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,WAAW,GAAG,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;QAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC;QAC9C,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YACf,MAAM,IAAA,qBAAK,EAAC,MAAM,CAAC,CAAC;QACtB,CAAC;QAED,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;QAClE,OAAO,EAAE,CAAC;IACZ,CAAC;CACF;AApCD,kCAoCC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Sanitization utilities for Airtable formula strings.
|
|
4
|
+
*
|
|
5
|
+
* Prevents formula injection attacks by escaping user-provided
|
|
6
|
+
* strings before they're interpolated into Airtable formulas.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.escapeFormulaString = escapeFormulaString;
|
|
10
|
+
exports.validateFormula = validateFormula;
|
|
11
|
+
exports.buildSafeFindFormula = buildSafeFindFormula;
|
|
12
|
+
exports.buildSafeEqualityFormula = buildSafeEqualityFormula;
|
|
13
|
+
/**
|
|
14
|
+
* Escapes a string for safe use within Airtable formula string literals.
|
|
15
|
+
*
|
|
16
|
+
* Airtable formulas use double quotes for strings. This function escapes:
|
|
17
|
+
* - Double quotes (") -> \"
|
|
18
|
+
* - Backslashes (\) -> \\
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // Safe interpolation in formula:
|
|
22
|
+
* const formula = `Name = "${escapeFormulaString(userInput)}"`;
|
|
23
|
+
*
|
|
24
|
+
* @param input - The user-provided string to escape
|
|
25
|
+
* @returns The escaped string safe for use in formulas
|
|
26
|
+
*/
|
|
27
|
+
function escapeFormulaString(input) {
|
|
28
|
+
if (!input)
|
|
29
|
+
return '';
|
|
30
|
+
// Escape backslashes first, then quotes
|
|
31
|
+
return input
|
|
32
|
+
.replace(/\\/g, '\\\\')
|
|
33
|
+
.replace(/"/g, '\\"');
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Validates that a formula string doesn't contain obvious injection attempts.
|
|
37
|
+
*
|
|
38
|
+
* This is a heuristic check - not foolproof, but catches common patterns.
|
|
39
|
+
* Should be used in addition to escapeFormulaString, not as a replacement.
|
|
40
|
+
*
|
|
41
|
+
* @param formula - The complete formula string to validate
|
|
42
|
+
* @returns Object with isValid flag and optional warning message
|
|
43
|
+
*/
|
|
44
|
+
function validateFormula(formula) {
|
|
45
|
+
if (!formula) {
|
|
46
|
+
return { isValid: true };
|
|
47
|
+
}
|
|
48
|
+
// Check for unbalanced quotes (simple heuristic)
|
|
49
|
+
const quoteCount = (formula.match(/(?<!\\)"/g) || []).length;
|
|
50
|
+
if (quoteCount % 2 !== 0) {
|
|
51
|
+
return {
|
|
52
|
+
isValid: false,
|
|
53
|
+
warning: 'Formula contains unbalanced quotes'
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
// Check for suspicious patterns that might indicate injection
|
|
57
|
+
const suspiciousPatterns = [
|
|
58
|
+
/"\s*\)\s*,/, // Closing quote followed by ) and comma - potential function breakout
|
|
59
|
+
/"\s*&\s*"/, // String concatenation that might be injection
|
|
60
|
+
];
|
|
61
|
+
for (const pattern of suspiciousPatterns) {
|
|
62
|
+
if (pattern.test(formula)) {
|
|
63
|
+
return {
|
|
64
|
+
isValid: false,
|
|
65
|
+
warning: 'Formula contains suspicious patterns'
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { isValid: true };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Builds a safe FIND formula for searching text in a field.
|
|
73
|
+
*
|
|
74
|
+
* @param searchTerm - The user-provided search term
|
|
75
|
+
* @param fieldName - The field name to search in
|
|
76
|
+
* @returns A safe FIND formula string
|
|
77
|
+
*/
|
|
78
|
+
function buildSafeFindFormula(searchTerm, fieldName) {
|
|
79
|
+
const escapedTerm = escapeFormulaString(searchTerm);
|
|
80
|
+
const escapedField = escapeFormulaString(fieldName);
|
|
81
|
+
return `FIND("${escapedTerm}", {${escapedField}})`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Builds a safe equality comparison formula.
|
|
85
|
+
*
|
|
86
|
+
* @param fieldName - The field name to compare
|
|
87
|
+
* @param value - The user-provided value to compare against
|
|
88
|
+
* @returns A safe equality formula string
|
|
89
|
+
*/
|
|
90
|
+
function buildSafeEqualityFormula(fieldName, value) {
|
|
91
|
+
const escapedField = escapeFormulaString(fieldName);
|
|
92
|
+
const escapedValue = escapeFormulaString(value);
|
|
93
|
+
return `{${escapedField}} = "${escapedValue}"`;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=sanitize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../../../src/typescript/app/sanitize.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAgBH,kDAOC;AAWD,0CA8BC;AASD,oDAIC;AASD,4DAIC;AAxFD;;;;;;;;;;;;;GAaG;AACH,SAAgB,mBAAmB,CAAC,KAAa;IAC/C,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAEtB,wCAAwC;IACxC,OAAO,KAAK;SACT,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,eAAe,CAAC,OAAe;IAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED,iDAAiD;IACjD,MAAM,UAAU,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IAC7D,IAAI,UAAU,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;YACL,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,oCAAoC;SAC9C,CAAC;IACJ,CAAC;IAED,8DAA8D;IAC9D,MAAM,kBAAkB,GAAG;QACzB,YAAY,EAAG,sEAAsE;QACrF,WAAW,EAAI,+CAA+C;KAC/D,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1B,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,sCAAsC;aAChD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,oBAAoB,CAAC,UAAkB,EAAE,SAAiB;IACxE,MAAM,WAAW,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IACpD,MAAM,YAAY,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IACpD,OAAO,SAAS,WAAW,OAAO,YAAY,IAAI,CAAC;AACrD,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,wBAAwB,CAAC,SAAiB,EAAE,KAAa;IACvE,MAAM,YAAY,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IACpD,MAAM,YAAY,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAChD,OAAO,IAAI,YAAY,QAAQ,YAAY,GAAG,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerCreateTool = registerCreateTool;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
const handleError_1 = require("./handleError");
|
|
6
|
+
const response_1 = require("./response");
|
|
7
|
+
function chunk(arr, size) {
|
|
8
|
+
const out = [];
|
|
9
|
+
for (let i = 0; i < arr.length; i += size)
|
|
10
|
+
out.push(arr.slice(i, i + size));
|
|
11
|
+
return out;
|
|
12
|
+
}
|
|
13
|
+
function registerCreateTool(server, ctx) {
|
|
14
|
+
server.registerTool('create', {
|
|
15
|
+
description: 'Create Airtable records (requires diff-before-write via dryRun first).',
|
|
16
|
+
inputSchema: types_1.createInputSchema.shape,
|
|
17
|
+
outputSchema: types_1.createOutputSchema.shape
|
|
18
|
+
}, async (raw) => {
|
|
19
|
+
try {
|
|
20
|
+
const args = types_1.createInputSchema.parse(raw);
|
|
21
|
+
ctx.governance.ensureOperationAllowed('create');
|
|
22
|
+
ctx.governance.ensureBaseAllowed(args.baseId);
|
|
23
|
+
ctx.governance.ensureTableAllowed(args.baseId, args.table);
|
|
24
|
+
const logger = ctx.logger.child({ tool: 'create', baseId: args.baseId, table: args.table });
|
|
25
|
+
if (args.dryRun) {
|
|
26
|
+
const structuredContent = {
|
|
27
|
+
diff: { added: args.records.length, updated: 0, unchanged: 0 },
|
|
28
|
+
dryRun: true,
|
|
29
|
+
records: args.records.map((r) => ({ id: 'pending', fields: r.fields }))
|
|
30
|
+
};
|
|
31
|
+
return (0, response_1.createToolResponse)(structuredContent);
|
|
32
|
+
}
|
|
33
|
+
const chunks = chunk(args.records, 10);
|
|
34
|
+
const aggregated = [];
|
|
35
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
36
|
+
const body = { records: chunks[i], typecast: args.typecast ?? false };
|
|
37
|
+
const headerKey = args.idempotencyKey ? `${args.idempotencyKey}:${i}` : undefined;
|
|
38
|
+
const response = await ctx.airtable.createRecords(args.baseId, args.table, body, headerKey);
|
|
39
|
+
if (Array.isArray(response?.records))
|
|
40
|
+
aggregated.push(...response.records);
|
|
41
|
+
}
|
|
42
|
+
const structuredContent = {
|
|
43
|
+
diff: { added: aggregated.length, updated: 0, unchanged: 0 },
|
|
44
|
+
records: aggregated.map((r) => ({ id: String(r.id), fields: r.fields || {} })),
|
|
45
|
+
dryRun: false
|
|
46
|
+
};
|
|
47
|
+
logger.info('Create completed', { added: aggregated.length });
|
|
48
|
+
return (0, response_1.createToolResponse)(structuredContent);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return (0, handleError_1.handleToolError)('create', error, ctx);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../../../src/typescript/app/tools/create.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerDescribeTool = registerDescribeTool;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
const errors_1 = require("../../errors");
|
|
6
|
+
const handleError_1 = require("./handleError");
|
|
7
|
+
const response_1 = require("./response");
|
|
8
|
+
/**
|
|
9
|
+
* Normalize field based on detail level:
|
|
10
|
+
* - identifiersOnly: only id and name
|
|
11
|
+
* - full: all details including type, description, options
|
|
12
|
+
*/
|
|
13
|
+
function normalizeField(raw, detailLevel) {
|
|
14
|
+
const source = raw;
|
|
15
|
+
const field = {
|
|
16
|
+
id: String(source?.id ?? ''),
|
|
17
|
+
name: String(source?.name ?? ''),
|
|
18
|
+
type: detailLevel === 'full' ? String(source?.type ?? '') : ''
|
|
19
|
+
};
|
|
20
|
+
// Only include full details in 'full' mode
|
|
21
|
+
if (detailLevel === 'full') {
|
|
22
|
+
if (source?.description && typeof source.description === 'string') {
|
|
23
|
+
field.description = source.description;
|
|
24
|
+
}
|
|
25
|
+
if (source?.options && typeof source.options === 'object') {
|
|
26
|
+
field.options = source.options;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Remove empty type for identifiersOnly
|
|
30
|
+
if (detailLevel === 'identifiersOnly') {
|
|
31
|
+
delete field.type;
|
|
32
|
+
}
|
|
33
|
+
return field;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Normalize view based on detail level:
|
|
37
|
+
* - identifiersOnly: only id and name
|
|
38
|
+
* - full: includes type
|
|
39
|
+
*/
|
|
40
|
+
function normalizeView(raw, detailLevel) {
|
|
41
|
+
const source = raw;
|
|
42
|
+
const view = {
|
|
43
|
+
id: String(source?.id ?? ''),
|
|
44
|
+
name: String(source?.name ?? '')
|
|
45
|
+
};
|
|
46
|
+
if (detailLevel === 'full' && source?.type && typeof source.type === 'string') {
|
|
47
|
+
view.type = source.type;
|
|
48
|
+
}
|
|
49
|
+
return view;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Normalize table based on detail level:
|
|
53
|
+
* - tableIdentifiersOnly: only id and name
|
|
54
|
+
* - identifiersOnly: id, name, and field/view identifiers
|
|
55
|
+
* - full: complete details
|
|
56
|
+
*/
|
|
57
|
+
function normalizeTable(raw, options) {
|
|
58
|
+
const { detailLevel, includeFields, includeViews } = options;
|
|
59
|
+
const source = raw;
|
|
60
|
+
const table = {
|
|
61
|
+
id: String(source?.id ?? ''),
|
|
62
|
+
name: String(source?.name ?? '')
|
|
63
|
+
};
|
|
64
|
+
// tableIdentifiersOnly: stop here
|
|
65
|
+
if (detailLevel === 'tableIdentifiersOnly') {
|
|
66
|
+
return table;
|
|
67
|
+
}
|
|
68
|
+
// identifiersOnly and full: include primaryFieldId
|
|
69
|
+
if (source?.primaryFieldId && typeof source.primaryFieldId === 'string') {
|
|
70
|
+
table.primaryFieldId = source.primaryFieldId;
|
|
71
|
+
}
|
|
72
|
+
// Include fields based on settings
|
|
73
|
+
if (includeFields && Array.isArray(source?.fields)) {
|
|
74
|
+
table.fields = source.fields.map((field) => normalizeField(field, detailLevel));
|
|
75
|
+
}
|
|
76
|
+
// Include views based on settings
|
|
77
|
+
if (includeViews && Array.isArray(source?.views)) {
|
|
78
|
+
table.views = source.views.map((view) => normalizeView(view, detailLevel));
|
|
79
|
+
}
|
|
80
|
+
return table;
|
|
81
|
+
}
|
|
82
|
+
function registerDescribeTool(server, ctx) {
|
|
83
|
+
server.registerTool('describe', {
|
|
84
|
+
description: `Describe Airtable base or table schema.
|
|
85
|
+
|
|
86
|
+
Use detailLevel to optimize context usage:
|
|
87
|
+
- tableIdentifiersOnly: Only table IDs and names (minimal)
|
|
88
|
+
- identifiersOnly: Table, field, and view IDs and names
|
|
89
|
+
- full: Complete details including field types and options (default)`,
|
|
90
|
+
inputSchema: types_1.describeInputShape,
|
|
91
|
+
outputSchema: types_1.describeOutputSchema.shape
|
|
92
|
+
}, async (args, _extra) => {
|
|
93
|
+
try {
|
|
94
|
+
const input = types_1.describeInputSchema.parse(args);
|
|
95
|
+
ctx.governance.ensureOperationAllowed('describe');
|
|
96
|
+
ctx.governance.ensureBaseAllowed(input.baseId);
|
|
97
|
+
// Determine detail level and field/view inclusion
|
|
98
|
+
const detailLevel = input.detailLevel ?? 'full';
|
|
99
|
+
// For backward compatibility, respect includeFields/includeViews
|
|
100
|
+
// but detailLevel takes precedence for tableIdentifiersOnly
|
|
101
|
+
const includeFields = detailLevel !== 'tableIdentifiersOnly' && (input.includeFields ?? true);
|
|
102
|
+
const includeViews = detailLevel !== 'tableIdentifiersOnly' && (input.includeViews ?? false);
|
|
103
|
+
const normalizeOptions = {
|
|
104
|
+
detailLevel,
|
|
105
|
+
includeFields,
|
|
106
|
+
includeViews
|
|
107
|
+
};
|
|
108
|
+
const logger = ctx.logger.child({
|
|
109
|
+
tool: 'describe',
|
|
110
|
+
baseId: input.baseId,
|
|
111
|
+
scope: input.scope,
|
|
112
|
+
detailLevel
|
|
113
|
+
});
|
|
114
|
+
const [baseInfo, tableInfo] = await Promise.all([
|
|
115
|
+
ctx.airtable.getBase(input.baseId),
|
|
116
|
+
ctx.airtable.listTables(input.baseId)
|
|
117
|
+
]);
|
|
118
|
+
const baseName = typeof baseInfo?.name === 'string'
|
|
119
|
+
? String(baseInfo.name)
|
|
120
|
+
: input.baseId;
|
|
121
|
+
const rawTables = Array.isArray(tableInfo?.tables)
|
|
122
|
+
? tableInfo.tables
|
|
123
|
+
: [];
|
|
124
|
+
const tables = rawTables
|
|
125
|
+
.filter((rawTable) => {
|
|
126
|
+
const record = rawTable;
|
|
127
|
+
const tableId = typeof record.id === 'string' ? record.id : '';
|
|
128
|
+
const tableName = typeof record.name === 'string' ? record.name : '';
|
|
129
|
+
const idAllowed = tableId
|
|
130
|
+
? ctx.governance.isTableAllowed(input.baseId, tableId)
|
|
131
|
+
: false;
|
|
132
|
+
const nameAllowed = tableName
|
|
133
|
+
? ctx.governance.isTableAllowed(input.baseId, tableName)
|
|
134
|
+
: false;
|
|
135
|
+
return idAllowed || nameAllowed;
|
|
136
|
+
})
|
|
137
|
+
.map((table) => normalizeTable(table, normalizeOptions));
|
|
138
|
+
let selectedTables = tables;
|
|
139
|
+
if (input.scope === 'table') {
|
|
140
|
+
const target = tables.find((tableRecord) => String(tableRecord.id) === input.table ||
|
|
141
|
+
String(tableRecord.name).toLowerCase() === input.table?.toLowerCase());
|
|
142
|
+
if (!target) {
|
|
143
|
+
const context = { baseId: input.baseId };
|
|
144
|
+
if (input.table) {
|
|
145
|
+
context.table = input.table;
|
|
146
|
+
}
|
|
147
|
+
throw new errors_1.NotFoundError(`Table ${input.table} not found in base ${input.baseId}`, {
|
|
148
|
+
context
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
const targetId = String(target.id);
|
|
152
|
+
const targetName = String(target.name);
|
|
153
|
+
if (!ctx.governance.isTableAllowed(input.baseId, targetId) &&
|
|
154
|
+
!ctx.governance.isTableAllowed(input.baseId, targetName)) {
|
|
155
|
+
const context = { baseId: input.baseId };
|
|
156
|
+
if (input.table) {
|
|
157
|
+
context.table = input.table;
|
|
158
|
+
}
|
|
159
|
+
throw new errors_1.GovernanceError(`Table ${input.table} is not allowed in base ${input.baseId}`, {
|
|
160
|
+
context
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
selectedTables = [target];
|
|
164
|
+
}
|
|
165
|
+
const structuredContent = {
|
|
166
|
+
base: {
|
|
167
|
+
id: input.baseId,
|
|
168
|
+
name: baseName
|
|
169
|
+
},
|
|
170
|
+
tables: selectedTables
|
|
171
|
+
};
|
|
172
|
+
if (input.scope === 'base' && includeViews) {
|
|
173
|
+
structuredContent.views = rawTables
|
|
174
|
+
.flatMap((table) => {
|
|
175
|
+
const record = table;
|
|
176
|
+
return Array.isArray(record.views) ? record.views : [];
|
|
177
|
+
})
|
|
178
|
+
.map((view) => normalizeView(view, detailLevel));
|
|
179
|
+
}
|
|
180
|
+
logger.debug('Describe completed', {
|
|
181
|
+
tableCount: selectedTables.length
|
|
182
|
+
});
|
|
183
|
+
return (0, response_1.createToolResponse)(structuredContent);
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
return (0, handleError_1.handleToolError)('describe', error, ctx);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=describe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"describe.js","sourceRoot":"","sources":["../../../../src/typescript/app/tools/describe.ts"],"names":[],"mappings":"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"}
|