@ema.co/mcp-toolkit 2026.2.19 → 2026.2.23
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/dist/cli/index.js +2 -2
- package/dist/mcp/domain/loop-detection.js +46 -54
- package/dist/mcp/domain/sanitizer.js +1 -1
- package/dist/mcp/domain/workflow-graph.js +2 -2
- package/dist/mcp/domain/workflow-path-enumerator.js +7 -4
- package/dist/mcp/guidance.js +53 -0
- 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/index.js +0 -1
- package/dist/mcp/handlers/persona/adapter.js +135 -0
- 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 +0 -24
- package/dist/mcp/knowledge-guidance-topics.js +615 -0
- package/dist/mcp/knowledge.js +23 -612
- package/dist/mcp/resources-dynamic.js +2395 -0
- package/dist/mcp/resources-validation.js +408 -0
- package/dist/mcp/resources.js +72 -2724
- package/dist/mcp/server.js +33 -832
- package/dist/mcp/tools.js +104 -2
- package/dist/sdk/client-adapter.js +265 -24
- package/dist/sdk/ema-client.js +100 -9
- package/dist/sdk/generated/well-known-types.js +99 -0
- package/dist/sdk/grpc-client.js +115 -1
- package/dist/sync/sdk.js +2 -2
- package/dist/sync.js +4 -3
- package/package.json +3 -2
- package/dist/mcp/handlers/knowledge/index.js +0 -54
|
@@ -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 ---");
|
|
@@ -287,27 +287,3 @@ export async function handleWorkflow(args, client, _getTemplateId) {
|
|
|
287
287
|
hint: "MCP provides data (get), validates (validate), optimizes (optimize), and executes (deploy). LLM does all thinking.",
|
|
288
288
|
};
|
|
289
289
|
}
|
|
290
|
-
/**
|
|
291
|
-
* Check if a workflow mode has been extracted
|
|
292
|
-
*/
|
|
293
|
-
export function hasExtractedWorkflowHandler(mode) {
|
|
294
|
-
// PUBLIC: get, deploy, validate, optimize
|
|
295
|
-
// REMOVED: modify, extend, generate, analyze, compare
|
|
296
|
-
const extractedModes = ["get", "deploy", "validate", "optimize"];
|
|
297
|
-
return extractedModes.includes(mode);
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Get handler for a specific workflow mode
|
|
301
|
-
*/
|
|
302
|
-
export function getWorkflowModeHandler(mode) {
|
|
303
|
-
switch (mode) {
|
|
304
|
-
case "get":
|
|
305
|
-
case "deploy":
|
|
306
|
-
case "validate":
|
|
307
|
-
case "optimize":
|
|
308
|
-
return handleWorkflow;
|
|
309
|
-
// REMOVED: modify, extend, generate, analyze, compare
|
|
310
|
-
default:
|
|
311
|
-
return undefined;
|
|
312
|
-
}
|
|
313
|
-
}
|