@enfyra/mcp-server 0.1.33 → 0.1.35
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/dist/lib/mcp-examples.js +11 -1
- package/dist/lib/mcp-examples.js.map +1 -1
- package/dist/lib/mcp-usage-telemetry.js +88 -17
- package/dist/lib/mcp-usage-telemetry.js.map +1 -1
- package/dist/lib/required-knowledge.js +3 -0
- package/dist/lib/required-knowledge.js.map +1 -1
- package/dist/lib/runtime-zone-tools.js +2 -0
- package/dist/lib/runtime-zone-tools.js.map +1 -1
- package/dist/mcp-server-entry.js +26 -3
- package/dist/mcp-server-entry.js.map +1 -1
- package/package.json +2 -2
package/dist/mcp-server-entry.js
CHANGED
|
@@ -32,6 +32,27 @@ function normalizeSortParam(sort) {
|
|
|
32
32
|
.filter(Boolean)
|
|
33
33
|
.join(',');
|
|
34
34
|
}
|
|
35
|
+
function normalizeFieldSelection(fields) {
|
|
36
|
+
return (fields || [])
|
|
37
|
+
.flatMap((field) => String(field).split(','))
|
|
38
|
+
.map((field) => field.trim())
|
|
39
|
+
.filter(Boolean);
|
|
40
|
+
}
|
|
41
|
+
function assertExtensionReadFields(tableName, fields) {
|
|
42
|
+
if (tableName !== 'enfyra_extension')
|
|
43
|
+
return;
|
|
44
|
+
const requestedFields = normalizeFieldSelection(fields);
|
|
45
|
+
const requestsSourceCode = requestedFields.some((field) => field === 'sourceCode' || field.endsWith('.sourceCode'));
|
|
46
|
+
if (!requestsSourceCode)
|
|
47
|
+
return;
|
|
48
|
+
throw new Error([
|
|
49
|
+
'enfyra_extension stores editable Vue SFC extension source in `code`, not `sourceCode`.',
|
|
50
|
+
'`sourceCode` belongs to dynamic server script records such as handlers, hooks, flow steps, websocket handlers, GraphQL resolvers, and bootstrap scripts.',
|
|
51
|
+
'For admin UI lookup, use search_admin_extensions(mode="search") then search_admin_extensions(mode="inspect").',
|
|
52
|
+
'For focused extension edits, use patch_extension_code or update_extension_code.',
|
|
53
|
+
'If you intentionally read raw extension records, request fields such as ["id","name","type","version","code"].',
|
|
54
|
+
].join(' '));
|
|
55
|
+
}
|
|
35
56
|
// Import modules
|
|
36
57
|
import { exchangeApiToken, getValidToken, getTokenExpiry, initAuth } from './lib/auth.js';
|
|
37
58
|
import { fetchAPI, validateFilter, validateTableName } from './lib/fetch.js';
|
|
@@ -1281,7 +1302,7 @@ server.tool('discover_script_contexts', [
|
|
|
1281
1302
|
},
|
|
1282
1303
|
socketInHttpOrFlow: 'HTTP/flow context can emitToUser/emitToRoom/emitToGateway/broadcast and roomSize, but cannot reply/join/leave/disconnect/emitToCurrentRoom/broadcastToRoom because there is no bound socket. emitToRoom requires an explicit gateway path: emitToRoom(path, room, event, data). roomSize(room) counts sockets in that room across registered gateways.',
|
|
1283
1304
|
packages: 'Server packages installed through install_package are exposed as $ctx.$pkgs.packageName in server scripts.',
|
|
1284
|
-
files: 'Upload helpers are on $storage; raw create_records on enfyra_file is not equivalent to multipart upload/storage rollback. For multipart request files, pass file: @UPLOADED_FILE to @STORAGE.$upload/@STORAGE.$update so Enfyra streams from disk-backed temp storage. Use @STORAGE.$registerFile only when the object already exists in storage and the script should create the enfyra_file record without uploading bytes. Use buffer only for small generated files.',
|
|
1305
|
+
files: 'Upload helpers are on $storage; raw create_records on enfyra_file is not equivalent to multipart upload/storage rollback. For multipart request files, pass file: @UPLOADED_FILE to @STORAGE.$upload/@STORAGE.$update so Enfyra streams from disk-backed temp storage. Use onProgress on $upload or blob-replacing $update when scripts need live progress; it receives raw storage storing progress from 0 to 100 and can emit custom @SOCKET events. Use @STORAGE.$registerFile only when the object already exists in storage and the script should create the enfyra_file record without uploading bytes. Use buffer only for small generated files.',
|
|
1285
1306
|
},
|
|
1286
1307
|
adminTesting: {
|
|
1287
1308
|
flowStep: 'Use test_flow_step or run_admin_test(kind=flow_step).',
|
|
@@ -1324,7 +1345,7 @@ server.tool('get_enfyra_api_context', [
|
|
|
1324
1345
|
};
|
|
1325
1346
|
return jsonContent(payload);
|
|
1326
1347
|
});
|
|
1327
|
-
server.tool('query_table', 'Query any route-backed table. Response is minimal unless fields is explicit. Every call must pass either limit or all=true. Use count_records or meta=filterCount/totalCount for counts; call discover_query_capabilities before using aggregate objects instead of guessing _sum/_count operators.', {
|
|
1348
|
+
server.tool('query_table', 'Query any route-backed table. Response is minimal unless fields is explicit. Every call must pass either limit or all=true. Use count_records or meta=filterCount/totalCount for counts; call discover_query_capabilities before using aggregate objects instead of guessing _sum/_count operators. For enfyra_extension, editable extension source is `code`, not `sourceCode`; prefer search_admin_extensions and patch_extension_code/update_extension_code for admin UI.', {
|
|
1328
1349
|
tableName: z.string().describe('Table name to query'),
|
|
1329
1350
|
filter: jsonObjectParam(z, 'Filter object').optional().describe('Filter object. Example: {"status": {"_eq": "active"}}.'),
|
|
1330
1351
|
sort: z.string().optional().describe('Sort field. Prefix with - for descending (e.g., "createdAt", "-id")'),
|
|
@@ -1343,6 +1364,7 @@ server.tool('query_table', 'Query any route-backed table. Response is minimal un
|
|
|
1343
1364
|
throw new Error('query_table accepts either all=true or limit, not both.');
|
|
1344
1365
|
}
|
|
1345
1366
|
validateTableName(tableName);
|
|
1367
|
+
assertExtensionReadFields(tableName, fields);
|
|
1346
1368
|
const filterParam = stringifyJsonArg(filter);
|
|
1347
1369
|
const deepParam = stringifyJsonArg(deep);
|
|
1348
1370
|
const aggregateParam = stringifyJsonArg(aggregate);
|
|
@@ -1431,13 +1453,14 @@ server.tool('count_records', [
|
|
|
1431
1453
|
};
|
|
1432
1454
|
return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] };
|
|
1433
1455
|
});
|
|
1434
|
-
server.tool('find_one_record', 'Find a single record by ID or filter. By ID uses GET with filter (Enfyra has no GET /table/:id route).', {
|
|
1456
|
+
server.tool('find_one_record', 'Find a single record by ID or filter. By ID uses GET with filter (Enfyra has no GET /table/:id route). For enfyra_extension, editable extension source is `code`, not `sourceCode`; prefer search_admin_extensions and patch_extension_code/update_extension_code for admin UI.', {
|
|
1435
1457
|
tableName: z.string().describe('Table name'),
|
|
1436
1458
|
id: z.string().optional().describe('Record ID'),
|
|
1437
1459
|
filter: jsonObjectParam(z, 'Filter object').optional().describe('Filter object to find by.'),
|
|
1438
1460
|
fields: z.array(z.string()).optional().describe('Fields to select. If omitted, returns only the primary key.'),
|
|
1439
1461
|
}, async ({ tableName, id, filter, fields }) => {
|
|
1440
1462
|
validateTableName(tableName);
|
|
1463
|
+
assertExtensionReadFields(tableName, fields);
|
|
1441
1464
|
const primaryKey = await getPrimaryFieldName(tableName);
|
|
1442
1465
|
const selectedFields = fields && fields.length > 0 ? fields : [primaryKey];
|
|
1443
1466
|
if (id) {
|