@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.
- package/.context/public/guides/ema-user-guide.md +12 -16
- package/.context/public/guides/mcp-tools-guide.md +203 -334
- package/dist/cli/index.js +2 -2
- package/dist/mcp/domain/loop-detection.js +89 -0
- package/dist/mcp/domain/sanitizer.js +1 -1
- package/dist/mcp/domain/structural-rules.js +4 -5
- package/dist/mcp/domain/validation-rules.js +5 -5
- package/dist/mcp/domain/workflow-graph.js +3 -5
- package/dist/mcp/domain/workflow-path-enumerator.js +7 -4
- package/dist/mcp/guidance.js +62 -29
- package/dist/mcp/handlers/debug/adapter.js +15 -0
- package/dist/mcp/handlers/debug/formatters.js +282 -0
- package/dist/mcp/handlers/debug/index.js +133 -0
- package/dist/mcp/handlers/demo/adapter.js +180 -0
- package/dist/mcp/handlers/env/config.js +2 -2
- package/dist/mcp/handlers/feedback/index.js +1 -1
- package/dist/mcp/handlers/index.js +0 -1
- package/dist/mcp/handlers/persona/adapter.js +135 -0
- package/dist/mcp/handlers/persona/index.js +237 -8
- package/dist/mcp/handlers/persona/schema.js +27 -0
- package/dist/mcp/handlers/reference/index.js +6 -4
- package/dist/mcp/handlers/sync/adapter.js +200 -0
- package/dist/mcp/handlers/workflow/adapter.js +174 -0
- package/dist/mcp/handlers/workflow/fix.js +11 -12
- package/dist/mcp/handlers/workflow/index.js +12 -40
- package/dist/mcp/handlers/workflow/validation.js +1 -1
- package/dist/mcp/knowledge-guidance-topics.js +615 -0
- package/dist/mcp/knowledge-types.js +7 -0
- package/dist/mcp/knowledge.js +75 -1403
- package/dist/mcp/resources-dynamic.js +2395 -0
- package/dist/mcp/resources-validation.js +408 -0
- package/dist/mcp/resources.js +72 -2508
- package/dist/mcp/server.js +69 -2825
- package/dist/mcp/tools.js +106 -5
- package/dist/sdk/client-adapter.js +265 -24
- package/dist/sdk/ema-client.js +100 -9
- package/dist/sdk/generated/agent-catalog.js +615 -0
- package/dist/sdk/generated/api-client/client/client.gen.js +3 -3
- package/dist/sdk/generated/api-client/client/index.js +5 -5
- package/dist/sdk/generated/api-client/client/utils.gen.js +4 -4
- package/dist/sdk/generated/api-client/client.gen.js +1 -1
- package/dist/sdk/generated/api-client/core/utils.gen.js +1 -1
- package/dist/sdk/generated/api-client/index.js +1 -1
- package/dist/sdk/generated/api-client/sdk.gen.js +2 -2
- package/dist/sdk/generated/well-known-types.js +99 -0
- package/dist/sdk/generated/widget-catalog.js +60 -0
- package/dist/sdk/grpc-client.js +115 -1
- package/dist/sync/sdk.js +2 -2
- package/dist/sync.js +4 -3
- package/docs/README.md +17 -9
- package/package.json +4 -3
- package/.context/public/guides/dashboard-operations.md +0 -349
- package/.context/public/guides/email-patterns.md +0 -125
- package/.context/public/guides/workflow-builder-patterns.md +0 -708
- package/dist/mcp/domain/intent-architect.js +0 -914
- package/dist/mcp/domain/quality-gates.js +0 -110
- package/dist/mcp/domain/workflow-execution-analyzer.js +0 -412
- package/dist/mcp/domain/workflow-intent.js +0 -1806
- package/dist/mcp/domain/workflow-merge.js +0 -449
- package/dist/mcp/domain/workflow-tracer.js +0 -648
- package/dist/mcp/domain/workflow-transformer.js +0 -742
- package/dist/mcp/handlers/knowledge/index.js +0 -54
- package/dist/mcp/handlers/persona/intent.js +0 -141
- package/dist/mcp/handlers/workflow/analyze.js +0 -119
- package/dist/mcp/handlers/workflow/compare.js +0 -70
- package/dist/mcp/handlers/workflow/generate.js +0 -384
- 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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
|
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
|
-
//
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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/
|
|
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.
|