@enfyra/mcp-server 0.1.62 → 0.1.64

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 (52) hide show
  1. package/README.md +1 -0
  2. package/dist/lib/dynamic-endpoint-contract.d.ts +12 -0
  3. package/dist/lib/dynamic-endpoint-contract.js +148 -0
  4. package/dist/lib/dynamic-endpoint-contract.js.map +1 -0
  5. package/dist/lib/dynamic-repository-builder.d.ts +7 -0
  6. package/dist/lib/dynamic-repository-builder.js +8 -0
  7. package/dist/lib/dynamic-repository-builder.js.map +1 -1
  8. package/dist/lib/extension-search-tools.js +2 -1
  9. package/dist/lib/extension-search-tools.js.map +1 -1
  10. package/dist/lib/extension-sfc-analyzer.d.ts +4 -0
  11. package/dist/lib/extension-sfc-analyzer.js +137 -0
  12. package/dist/lib/extension-sfc-analyzer.js.map +1 -0
  13. package/dist/lib/mcp-examples.js +117 -8
  14. package/dist/lib/mcp-examples.js.map +1 -1
  15. package/dist/lib/mcp-instructions.js +5 -15
  16. package/dist/lib/mcp-instructions.js.map +1 -1
  17. package/dist/lib/mcp-usage-telemetry.js +41 -2
  18. package/dist/lib/mcp-usage-telemetry.js.map +1 -1
  19. package/dist/lib/mutation-guards.d.ts +1 -0
  20. package/dist/lib/mutation-guards.js +42 -0
  21. package/dist/lib/mutation-guards.js.map +1 -1
  22. package/dist/lib/platform-operation-tools.d.ts +160 -3
  23. package/dist/lib/platform-operation-tools.js +454 -95
  24. package/dist/lib/platform-operation-tools.js.map +1 -1
  25. package/dist/lib/required-knowledge.d.ts +9 -2
  26. package/dist/lib/required-knowledge.js +60 -17
  27. package/dist/lib/required-knowledge.js.map +1 -1
  28. package/dist/lib/response-format.js +39 -25
  29. package/dist/lib/response-format.js.map +1 -1
  30. package/dist/lib/runtime-zone-tools.js +4 -3
  31. package/dist/lib/runtime-zone-tools.js.map +1 -1
  32. package/dist/lib/session-safety.d.ts +9 -0
  33. package/dist/lib/session-safety.js +89 -0
  34. package/dist/lib/session-safety.js.map +1 -0
  35. package/dist/lib/source-artifacts.js +3 -1
  36. package/dist/lib/source-artifacts.js.map +1 -1
  37. package/dist/lib/table-tools.d.ts +9 -1
  38. package/dist/lib/table-tools.js +69 -9
  39. package/dist/lib/table-tools.js.map +1 -1
  40. package/dist/lib/tool-input-normalization.d.ts +4 -0
  41. package/dist/lib/tool-input-normalization.js +35 -0
  42. package/dist/lib/tool-input-normalization.js.map +1 -0
  43. package/dist/lib/tool-routing.d.ts +6 -2
  44. package/dist/lib/tool-routing.js +52 -16
  45. package/dist/lib/tool-routing.js.map +1 -1
  46. package/dist/lib/toolset-filter.d.ts +7 -3
  47. package/dist/lib/toolset-filter.js +144 -86
  48. package/dist/lib/toolset-filter.js.map +1 -1
  49. package/dist/lib/types.d.ts +62 -0
  50. package/dist/mcp-server-entry.js +79 -23
  51. package/dist/mcp-server-entry.js.map +1 -1
  52. package/package.json +4 -1
@@ -0,0 +1,89 @@
1
+ const DESTRUCTIVE_TOOLS = new Set([
2
+ 'delete_records',
3
+ 'delete_tables',
4
+ 'delete_columns',
5
+ 'delete_relations',
6
+ 'delete_method',
7
+ 'delete_route',
8
+ ]);
9
+ const MUTATION_TOOL_PATTERN = /^(?:create|update|delete|ensure|patch|install|enable|disable|reorder|reload|trigger|set|add|remove)_/;
10
+ const MUTATION_TOOLS = new Set([
11
+ 'api_endpoint_workflow',
12
+ 'extension_workflow',
13
+ 'flow_workflow',
14
+ 'public_route_methods',
15
+ 'private_route_methods',
16
+ 'replace_route_methods',
17
+ 'run_admin_test',
18
+ 'test_flow_step',
19
+ 'test_graphql',
20
+ 'test_rest_endpoint',
21
+ ]);
22
+ const PREVIEW_IGNORED_KEYS = new Set([
23
+ 'confirm',
24
+ 'expectedPath',
25
+ 'globalRulesAckKey',
26
+ 'maxItems',
27
+ 'skipNotFound',
28
+ ]);
29
+ const ID_KEYS = new Set(['id', '_id', 'columnId', 'flowId', 'relationId', 'routeId', 'tableId']);
30
+ let targetConfirmed = false;
31
+ const destructivePreviews = new Set();
32
+ function isRecord(value) {
33
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
34
+ }
35
+ function normalizeFingerprintValue(value, key) {
36
+ if (Array.isArray(value))
37
+ return value.map((entry) => normalizeFingerprintValue(entry));
38
+ if (isRecord(value)) {
39
+ return Object.fromEntries(Object.keys(value)
40
+ .filter((entryKey) => !PREVIEW_IGNORED_KEYS.has(entryKey))
41
+ .sort()
42
+ .map((entryKey) => [entryKey, normalizeFingerprintValue(value[entryKey], entryKey)]));
43
+ }
44
+ if (key && ID_KEYS.has(key) && value !== undefined && value !== null)
45
+ return String(value);
46
+ return value;
47
+ }
48
+ function destructivePreviewKey(toolName, input) {
49
+ return `${toolName}:${JSON.stringify(normalizeFingerprintValue(input))}`;
50
+ }
51
+ function isMutationTool(toolName) {
52
+ return MUTATION_TOOL_PATTERN.test(toolName) || MUTATION_TOOLS.has(toolName);
53
+ }
54
+ export function resetMcpSafetySession() {
55
+ targetConfirmed = false;
56
+ destructivePreviews.clear();
57
+ }
58
+ export function getMcpSafetySessionState() {
59
+ return {
60
+ targetConfirmed,
61
+ destructivePreviewCount: destructivePreviews.size,
62
+ };
63
+ }
64
+ export function beforeMcpToolExecution(toolName, input = {}) {
65
+ if (isMutationTool(toolName) && !targetConfirmed) {
66
+ throw new Error(`Target is not confirmed for this MCP process session. Call get_enfyra_api_context before ${toolName}, verify the API base, then retry.`);
67
+ }
68
+ if (DESTRUCTIVE_TOOLS.has(toolName) && input.confirm === true) {
69
+ const key = destructivePreviewKey(toolName, input);
70
+ if (!destructivePreviews.has(key)) {
71
+ throw new Error(`Missing matching destructive preview for ${toolName}. Call the same tool first with confirm=false, inspect the preview, then retry with confirm=true in this MCP process session.`);
72
+ }
73
+ }
74
+ }
75
+ export function afterMcpToolExecution(toolName, input = {}) {
76
+ if (toolName === 'get_enfyra_api_context') {
77
+ targetConfirmed = true;
78
+ return;
79
+ }
80
+ if (!DESTRUCTIVE_TOOLS.has(toolName))
81
+ return;
82
+ const key = destructivePreviewKey(toolName, input);
83
+ if (input.confirm === true) {
84
+ destructivePreviews.delete(key);
85
+ return;
86
+ }
87
+ destructivePreviews.add(key);
88
+ }
89
+ //# sourceMappingURL=session-safety.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-safety.js","sourceRoot":"","sources":["../../src/lib/session-safety.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';
2
2
  import { mkdirSync, writeFileSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  import { tmpdir } from 'node:os';
5
- const DEFAULT_PREVIEW_CHARS = 1200;
5
+ const DEFAULT_PREVIEW_CHARS = 480;
6
6
  const DEFAULT_INLINE_LIMIT = 1400;
7
7
  const SOURCE_FIELD_NAMES = new Set([
8
8
  'sourceCode',
@@ -21,6 +21,8 @@ function safePart(value) {
21
21
  function extensionForField(fieldName) {
22
22
  if (fieldName === 'code')
23
23
  return '.vue';
24
+ if (fieldName.endsWith('.diff') || fieldName === 'diff')
25
+ return '.diff';
24
26
  return '.js';
25
27
  }
26
28
  export function writeSourceArtifact({ tableName, id, fieldName, source }) {
@@ -1 +1 @@
1
- {"version":3,"file":"source-artifacts.js","sourceRoot":"","sources":["../../src/lib/source-artifacts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"source-artifacts.js","sourceRoot":"","sources":["../../src/lib/source-artifacts.ts"],"names":[],"mappings":"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"}
@@ -29,6 +29,7 @@ type RelationPatch = AnyRecord & {
29
29
  onDelete?: string;
30
30
  description?: string;
31
31
  };
32
+ export declare function assertColumnContractBroadening(existingColumn: AnyRecord, requested: AnyRecord, toolset?: string): string[];
32
33
  export declare function buildPrimaryColumnForDbType(dbType: string | null | undefined): ColumnPatch;
33
34
  export declare function normalizeTablesFromMetadata(metadata: any): any[];
34
35
  export declare function resolveTableFromMetadata(metadata: any, tableId: any): any;
@@ -40,6 +41,11 @@ export declare function resolveTableIdentifierFromMetadata(metadata: any, tableR
40
41
  * complete permission-projected schema from /metadata/:name.
41
42
  */
42
43
  export declare function fetchTableWithDetails(ENFYRA_API_URL: any, tableId: any): Promise<AnyRecord>;
44
+ export declare function normalizeCreateTableDefinitions(items: AnyRecord[]): {
45
+ _requestedTableName?: string;
46
+ relations?: any;
47
+ name: string;
48
+ }[];
43
49
  export declare function computeBatchCleanupOrder(items: AnyRecord[]): string[];
44
50
  export declare function assertIndexesDoNotReferenceUniqueFields(indexes: ConstraintGroup[], uniques: ConstraintGroup[]): void;
45
51
  export declare function normalizeRelationForTablePatch(relation: AnyRecord): RelationPatch;
@@ -75,5 +81,7 @@ export declare function buildColumnDefinition({ name, type, supportedTypes, isNu
75
81
  /**
76
82
  * Register table tools with MCP server
77
83
  */
78
- export declare function registerTableTools(server: any, ENFYRA_API_URL: any): void;
84
+ export declare function registerTableTools(server: any, ENFYRA_API_URL: any, options?: {
85
+ toolset?: string;
86
+ }): void;
79
87
  export {};
@@ -6,10 +6,22 @@ import { fetchAPI } from './fetch.js';
6
6
  import { fetchMetadataContext, fetchTableCatalog, fetchTableMetadata, resolveTableCatalogEntry, } from './metadata-client.js';
7
7
  import { jsonContent } from './response-format.js';
8
8
  import { assertGlobalRulesAck, globalRulesAckParam } from './required-knowledge.js';
9
+ import { normalizeTableName } from './tool-input-normalization.js';
9
10
  function bulkObjectArrayParam(z, label) {
10
11
  return z.array(z.record(z.any())).describe(`${label} as a native JSON array of objects. Pass one object in the array for a single mutation.`);
11
12
  }
12
13
  let schemaQueue = Promise.resolve();
14
+ export function assertColumnContractBroadening(existingColumn, requested, toolset = 'guided') {
15
+ const broadened = [];
16
+ if (existingColumn?.isUpdatable === false && requested?.isUpdatable === true)
17
+ broadened.push('isUpdatable false→true');
18
+ if (existingColumn?.isPublished === false && requested?.isPublished === true)
19
+ broadened.push('isPublished false→true');
20
+ if (broadened.length > 0 && (toolset !== 'full' || requested?.allowContractBroadening !== true)) {
21
+ throw new Error(`Column contract broadening is blocked in the guided toolset: ${broadened.join(', ')}. Do not broaden canonical metadata merely for a custom action or E2E fixture; use an exact trusted internal write after authorization instead. Expert full-toolset changes additionally require allowContractBroadening=true.`);
22
+ }
23
+ return broadened;
24
+ }
13
25
  function withSchemaQueue(operation) {
14
26
  const run = schemaQueue.then(operation, operation);
15
27
  schemaQueue = run.catch(() => { });
@@ -283,6 +295,35 @@ function preflightCreateTableDefinitions(items) {
283
295
  }
284
296
  });
285
297
  }
298
+ export function normalizeCreateTableDefinitions(items) {
299
+ const batchNameMap = new Map(items
300
+ .map((item) => String(item?.name ?? ''))
301
+ .filter(Boolean)
302
+ .map((name) => [name.toLowerCase(), normalizeTableName(name)]));
303
+ return items.map((item) => {
304
+ const originalName = String(item?.name ?? '');
305
+ const name = normalizeTableName(originalName);
306
+ const relations = Array.isArray(item?.relations)
307
+ ? item.relations.map((relation) => {
308
+ const target = relation?.targetTable ?? relation?.targetTableId;
309
+ if (typeof target !== 'string')
310
+ return relation;
311
+ const normalizedTarget = batchNameMap.get(target.toLowerCase());
312
+ if (!normalizedTarget || normalizedTarget === target)
313
+ return relation;
314
+ return relation?.targetTable !== undefined
315
+ ? { ...relation, targetTable: normalizedTarget }
316
+ : { ...relation, targetTableId: normalizedTarget };
317
+ })
318
+ : item?.relations;
319
+ return {
320
+ ...item,
321
+ name,
322
+ ...(relations !== undefined ? { relations } : {}),
323
+ ...(name !== originalName ? { _requestedTableName: originalName } : {}),
324
+ };
325
+ });
326
+ }
286
327
  function relationTargetName(relation) {
287
328
  const target = relation?.targetTable ?? relation?.targetTableId;
288
329
  if (target && typeof target === 'object')
@@ -639,7 +680,8 @@ export function buildColumnDefinition({ name, type, supportedTypes, isNullable,
639
680
  /**
640
681
  * Register table tools with MCP server
641
682
  */
642
- export function registerTableTools(server, ENFYRA_API_URL) {
683
+ export function registerTableTools(server, ENFYRA_API_URL, options = {}) {
684
+ const toolset = options.toolset || 'guided';
643
685
  const apiBase = ENFYRA_API_URL.replace(/\/$/, '');
644
686
  async function appendColumnToTable(args) {
645
687
  assertGlobalRulesAck(args.globalRulesAckKey);
@@ -861,6 +903,10 @@ export function registerTableTools(server, ENFYRA_API_URL) {
861
903
  const idColumn = buildPrimaryColumnForDbType(metadataContext.dbType);
862
904
  const { columns: userColumnsWithoutAuto, skippedAutoColumns } = stripAutoManagedColumns(userColumns);
863
905
  const { columns: normalizedUserColumns, normalizations } = normalizeColumnsForLiveMetadata(userColumnsWithoutAuto, supportedTypes);
906
+ const schemaNormalizations = [
907
+ ...(args._requestedTableName ? [{ field: 'name', from: args._requestedTableName, to: args.name, reason: 'Enfyra table names are lowercase.' }] : []),
908
+ ...normalizations,
909
+ ];
864
910
  const deferredRelations = arrayValue('relations', args.relations).map(normalizeRelationForTablePatch);
865
911
  const relationNames = new Set(deferredRelations.map((relation) => relation.propertyName).filter(Boolean));
866
912
  assertNoColumnRelationNameCollision(normalizedUserColumns.map((column) => String(column.name || '')).filter(Boolean), deferredRelations.map((relation) => String(relation.propertyName || '')).filter(Boolean), `create_tables item "${args.name}"`);
@@ -900,7 +946,7 @@ export function registerTableTools(server, ENFYRA_API_URL) {
900
946
  uniqueGroupCount: uniques.length,
901
947
  deferredConstraintCount: splitIndexes.deferred.length + splitUniques.deferred.length,
902
948
  },
903
- schemaNormalization: normalizations,
949
+ schemaNormalization: schemaNormalizations,
904
950
  skippedAutoColumns,
905
951
  schema: {
906
952
  intended: {
@@ -1029,7 +1075,7 @@ export function registerTableTools(server, ENFYRA_API_URL) {
1029
1075
  result,
1030
1076
  };
1031
1077
  }
1032
- async function updateOneColumn({ tableId, columnId, name, type, isNullable, isPublished, isUpdatable, defaultValue, description, options }) {
1078
+ async function updateOneColumn({ tableId, columnId, name, type, isNullable, isPublished, isUpdatable, defaultValue, description, options, allowContractBroadening }) {
1033
1079
  const tableData = await fetchTableWithDetails(ENFYRA_API_URL, tableId);
1034
1080
  if (!tableData) {
1035
1081
  throw new Error(`Table with ID ${tableId} not found.`);
@@ -1039,6 +1085,12 @@ export function registerTableTools(server, ENFYRA_API_URL) {
1039
1085
  if (!beforeIds.includes(String(columnId))) {
1040
1086
  throw new Error(`Column ${columnId} was not found on table ${tableId}; refusing schema cascade patch.`);
1041
1087
  }
1088
+ const existingColumn = existingColumns.find((column) => String(getId(column)) === String(columnId));
1089
+ const contractBroadening = assertColumnContractBroadening(existingColumn || {}, {
1090
+ isPublished,
1091
+ isUpdatable,
1092
+ allowContractBroadening,
1093
+ }, toolset);
1042
1094
  const columns = existingColumns.map(col => {
1043
1095
  const rest = normalizeColumnForTablePatch(col);
1044
1096
  if (String(getId(col)) === String(columnId)) {
@@ -1070,6 +1122,7 @@ export function registerTableTools(server, ENFYRA_API_URL) {
1070
1122
  action: 'column_updated',
1071
1123
  tableId,
1072
1124
  columnId,
1125
+ contractBroadening,
1073
1126
  result,
1074
1127
  };
1075
1128
  }
@@ -1230,7 +1283,7 @@ export function registerTableTools(server, ENFYRA_API_URL) {
1230
1283
  assertGlobalRulesAck(globalRulesAckKey);
1231
1284
  if (items !== undefined && tables !== undefined)
1232
1285
  throw new Error('Pass either items or tables to create_tables, not both.');
1233
- const parsedItems = parseBulkItemsParam('items', items ?? tables);
1286
+ const parsedItems = normalizeCreateTableDefinitions(parseBulkItemsParam('items', items ?? tables));
1234
1287
  assertBulkLimit('create_tables', parsedItems, maxItems);
1235
1288
  preflightCreateTableDefinitions(parsedItems);
1236
1289
  const recordDeleteOrder = computeBatchCleanupOrder(parsedItems);
@@ -1300,9 +1353,16 @@ export function registerTableTools(server, ENFYRA_API_URL) {
1300
1353
  recordRule: 'If you delete seeded records before deleting test tables, delete record batches sequentially in recordDeleteOrder; do not parallelize parent/child deletes.',
1301
1354
  tableRule: 'For full test cleanup, prefer delete_tables with tableDeleteOrder after deleting custom routes/flows; table deletion removes the remaining table data.',
1302
1355
  },
1303
- created,
1304
- createdRelations,
1305
- appliedDeferredConstraints,
1356
+ created: toolset === 'full' ? created : created.map(({ result, supportedColumnTypes, schema, ...item }) => ({
1357
+ ...item,
1358
+ schema: {
1359
+ intended: schema?.intended,
1360
+ liveMetadataAvailable: schema?.liveMetadataAvailable,
1361
+ liveMetadataError: schema?.liveMetadataError,
1362
+ },
1363
+ })),
1364
+ createdRelations: toolset === 'full' ? createdRelations : createdRelations.map(({ result, responseFormat, ...item }) => item),
1365
+ appliedDeferredConstraints: toolset === 'full' ? appliedDeferredConstraints : appliedDeferredConstraints.map(({ result, ...item }) => item),
1306
1366
  });
1307
1367
  });
1308
1368
  server.tool('update_tables', 'Update one or more table definitions. Always pass items as a native JSON array; for one table, pass one item. Items run sequentially through the schema queue.', {
@@ -1367,8 +1427,8 @@ export function registerTableTools(server, ENFYRA_API_URL) {
1367
1427
  }
1368
1428
  return jsonContent({ action: 'columns_created', requested: parsedItems.length, createdCount: created.length, sequential: true, created });
1369
1429
  });
1370
- server.tool('update_columns', 'Update one or more columns. Always pass items as a native JSON array; for one column, pass one item. Items run sequentially through the schema queue.', {
1371
- items: bulkObjectArrayParam(z, 'Column update items').describe('Native JSON array of column update items: [{ tableId, columnId, name?, type?, isNullable?, isPublished?, isUpdatable?, defaultValue?, description?, options? }].'),
1430
+ server.tool('update_columns', 'Update one or more columns. Always pass items as a native JSON array; for one column, pass one item. Items run sequentially through the schema queue. Guided mode blocks isUpdatable/isPublished false→true broadening. Do not set isUpdatable=true merely to seed E2E data or let a custom action change a server-owned field; use an exact trusted internal write after authorization and preserve the canonical metadata contract.', {
1431
+ items: bulkObjectArrayParam(z, 'Column update items').describe('Native JSON array of column update items: [{ tableId, columnId, name?, type?, isNullable?, isPublished?, isUpdatable?, defaultValue?, description?, options?, allowContractBroadening? }]. Guided mode blocks false→true isUpdatable/isPublished changes. Expert full mode requires allowContractBroadening=true; never use broadening only for custom-action writes or test fixtures.'),
1372
1432
  maxItems: z.number().int().min(1).max(100).optional().default(100).describe('Safety cap for one schema batch. Default/max is 100.'),
1373
1433
  globalRulesAckKey: globalRulesAckParam(z),
1374
1434
  }, async ({ items, maxItems, globalRulesAckKey }) => {