@ema.co/mcp-toolkit 2026.2.13 → 2026.2.23-1

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.

Potentially problematic release.


This version of @ema.co/mcp-toolkit might be problematic. Click here for more details.

Files changed (67) hide show
  1. package/.context/public/guides/ema-user-guide.md +12 -16
  2. package/.context/public/guides/mcp-tools-guide.md +203 -334
  3. package/dist/cli/index.js +2 -2
  4. package/dist/mcp/domain/loop-detection.js +89 -0
  5. package/dist/mcp/domain/sanitizer.js +1 -1
  6. package/dist/mcp/domain/structural-rules.js +4 -5
  7. package/dist/mcp/domain/validation-rules.js +5 -5
  8. package/dist/mcp/domain/workflow-graph.js +3 -5
  9. package/dist/mcp/domain/workflow-path-enumerator.js +7 -4
  10. package/dist/mcp/guidance.js +62 -29
  11. package/dist/mcp/handlers/debug/adapter.js +15 -0
  12. package/dist/mcp/handlers/debug/formatters.js +282 -0
  13. package/dist/mcp/handlers/debug/index.js +133 -0
  14. package/dist/mcp/handlers/demo/adapter.js +180 -0
  15. package/dist/mcp/handlers/env/config.js +2 -2
  16. package/dist/mcp/handlers/feedback/index.js +1 -1
  17. package/dist/mcp/handlers/index.js +0 -1
  18. package/dist/mcp/handlers/persona/adapter.js +135 -0
  19. package/dist/mcp/handlers/persona/index.js +237 -8
  20. package/dist/mcp/handlers/persona/schema.js +27 -0
  21. package/dist/mcp/handlers/reference/index.js +6 -4
  22. package/dist/mcp/handlers/sync/adapter.js +200 -0
  23. package/dist/mcp/handlers/workflow/adapter.js +174 -0
  24. package/dist/mcp/handlers/workflow/fix.js +11 -12
  25. package/dist/mcp/handlers/workflow/index.js +12 -40
  26. package/dist/mcp/handlers/workflow/validation.js +1 -1
  27. package/dist/mcp/knowledge-guidance-topics.js +615 -0
  28. package/dist/mcp/knowledge-types.js +7 -0
  29. package/dist/mcp/knowledge.js +75 -1403
  30. package/dist/mcp/resources-dynamic.js +2395 -0
  31. package/dist/mcp/resources-validation.js +408 -0
  32. package/dist/mcp/resources.js +72 -2508
  33. package/dist/mcp/server.js +69 -2825
  34. package/dist/mcp/tools.js +106 -5
  35. package/dist/sdk/client-adapter.js +265 -24
  36. package/dist/sdk/ema-client.js +100 -9
  37. package/dist/sdk/generated/agent-catalog.js +615 -0
  38. package/dist/sdk/generated/api-client/client/client.gen.js +3 -3
  39. package/dist/sdk/generated/api-client/client/index.js +5 -5
  40. package/dist/sdk/generated/api-client/client/utils.gen.js +4 -4
  41. package/dist/sdk/generated/api-client/client.gen.js +1 -1
  42. package/dist/sdk/generated/api-client/core/utils.gen.js +1 -1
  43. package/dist/sdk/generated/api-client/index.js +1 -1
  44. package/dist/sdk/generated/api-client/sdk.gen.js +2 -2
  45. package/dist/sdk/generated/well-known-types.js +99 -0
  46. package/dist/sdk/generated/widget-catalog.js +60 -0
  47. package/dist/sdk/grpc-client.js +115 -1
  48. package/dist/sync/sdk.js +2 -2
  49. package/dist/sync.js +4 -3
  50. package/docs/README.md +17 -9
  51. package/package.json +4 -3
  52. package/.context/public/guides/dashboard-operations.md +0 -349
  53. package/.context/public/guides/email-patterns.md +0 -125
  54. package/.context/public/guides/workflow-builder-patterns.md +0 -708
  55. package/dist/mcp/domain/intent-architect.js +0 -914
  56. package/dist/mcp/domain/quality-gates.js +0 -110
  57. package/dist/mcp/domain/workflow-execution-analyzer.js +0 -412
  58. package/dist/mcp/domain/workflow-intent.js +0 -1806
  59. package/dist/mcp/domain/workflow-merge.js +0 -449
  60. package/dist/mcp/domain/workflow-tracer.js +0 -648
  61. package/dist/mcp/domain/workflow-transformer.js +0 -742
  62. package/dist/mcp/handlers/knowledge/index.js +0 -54
  63. package/dist/mcp/handlers/persona/intent.js +0 -141
  64. package/dist/mcp/handlers/workflow/analyze.js +0 -119
  65. package/dist/mcp/handlers/workflow/compare.js +0 -70
  66. package/dist/mcp/handlers/workflow/generate.js +0 -384
  67. package/dist/mcp/handlers-consolidated.js +0 -333
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Workflow V2 Adapter
3
+ *
4
+ * Handles file-path resolution for large workflow_def payloads, fingerprint-based
5
+ * stale-state protection, pre-deploy snapshots, and mode routing to handleWorkflow.
6
+ *
7
+ * Extracted from server.ts to keep the dispatch table thin.
8
+ */
9
+ import { fingerprintPersona } from "../../../sync.js";
10
+ import { createVersionStorage } from "../../../sync/version-storage.js";
11
+ import { createVersionPolicyEngine } from "../../../sync/version-policy.js";
12
+ import { handleWorkflow } from "./index.js";
13
+ export async function handleWorkflowAdapter(args, createClient, getDefaultEnvName) {
14
+ const normalizedArgs = { ...(args ?? {}) };
15
+ const personaId = normalizedArgs.persona_id ? String(normalizedArgs.persona_id) : undefined;
16
+ let workflowDef = normalizedArgs.workflow_def;
17
+ const workflowDefPath = normalizedArgs.workflow_def_path;
18
+ const mode = normalizedArgs.mode ? String(normalizedArgs.mode) : undefined;
19
+ const baseFingerprint = normalizedArgs.base_fingerprint;
20
+ const force = normalizedArgs.force;
21
+ // Resolve workflow_def from file when workflow_def_path is provided (large payloads).
22
+ // Also handle the common agent mistake: passing {"workflow_def_path": "/path"} as workflow_def.
23
+ let effectivePath = workflowDefPath;
24
+ if (mode === "deploy" && workflowDef && !effectivePath) {
25
+ const keys = Object.keys(workflowDef);
26
+ if (keys.length === 1 && keys[0] === "workflow_def_path" && typeof workflowDef.workflow_def_path === "string") {
27
+ effectivePath = workflowDef.workflow_def_path;
28
+ workflowDef = undefined;
29
+ }
30
+ }
31
+ if (mode === "deploy" && !workflowDef && effectivePath) {
32
+ try {
33
+ const path = await import("path");
34
+ if (!path.isAbsolute(effectivePath)) {
35
+ return { error: "workflow_def_path must be an absolute path on the MCP server host", path: effectivePath };
36
+ }
37
+ if (!effectivePath.toLowerCase().endsWith(".json")) {
38
+ return { error: "workflow_def_path must point to a .json file", path: effectivePath };
39
+ }
40
+ const fs = await import("fs/promises");
41
+ const stat = await fs.stat(effectivePath);
42
+ const MAX_WORKFLOW_DEF_BYTES = 1024 * 1024; // 1MB
43
+ if (stat.size > MAX_WORKFLOW_DEF_BYTES) {
44
+ return { error: `workflow_def_path file too large (${stat.size} bytes)`, max_bytes: MAX_WORKFLOW_DEF_BYTES, path: effectivePath };
45
+ }
46
+ const raw = await fs.readFile(effectivePath, "utf-8");
47
+ const parsed = JSON.parse(raw);
48
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
49
+ workflowDef = parsed;
50
+ }
51
+ else {
52
+ return { error: "workflow_def_path must point to a JSON object", path: effectivePath };
53
+ }
54
+ }
55
+ catch (err) {
56
+ const msg = err instanceof Error ? err.message : String(err);
57
+ return { error: `Failed to read workflow_def from path: ${msg}`, path: effectivePath };
58
+ }
59
+ }
60
+ const client = createClient(normalizedArgs.env);
61
+ switch (mode) {
62
+ case "get": {
63
+ return handleWorkflow({
64
+ mode: "get",
65
+ persona_id: personaId,
66
+ env: normalizedArgs.env,
67
+ }, client, () => undefined);
68
+ }
69
+ case "validate": {
70
+ return handleWorkflow({
71
+ mode: "validate",
72
+ persona_id: personaId,
73
+ workflow_def: normalizedArgs.workflow_def,
74
+ workflow_spec: normalizedArgs.workflow_spec,
75
+ validation_type: normalizedArgs.validation_type,
76
+ max_paths: normalizedArgs.max_paths,
77
+ timeout_ms: normalizedArgs.timeout_ms,
78
+ env: normalizedArgs.env,
79
+ }, client, () => undefined);
80
+ }
81
+ case "deploy": {
82
+ if (!personaId) {
83
+ return { error: 'persona_id is required for workflow(mode="deploy")' };
84
+ }
85
+ const targetEnv = normalizedArgs.env ?? getDefaultEnvName();
86
+ let versionCreated;
87
+ try {
88
+ const personaBefore = await client.getPersonaById(personaId);
89
+ if (!personaBefore) {
90
+ return { error: `Persona not found: ${personaId}` };
91
+ }
92
+ const currentFp = fingerprintPersona(personaBefore);
93
+ if (!force && !baseFingerprint) {
94
+ return {
95
+ error: "base_fingerprint is required for workflow deploy (stale-state protection)",
96
+ persona_id: personaId,
97
+ current_fingerprint: currentFp,
98
+ hint: "Run workflow(mode='get', persona_id='...') immediately before deploying and pass fingerprint as base_fingerprint. Use force=true only for emergency overrides.",
99
+ };
100
+ }
101
+ if (!force && baseFingerprint && baseFingerprint !== currentFp) {
102
+ return {
103
+ error: "Persona changed since you last fetched it (fingerprint mismatch)",
104
+ persona_id: personaId,
105
+ base_fingerprint: baseFingerprint,
106
+ current_fingerprint: currentFp,
107
+ hint: "Re-run workflow(mode='get') to fetch the latest workflow_def, re-apply your edits, then deploy again. Use force=true only if you intend to overwrite out-of-band changes.",
108
+ };
109
+ }
110
+ const storage = createVersionStorage(process.cwd());
111
+ const engine = createVersionPolicyEngine(storage);
112
+ const snap = engine.forceCreateVersion(personaBefore, {
113
+ environment: targetEnv,
114
+ tenant_id: targetEnv,
115
+ message: "Pre-deploy snapshot",
116
+ created_by: "mcp-toolkit",
117
+ });
118
+ if (!snap.created || !snap.version) {
119
+ if (!force) {
120
+ return {
121
+ error: "Failed to create pre-deploy snapshot (required before deploy)",
122
+ persona_id: personaId,
123
+ details: snap.reason,
124
+ hint: "Fix snapshotting (workspace storage) or retry with force=true for emergency override.",
125
+ };
126
+ }
127
+ }
128
+ else {
129
+ versionCreated = { id: snap.version.id, version_name: snap.version.version_name };
130
+ }
131
+ }
132
+ catch (snapshotErr) {
133
+ if (!force) {
134
+ return {
135
+ error: "Failed to create pre-deploy snapshot (required before deploy)",
136
+ persona_id: personaId,
137
+ hint: "Retry after fixing local workspace write access, or use force=true for emergency override.",
138
+ };
139
+ }
140
+ }
141
+ const deployResult = await handleWorkflow({
142
+ mode: "deploy",
143
+ persona_id: personaId,
144
+ workflow_def: workflowDef,
145
+ proto_config: normalizedArgs.proto_config,
146
+ env: normalizedArgs.env,
147
+ force: force,
148
+ strict_validation: normalizedArgs.strict_validation,
149
+ }, client, () => undefined);
150
+ if (versionCreated && deployResult && typeof deployResult === "object") {
151
+ deployResult.version_snapshot = versionCreated;
152
+ }
153
+ return deployResult;
154
+ }
155
+ case "analyze":
156
+ case "compare":
157
+ case "compile":
158
+ case "optimize":
159
+ case "generate": {
160
+ return {
161
+ error: `Mode "${mode}" removed - LLM does this thinking`,
162
+ hint: "Use workflow(mode='get') to fetch data, then analyze/generate in your reasoning. Deploy with workflow(mode='deploy').",
163
+ valid_modes: ["get", "validate", "deploy"],
164
+ };
165
+ }
166
+ default: {
167
+ return {
168
+ error: `Mode required. Valid modes: get, validate, deploy`,
169
+ hint: "workflow(mode='get') returns data for LLM. workflow(mode='validate') validates specs. workflow(mode='deploy') executes LLM's workflow_def.",
170
+ example: `workflow(mode="get", persona_id="...")`,
171
+ };
172
+ }
173
+ }
174
+ }
@@ -42,18 +42,17 @@ export function summarizeWorkflow(workflowDef) {
42
42
  if (nodeNames.length > 0) {
43
43
  lines.push(`\nNodes: ${nodeNames.slice(0, 20).join(", ")}${nodeNames.length > 20 ? "..." : ""}`);
44
44
  }
45
- // TODO: This uses keyword matching to detect workflow features.
46
- // Consider using ActionRegistry metadata instead for consistency with LLM-driven architecture.
47
- // Current approach is acceptable for Auto Builder descriptions but could be improved.
48
- // Check for categorizer (indicates intent routing)
49
- const hasCategorizer = Array.from(actionTypes.keys()).some(t => t.includes("categorizer"));
50
- if (hasCategorizer) {
51
- lines.push("\nThis workflow uses intent-based routing with a categorizer.");
52
- }
53
- // Check for HITL
54
- const hasHitl = Array.from(actionTypes.keys()).some(t => t.includes("hitl") || t.includes("general_hitl"));
55
- if (hasHitl) {
56
- lines.push("This workflow includes human-in-the-loop approval steps.");
45
+ const FEATURE_INDICATORS = {
46
+ categorizer: "This workflow uses intent-based routing with a categorizer.",
47
+ hitl: "This workflow includes human-in-the-loop approval steps.",
48
+ general_hitl: "This workflow includes human-in-the-loop approval steps.",
49
+ entity_extraction: "This workflow extracts structured data from documents.",
50
+ search: "This workflow searches a knowledge base.",
51
+ };
52
+ for (const [actionType, description] of Object.entries(FEATURE_INDICATORS)) {
53
+ if (actionTypes.has(actionType)) {
54
+ lines.push(`\n${description}`);
55
+ }
57
56
  }
58
57
  // Include raw workflow JSON for Auto Builder to parse
59
58
  lines.push("\n--- Full workflow_def JSON ---");
@@ -22,18 +22,13 @@ import { fingerprintPersona } from "../../../sync.js";
22
22
  export * from "./types.js";
23
23
  // Re-export utilities
24
24
  export * from "./utils.js";
25
- // Import internal handlers (not exposed as tool modes, but used internally)
26
- import { handleWorkflowAnalyze } from "./analyze.js";
27
- import { handleWorkflowCompare } from "./compare.js";
25
+ // Import internal handlers
28
26
  import { handleWorkflowOptimize } from "./optimize.js";
29
27
  import { handleWorkflowDeploy } from "./deploy.js";
30
28
  import { handleWorkflowValidate } from "./validate.js";
31
29
  // Re-export for internal use (persona handler routes to these)
32
- export { handleWorkflowAnalyze } from "./analyze.js";
33
- export { handleWorkflowCompare } from "./compare.js";
34
30
  export { handleWorkflowOptimize } from "./optimize.js";
35
31
  export { handleWorkflowDeploy } from "./deploy.js";
36
- export { handleWorkflowGenerate } from "./generate.js";
37
32
  // NOTE: modify.ts REMOVED - violated LLM-driven architecture
38
33
  // The correct flow is: workflow(mode="get") → LLM modifies → workflow(mode="deploy")
39
34
  // Fallback deprecated actions - ONLY used when API unavailable
@@ -265,13 +260,17 @@ export async function handleWorkflow(args, client, _getTemplateId) {
265
260
  _tip: "The LLM generates the full workflow_def. MCP just deploys it.",
266
261
  };
267
262
  }
268
- // DEPRECATED: analyze, compare - LLM can do this directly
269
- // Kept for backwards compat but may be removed
270
- if (mode === "analyze") {
271
- return handleWorkflowAnalyze(args, client);
272
- }
273
- if (mode === "compare") {
274
- return handleWorkflowCompare(args, client);
263
+ // REMOVED: analyze, compare - LLM does this by fetching rules and comparing
264
+ if (mode === "analyze" || mode === "compare") {
265
+ return {
266
+ error: `Mode "${mode}" has been removed.`,
267
+ reason: "LLM can do analysis/comparison by fetching ema://rules/* and reasoning about workflow_def.",
268
+ correct_flow: [
269
+ "1. workflow(mode='get', persona_id='...') - get current workflow_def",
270
+ "2. Fetch ema://rules/anti-patterns and ema://rules/structural-invariants",
271
+ "3. LLM analyzes/compares the data",
272
+ ],
273
+ };
275
274
  }
276
275
  // Auto-detect: only deploy if workflow_def provided
277
276
  if (personaId && workflowDef) {
@@ -288,30 +287,3 @@ export async function handleWorkflow(args, client, _getTemplateId) {
288
287
  hint: "MCP provides data (get), validates (validate), optimizes (optimize), and executes (deploy). LLM does all thinking.",
289
288
  };
290
289
  }
291
- /**
292
- * Check if a workflow mode has been extracted
293
- */
294
- export function hasExtractedWorkflowHandler(mode) {
295
- // PUBLIC: get, deploy, validate, optimize
296
- // DEPRECATED (kept for compat): analyze, compare
297
- // REMOVED: modify, extend, generate (violated LLM-driven architecture)
298
- const extractedModes = ["get", "deploy", "validate", "analyze", "optimize", "compare"];
299
- return extractedModes.includes(mode);
300
- }
301
- /**
302
- * Get handler for a specific workflow mode
303
- */
304
- export function getWorkflowModeHandler(mode) {
305
- switch (mode) {
306
- case "get":
307
- case "deploy":
308
- case "validate":
309
- case "analyze":
310
- case "optimize":
311
- case "compare":
312
- return handleWorkflow;
313
- // REMOVED: modify, extend, generate
314
- default:
315
- return undefined;
316
- }
317
- }
@@ -706,7 +706,7 @@ export function validateExternalActionCallerTools(workflowDef) {
706
706
  // ═══════════════════════════════════════════════════════════════════════════
707
707
  // LOOP DETECTION VALIDATION
708
708
  // ═══════════════════════════════════════════════════════════════════════════
709
- import { detectLoops } from "../../domain/workflow-execution-analyzer.js";
709
+ import { detectLoops } from "../../domain/loop-detection.js";
710
710
  /**
711
711
  * Validate that workflow has no circular dependencies.
712
712
  * Circular references cause infinite execution loops.