@enfyra/mcp-server 0.1.64 → 0.1.66

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 (49) hide show
  1. package/README.md +1 -0
  2. package/dist/lib/extension-search-tools.d.ts +8 -0
  3. package/dist/lib/extension-search-tools.js +12 -5
  4. package/dist/lib/extension-search-tools.js.map +1 -1
  5. package/dist/lib/mcp-instructions.js +3 -1
  6. package/dist/lib/mcp-instructions.js.map +1 -1
  7. package/dist/lib/model-eval.d.ts +4 -0
  8. package/dist/lib/model-eval.js +291 -0
  9. package/dist/lib/model-eval.js.map +1 -0
  10. package/dist/lib/pagination.d.ts +16 -0
  11. package/dist/lib/pagination.js +41 -0
  12. package/dist/lib/pagination.js.map +1 -0
  13. package/dist/lib/platform-operation-tools.d.ts +1 -0
  14. package/dist/lib/required-knowledge.d.ts +2 -2
  15. package/dist/lib/required-knowledge.js +8 -4
  16. package/dist/lib/required-knowledge.js.map +1 -1
  17. package/dist/lib/response-format.d.ts +3 -1
  18. package/dist/lib/response-format.js +47 -17
  19. package/dist/lib/response-format.js.map +1 -1
  20. package/dist/lib/runtime-zone-tools.d.ts +37 -0
  21. package/dist/lib/runtime-zone-tools.js +70 -16
  22. package/dist/lib/runtime-zone-tools.js.map +1 -1
  23. package/dist/lib/session-safety.js +3 -26
  24. package/dist/lib/session-safety.js.map +1 -1
  25. package/dist/lib/source-artifacts.d.ts +7 -0
  26. package/dist/lib/source-artifacts.js +38 -2
  27. package/dist/lib/source-artifacts.js.map +1 -1
  28. package/dist/lib/tool-catalog.d.ts +7 -0
  29. package/dist/lib/tool-catalog.js +141 -0
  30. package/dist/lib/tool-catalog.js.map +1 -0
  31. package/dist/lib/tool-contracts.d.ts +6 -0
  32. package/dist/lib/tool-contracts.js +99 -0
  33. package/dist/lib/tool-contracts.js.map +1 -0
  34. package/dist/lib/tool-output-contracts.d.ts +7 -0
  35. package/dist/lib/tool-output-contracts.js +152 -0
  36. package/dist/lib/tool-output-contracts.js.map +1 -0
  37. package/dist/lib/tool-routing.d.ts +2 -1
  38. package/dist/lib/tool-routing.js +27 -7
  39. package/dist/lib/tool-routing.js.map +1 -1
  40. package/dist/lib/toolset-filter.d.ts +7 -6
  41. package/dist/lib/toolset-filter.js +80 -22
  42. package/dist/lib/toolset-filter.js.map +1 -1
  43. package/dist/lib/types.d.ts +75 -0
  44. package/dist/lib/workflow-tool-packs.d.ts +2 -0
  45. package/dist/lib/workflow-tool-packs.js +55 -0
  46. package/dist/lib/workflow-tool-packs.js.map +1 -0
  47. package/dist/mcp-server-entry.js +48 -7
  48. package/dist/mcp-server-entry.js.map +1 -1
  49. package/package.json +5 -2
@@ -15,6 +15,81 @@ export interface ToolResult {
15
15
  _meta?: Record<string, unknown>;
16
16
  }
17
17
  export type UnknownRecord = Record<string, unknown>;
18
+ export interface McpToolAnnotations {
19
+ title: string;
20
+ readOnlyHint: boolean;
21
+ destructiveHint: boolean;
22
+ idempotentHint: boolean;
23
+ openWorldHint: boolean;
24
+ }
25
+ export interface McpToolContract {
26
+ name: string;
27
+ annotations: McpToolAnnotations;
28
+ catalogExecutable: boolean;
29
+ }
30
+ export interface RegisteredToolDefinition {
31
+ name: string;
32
+ description: string;
33
+ inputSchema: Record<string, unknown>;
34
+ annotations?: McpToolAnnotations;
35
+ handler: (...args: any[]) => any;
36
+ visible: boolean;
37
+ registration?: {
38
+ enabled: boolean;
39
+ };
40
+ }
41
+ export interface ToolsetRegistrationState {
42
+ toolset: string;
43
+ profile: string;
44
+ dynamic: boolean;
45
+ hiddenTools: string[];
46
+ getTool(name: string): RegisteredToolDefinition | undefined;
47
+ listTools(): RegisteredToolDefinition[];
48
+ listVisibleToolNames(): string[];
49
+ setActiveTools(toolNames: Iterable<string>): {
50
+ changed: boolean;
51
+ visibleToolNames: string[];
52
+ hiddenToolCount: number;
53
+ };
54
+ }
55
+ export type ToolAvailabilityStatus = 'allowed' | 'denied' | 'unknown';
56
+ export interface ToolAvailability {
57
+ status: ToolAvailabilityStatus;
58
+ reason: string;
59
+ }
60
+ export interface ModelEvalTraceEvent {
61
+ tool: string;
62
+ arguments?: Record<string, unknown>;
63
+ result?: unknown;
64
+ isError?: boolean;
65
+ }
66
+ export interface ModelEvalRun {
67
+ scenarioId: string;
68
+ model: string;
69
+ events: ModelEvalTraceEvent[];
70
+ }
71
+ export interface ModelEvalScenario {
72
+ id: string;
73
+ prompt: string;
74
+ surface: string;
75
+ requiredToolGroups: string[][];
76
+ verificationTools: string[];
77
+ maxToolCalls: number;
78
+ mutationExpected: boolean;
79
+ destructiveExpected?: boolean;
80
+ }
81
+ export interface ModelEvalCheck {
82
+ key: string;
83
+ passed: boolean;
84
+ detail: string;
85
+ }
86
+ export interface ModelEvalScore {
87
+ scenarioId: string;
88
+ model: string;
89
+ score: number;
90
+ recommended: boolean;
91
+ checks: ModelEvalCheck[];
92
+ }
18
93
  export interface MetadataContext {
19
94
  dbType: "postgres" | "mysql" | "mongodb" | "mariadb" | "sqlite" | null;
20
95
  enfyraVersion: string | null;
@@ -0,0 +1,2 @@
1
+ import type { ToolsetRegistrationState } from './types.js';
2
+ export declare function registerWorkflowToolPack(server: any, state: ToolsetRegistrationState): any;
@@ -0,0 +1,55 @@
1
+ import { z } from 'zod';
2
+ import { jsonContent } from './response-format.js';
3
+ import { WORKFLOW_SURFACES, workflowToolNames } from './tool-routing.js';
4
+ const MAX_ACTIVE_WORKFLOWS = 2;
5
+ export function registerWorkflowToolPack(server, state) {
6
+ const activeSurfaces = new Set();
7
+ return server.tool('select_enfyra_workflow', [
8
+ 'Select a workflow surface and expose its exact direct MCP tools for this stdio session.',
9
+ 'Use replace for normal tasks, add only when one task genuinely spans two surfaces, and reset to return to the compact routing surface.',
10
+ 'This changes tool visibility only; it does not grant permissions or bypass Enfyra PAT/RBAC.',
11
+ ].join(' '), {
12
+ surface: z.enum(WORKFLOW_SURFACES).optional().describe('Required for replace/add. Omit only with mode=reset.'),
13
+ mode: z.enum(['replace', 'add', 'reset']).optional().default('replace'),
14
+ }, async ({ surface, mode }) => {
15
+ if (mode !== 'reset' && !surface)
16
+ throw new Error('surface is required when mode is replace or add.');
17
+ if (mode === 'reset') {
18
+ activeSurfaces.clear();
19
+ }
20
+ else if (mode === 'replace') {
21
+ activeSurfaces.clear();
22
+ activeSurfaces.add(surface);
23
+ }
24
+ else {
25
+ activeSurfaces.add(surface);
26
+ if (activeSurfaces.size > MAX_ACTIVE_WORKFLOWS) {
27
+ activeSurfaces.delete(surface);
28
+ throw new Error(`At most ${MAX_ACTIVE_WORKFLOWS} workflow surfaces may be active. Use mode=replace or reset first.`);
29
+ }
30
+ }
31
+ const packTools = [...activeSurfaces].flatMap(workflowToolNames);
32
+ const result = state.setActiveTools(packTools);
33
+ return jsonContent({
34
+ action: 'enfyra_workflow_selected',
35
+ mode,
36
+ dynamic: state.dynamic,
37
+ activeSurfaces: [...activeSurfaces],
38
+ visibleToolCount: result.visibleToolNames.length,
39
+ visibleTools: result.visibleToolNames,
40
+ hiddenToolCount: result.hiddenToolCount,
41
+ changed: result.changed,
42
+ guidance: state.dynamic
43
+ ? [
44
+ 'Refresh tools/list after this response if the host has not already processed notifications/tools/list_changed.',
45
+ 'Call the newly visible workflow tools directly so their exact schemas, annotations, and safety gates remain active.',
46
+ 'Use search_enfyra_tools only for hidden read-only long-tail helpers.',
47
+ ]
48
+ : [
49
+ 'Dynamic packs are disabled for this static profile/toolset; the configured profile remains visible.',
50
+ 'Use ENFYRA_MCP_DYNAMIC_TOOLS=on with guided/all on hosts that support tools/list_changed.',
51
+ ],
52
+ });
53
+ });
54
+ }
55
+ //# sourceMappingURL=workflow-tool-packs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-tool-packs.js","sourceRoot":"","sources":["../../src/lib/workflow-tool-packs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAwB,MAAM,mBAAmB,CAAC;AAG/F,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAE/B,MAAM,UAAU,wBAAwB,CAAC,MAAW,EAAE,KAA+B;IACnF,MAAM,cAAc,GAAG,IAAI,GAAG,EAAmB,CAAC;IAElD,OAAO,MAAM,CAAC,IAAI,CAChB,wBAAwB,EACxB;QACE,yFAAyF;QACzF,wIAAwI;QACxI,6FAA6F;KAC9F,CAAC,IAAI,CAAC,GAAG,CAAC,EACX;QACE,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sDAAsD,CAAC;QAC9G,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC;KACxE,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAoE,EAAE,EAAE;QAC5F,IAAI,IAAI,KAAK,OAAO,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACtG,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YACrB,cAAc,CAAC,KAAK,EAAE,CAAC;QACzB,CAAC;aAAM,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YAC9B,cAAc,CAAC,KAAK,EAAE,CAAC;YACvB,cAAc,CAAC,GAAG,CAAC,OAAQ,CAAC,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,cAAc,CAAC,GAAG,CAAC,OAAQ,CAAC,CAAC;YAC7B,IAAI,cAAc,CAAC,IAAI,GAAG,oBAAoB,EAAE,CAAC;gBAC/C,cAAc,CAAC,MAAM,CAAC,OAAQ,CAAC,CAAC;gBAChC,MAAM,IAAI,KAAK,CAAC,WAAW,oBAAoB,oEAAoE,CAAC,CAAC;YACvH,CAAC;QACH,CAAC;QAED,MAAM,SAAS,GAAG,CAAC,GAAG,cAAc,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACjE,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;QAC/C,OAAO,WAAW,CAAC;YACjB,MAAM,EAAE,0BAA0B;YAClC,IAAI;YACJ,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,cAAc,EAAE,CAAC,GAAG,cAAc,CAAC;YACnC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,CAAC,MAAM;YAChD,YAAY,EAAE,MAAM,CAAC,gBAAgB;YACrC,eAAe,EAAE,MAAM,CAAC,eAAe;YACvC,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,QAAQ,EAAE,KAAK,CAAC,OAAO;gBACrB,CAAC,CAAC;oBACE,gHAAgH;oBAChH,qHAAqH;oBACrH,sEAAsE;iBACvE;gBACH,CAAC,CAAC;oBACE,qGAAqG;oBACrG,2FAA2F;iBAC5F;SACN,CAAC,CAAC;IACL,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { config } from 'dotenv';
5
5
  config();
6
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
6
+ import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
7
7
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
8
8
  import { z } from 'zod';
9
9
  import { createHash } from 'node:crypto';
@@ -72,13 +72,18 @@ import { installColumnarToolFormatter, jsonContent } from './lib/response-format
72
72
  import { startMcpUsageTelemetry } from './lib/mcp-usage-telemetry.js';
73
73
  import { startRuntimeCacheSocket } from './lib/runtime-cache-socket.js';
74
74
  import { executeSequentialBatch } from './lib/sequential-batch.js';
75
- import { compactSourceFields, writeSourceArtifact } from './lib/source-artifacts.js';
76
- import { installToolsetFilter, normalizeMcpProfile, normalizeMcpToolset, summarizeToolsetForInstructions } from './lib/toolset-filter.js';
75
+ import { compactSourceFields, readSourceArtifactResource, writeSourceArtifact } from './lib/source-artifacts.js';
76
+ import { installToolsetFilter, normalizeDynamicToolPacks, normalizeMcpProfile, normalizeMcpToolset, summarizeToolsetForInstructions } from './lib/toolset-filter.js';
77
+ import { installToolAnnotations } from './lib/tool-contracts.js';
78
+ import { installToolOutputContracts } from './lib/tool-output-contracts.js';
79
+ import { registerToolCatalogTools } from './lib/tool-catalog.js';
80
+ import { registerWorkflowToolPack } from './lib/workflow-tool-packs.js';
77
81
  import { findRoutePermission, mergeMethodNames, normalizeMethodNames, resolveRoleByNameOrId, routeAvailableMethodNames, routePublicMethodNames, summarizeRouteAccess, summarizeRoutePermission, validateMethodsForRoute, } from './lib/route-permission-tools.js';
78
82
  // Initialize auth module
79
83
  initAuth(ENFYRA_API_URL, ENFYRA_API_TOKEN);
80
84
  const MCP_TOOLSET = normalizeMcpToolset(process.env.ENFYRA_MCP_TOOLSET);
81
85
  const MCP_PROFILE = normalizeMcpProfile(process.env.ENFYRA_MCP_PROFILE);
86
+ const MCP_DYNAMIC_TOOLS = normalizeDynamicToolPacks(process.env.ENFYRA_MCP_DYNAMIC_TOOLS, MCP_TOOLSET, MCP_PROFILE);
82
87
  const CAPABILITY_AREAS = [
83
88
  {
84
89
  area: 'Schema and metadata',
@@ -418,6 +423,30 @@ function summarizePermissionProfile(user) {
418
423
  })),
419
424
  };
420
425
  }
426
+ async function resolveCatalogToolAvailability(toolNames) {
427
+ const fields = DEFAULT_ME_PERMISSION_FIELDS.join(',');
428
+ const result = await fetchAPI(ENFYRA_API_URL, `/me?fields=${encodeURIComponent(fields)}`);
429
+ const user = firstDataRecord(result);
430
+ if (user?.isRootAdmin) {
431
+ return Object.fromEntries(toolNames.map((name) => [name, {
432
+ status: 'allowed',
433
+ reason: 'The configured PAT belongs to a root administrator.',
434
+ }]));
435
+ }
436
+ const requirements = summarizePermissionProfile(user).mcpRequirements;
437
+ return Object.fromEntries(toolNames.map((name) => {
438
+ const requirement = requirements.find((item) => item.tools.includes(name));
439
+ if (!requirement) {
440
+ return [name, {
441
+ status: 'unknown',
442
+ reason: 'No static admin-route capability mapping exists for this tool; Enfyra PAT/RBAC remains authoritative at execution time.',
443
+ }];
444
+ }
445
+ return [name, requirement.allowed
446
+ ? { status: 'allowed', reason: `Current PAT grants ${requirement.methods.map((item) => item.method).join(', ')} ${requirement.route}.` }
447
+ : { status: 'denied', reason: `Current PAT lacks one or more required methods on ${requirement.route}.` }];
448
+ }));
449
+ }
421
450
  function parseJsonArg(value, fallback = undefined) {
422
451
  if (value === undefined || value === null || value === '')
423
452
  return fallback;
@@ -810,12 +839,19 @@ const server = new McpServer({
810
839
  version: '1.0.0',
811
840
  }, {
812
841
  instructions: buildMcpServerInstructions(ENFYRA_API_URL, {
813
- toolsetSummary: summarizeToolsetForInstructions(MCP_TOOLSET, MCP_PROFILE),
842
+ toolsetSummary: summarizeToolsetForInstructions(MCP_TOOLSET, MCP_PROFILE, MCP_DYNAMIC_TOOLS),
814
843
  }),
815
844
  });
845
+ installToolOutputContracts(server);
816
846
  installColumnarToolFormatter(server);
817
- installToolsetFilter(server, MCP_TOOLSET, MCP_PROFILE);
847
+ const toolsetState = installToolsetFilter(server, MCP_TOOLSET, MCP_PROFILE, { dynamic: MCP_DYNAMIC_TOOLS });
848
+ installToolAnnotations(server);
818
849
  startMcpUsageTelemetry(ENFYRA_API_URL, `${MCP_TOOLSET}:${MCP_PROFILE}`);
850
+ server.registerResource('enfyra-source-artifact', new ResourceTemplate('enfyra-source://artifact/{artifactId}', { list: undefined }), {
851
+ title: 'Enfyra source artifact',
852
+ description: 'Process-scoped source or diff artifact created by an Enfyra MCP inspect or preview tool.',
853
+ mimeType: 'text/plain',
854
+ }, async (uri) => ({ contents: [readSourceArtifactResource(uri.href)] }));
819
855
  // ============================================================================
820
856
  // METADATA TOOLS
821
857
  // ============================================================================
@@ -1632,10 +1668,11 @@ server.tool('update_records', 'Update one or more records in one MCP call. Pass
1632
1668
  });
1633
1669
  server.tool('get_script_source', [
1634
1670
  'Fetch the full editable source for one script-backed metadata record without preview truncation.',
1635
- 'Use this before reviewing or patching long handlers, hooks, flow steps, websocket scripts, OAuth provisioning scripts, or bootstrap scripts.',
1671
+ 'Use search_runtime_zone first and pass the returned nextInspect.input to inspect the concrete record. The inspection already returns exact source artifacts.',
1672
+ 'Call get_script_source only when a fresh artifact is needed for that located record. Never guess or probe record ids.',
1636
1673
  ].join(' '), {
1637
1674
  tableName: z.enum(SCRIPT_BACKED_TABLES).describe('Script-backed table to read'),
1638
- id: z.string().describe('Record ID to read'),
1675
+ id: z.string().describe('Concrete record id returned by search_runtime_zone, inspect output, or a successful create/update operation. Never guess an id.'),
1639
1676
  }, async ({ tableName, id }) => {
1640
1677
  const { primaryKey, record, sourceField, sourceCode } = await fetchScriptRecord(tableName, id);
1641
1678
  const sourceArtifact = writeSourceArtifact({ tableName, id, fieldName: sourceField, source: sourceCode });
@@ -3149,6 +3186,10 @@ server.tool('install_package', [
3149
3186
  result,
3150
3187
  });
3151
3188
  });
3189
+ registerToolCatalogTools(server, toolsetState, {
3190
+ resolveAvailability: resolveCatalogToolAvailability,
3191
+ });
3192
+ registerWorkflowToolPack(server, toolsetState);
3152
3193
  // ============================================================================
3153
3194
  // MAIN
3154
3195
  // ============================================================================