@ema.co/mcp-toolkit 2026.1.26 → 2026.1.27-10
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/mcp/handlers/action/index.js +17 -20
- package/dist/mcp/handlers/data/index.js +75 -6
- package/dist/mcp/handlers/deprecation.js +50 -0
- package/dist/mcp/handlers/env/index.js +3 -3
- package/dist/mcp/handlers/knowledge/index.js +44 -237
- package/dist/mcp/handlers/persona/create.js +63 -18
- package/dist/mcp/handlers/persona/index.js +9 -10
- package/dist/mcp/handlers/persona/update.js +6 -3
- package/dist/mcp/handlers/reference/index.js +15 -2
- package/dist/mcp/handlers/sync/index.js +3 -18
- package/dist/mcp/handlers/workflow/analyze.js +53 -105
- package/dist/mcp/handlers/workflow/deploy.js +160 -1
- package/dist/mcp/handlers/workflow/generate.js +8 -28
- package/dist/mcp/handlers/workflow/index.js +258 -85
- package/dist/mcp/handlers/workflow/modify.js +69 -26
- package/dist/mcp/handlers/workflow/optimize.js +22 -108
- package/dist/mcp/handlers/workflow/utils.js +0 -102
- package/dist/mcp/handlers-consolidated.js +15 -38
- package/dist/mcp/prompts.js +82 -44
- package/dist/mcp/resources.js +335 -3
- package/dist/mcp/server.js +242 -457
- package/dist/mcp/tools.js +44 -61
- package/dist/sdk/action-schema-parser.js +11 -5
- package/dist/sdk/client.js +81 -25
- package/dist/sdk/ema-client.js +11 -0
- package/dist/sdk/generated/deprecated-actions.js +171 -0
- package/dist/sdk/guidance.js +58 -35
- package/dist/sdk/index.js +8 -7
- package/dist/sdk/knowledge.js +216 -1932
- package/dist/sdk/quality-gates.js +60 -336
- package/dist/sdk/validation-rules.js +33 -0
- package/dist/sdk/workflow-fixer.js +29 -360
- package/dist/sdk/workflow-generator.js +2 -1
- package/dist/sdk/workflow-intent.js +43 -3
- package/dist/sdk/workflow-transformer.js +0 -342
- package/docs/dashboard-operations.md +35 -0
- package/docs/ema-user-guide.md +66 -0
- package/docs/mcp-tools-guide.md +74 -45
- package/package.json +2 -2
- package/dist/mcp/handlers/persona/analyze.js +0 -275
- package/dist/mcp/handlers/persona/compare.js +0 -32
- package/dist/mcp/handlers/workflow/compile.js +0 -39
- package/docs/DEBUG-ANALYSIS-unused-category-type-mismatch.md +0 -481
- package/docs/TODO-fix-analyzer-and-modify.md +0 -182
- package/resources/action-schema.json +0 -5678
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Persona ANALYZE mode handler
|
|
3
|
-
*
|
|
4
|
-
* Analyzes a persona for issues in workflow, config, and data.
|
|
5
|
-
* Returns decompiled WorkflowSpec + schema for LLM-native modifications.
|
|
6
|
-
*
|
|
7
|
-
* ARCHITECTURE NOTE: MCP provides context, Agent proposes fixes.
|
|
8
|
-
* The `fix` parameter is DEPRECATED - agent should build workflow_spec fixes.
|
|
9
|
-
*/
|
|
10
|
-
import { resolvePersona } from "../utils.js";
|
|
11
|
-
import { detectWorkflowIssues, suggestWorkflowFixes } from "../../../sdk/knowledge.js";
|
|
12
|
-
import { autoFixWorkflow } from "../../../sdk/workflow-fixer.js";
|
|
13
|
-
import { decompileWorkflow, summarizeSpec, WORKFLOW_SCHEMA_FOR_LLM } from "../../../sdk/workflow-transformer.js";
|
|
14
|
-
import { hasFallback, GUIDANCE_RULES } from "../../../sdk/guidance.js";
|
|
15
|
-
/**
|
|
16
|
-
* Handle persona(mode="analyze") - analyze persona for issues
|
|
17
|
-
*
|
|
18
|
-
* @param args.id - Persona ID or name (required)
|
|
19
|
-
* @param args.fix - DEPRECATED: Returns preview instead of deploying. Agent should build workflow_spec.
|
|
20
|
-
* @param args.include - Aspects to analyze (workflow, config, data)
|
|
21
|
-
*/
|
|
22
|
-
export async function handleAnalyze(args, client) {
|
|
23
|
-
const idOrName = (args.id ?? args.identifier);
|
|
24
|
-
if (!idOrName) {
|
|
25
|
-
return { status: "error", error: "id required for analyze mode" };
|
|
26
|
-
}
|
|
27
|
-
const persona = await resolvePersona(client, idOrName);
|
|
28
|
-
if (!persona) {
|
|
29
|
-
return { status: "error", error: `Persona not found: ${idOrName}` };
|
|
30
|
-
}
|
|
31
|
-
const fix = args.fix;
|
|
32
|
-
const includeAspects = args.include ?? ["workflow", "config", "data"];
|
|
33
|
-
// Collect issues across all aspects
|
|
34
|
-
const issues = [];
|
|
35
|
-
const fixesApplied = [];
|
|
36
|
-
// ── Analyze Workflow ──
|
|
37
|
-
if (includeAspects.includes("workflow") && persona.workflow_def) {
|
|
38
|
-
const workflowDef = persona.workflow_def;
|
|
39
|
-
// Use the sophisticated workflow issue detection
|
|
40
|
-
const workflowIssues = detectWorkflowIssues(workflowDef);
|
|
41
|
-
const suggestedFixes = suggestWorkflowFixes(workflowIssues);
|
|
42
|
-
// Map workflow issues to our uniform format
|
|
43
|
-
for (const issue of workflowIssues) {
|
|
44
|
-
const wfIssue = issue;
|
|
45
|
-
const severityMap = {
|
|
46
|
-
"critical": "critical",
|
|
47
|
-
"warning": "warning",
|
|
48
|
-
"info": "info",
|
|
49
|
-
};
|
|
50
|
-
const matchingFix = suggestedFixes.find(f => f.issue_type === wfIssue.type);
|
|
51
|
-
issues.push({
|
|
52
|
-
id: `wf-${wfIssue.type}-${wfIssue.node || "global"}`,
|
|
53
|
-
severity: severityMap[wfIssue.severity] || "warning",
|
|
54
|
-
category: "workflow",
|
|
55
|
-
title: `${wfIssue.type}${wfIssue.node ? `: ${wfIssue.node}` : ""}`,
|
|
56
|
-
description: wfIssue.reason,
|
|
57
|
-
auto_fixable: !!matchingFix,
|
|
58
|
-
fix_description: matchingFix?.description,
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
// ── Analyze Config ──
|
|
63
|
-
if (includeAspects.includes("config") && persona.proto_config) {
|
|
64
|
-
const protoConfig = persona.proto_config;
|
|
65
|
-
const widgets = (protoConfig.widgets || []);
|
|
66
|
-
// Check for malformed widgets
|
|
67
|
-
for (const widget of widgets) {
|
|
68
|
-
const widgetName = widget.name;
|
|
69
|
-
if (!widgetName || widgetName.trim() === "") {
|
|
70
|
-
issues.push({
|
|
71
|
-
id: `widget-empty-name`,
|
|
72
|
-
severity: "warning",
|
|
73
|
-
category: "config",
|
|
74
|
-
title: "Widget with empty name",
|
|
75
|
-
description: "A widget has an empty or missing name field",
|
|
76
|
-
auto_fixable: false,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
// Check for generic description
|
|
81
|
-
const description = persona.description;
|
|
82
|
-
if (!description || description.length < 20 || description.includes("template")) {
|
|
83
|
-
issues.push({
|
|
84
|
-
id: `generic-description`,
|
|
85
|
-
severity: "info",
|
|
86
|
-
category: "metadata",
|
|
87
|
-
title: "Generic or missing description",
|
|
88
|
-
description: "Persona description appears to be a template default or too short",
|
|
89
|
-
auto_fixable: false,
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
// ── Analyze Data Sources ──
|
|
94
|
-
if (includeAspects.includes("data")) {
|
|
95
|
-
try {
|
|
96
|
-
const dataSources = await client.listDataSourceFiles(persona.id);
|
|
97
|
-
const files = dataSources.files || [];
|
|
98
|
-
// Check data source health
|
|
99
|
-
if (files.length === 0) {
|
|
100
|
-
issues.push({
|
|
101
|
-
id: `no-data-sources`,
|
|
102
|
-
severity: "info",
|
|
103
|
-
category: "data",
|
|
104
|
-
title: "No data sources uploaded",
|
|
105
|
-
description: "Persona has no knowledge files - Knowledge Agent won't have context to search",
|
|
106
|
-
auto_fixable: false,
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
// Check for failed uploads
|
|
110
|
-
for (const file of files) {
|
|
111
|
-
if (file.status === "failed" || file.status === "error") {
|
|
112
|
-
issues.push({
|
|
113
|
-
id: `failed-upload-${file.id}`,
|
|
114
|
-
severity: "warning",
|
|
115
|
-
category: "data",
|
|
116
|
-
title: `Failed upload: ${file.filename}`,
|
|
117
|
-
description: `File upload failed with status: ${file.status}`,
|
|
118
|
-
auto_fixable: false,
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
// Data source listing may fail for some persona types
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
// ── Handle fix parameter (DEPRECATED) ──
|
|
128
|
-
// DEPRECATED: fix=true no longer auto-deploys. Returns preview for agent to review.
|
|
129
|
-
// Architecture: MCP provides context, Agent proposes and builds fixes.
|
|
130
|
-
let fixPreview;
|
|
131
|
-
if (fix && persona.workflow_def) {
|
|
132
|
-
// Log deprecation warning
|
|
133
|
-
console.warn("[DEPRECATED] fix=true is deprecated. MCP should not auto-fix - agent builds workflow_spec.\n" +
|
|
134
|
-
"See CLAUDE.md 'MCP vs Agent: Separation of Duties'.\n" +
|
|
135
|
-
"Returning preview instead of deploying.");
|
|
136
|
-
const workflowDef = persona.workflow_def;
|
|
137
|
-
const fixResult = autoFixWorkflow(workflowDef);
|
|
138
|
-
// Record what would be fixed
|
|
139
|
-
for (const applied of fixResult.fixesApplied) {
|
|
140
|
-
fixesApplied.push({
|
|
141
|
-
issue_id: applied.issueType,
|
|
142
|
-
action: applied.description,
|
|
143
|
-
result: "preview", // Changed from success/failed to preview
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
// Return preview instead of deploying
|
|
147
|
-
if (fixResult.fixesApplied.length > 0 && fixResult.success) {
|
|
148
|
-
// Determine persona type for decompile
|
|
149
|
-
const protoConfig = persona.proto_config;
|
|
150
|
-
const projectSettings = protoConfig?.projectSettings;
|
|
151
|
-
const personaType = projectSettings?.projectType === 5 ? "voice" : "chat";
|
|
152
|
-
try {
|
|
153
|
-
fixPreview = {
|
|
154
|
-
proposed_spec: decompileWorkflow(fixResult.workflowDef, personaType),
|
|
155
|
-
changes: fixResult.fixesApplied.map(f => f.description),
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
// Decompile failed, just note the changes
|
|
160
|
-
fixPreview = {
|
|
161
|
-
changes: fixResult.fixesApplied.map(f => f.description),
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
// ── Build Response ──
|
|
167
|
-
const analysisSummary = {
|
|
168
|
-
total_issues: issues.length,
|
|
169
|
-
by_severity: {
|
|
170
|
-
critical: issues.filter(i => i.severity === "critical").length,
|
|
171
|
-
warning: issues.filter(i => i.severity === "warning").length,
|
|
172
|
-
info: issues.filter(i => i.severity === "info").length,
|
|
173
|
-
},
|
|
174
|
-
by_category: {
|
|
175
|
-
workflow: issues.filter(i => i.category === "workflow").length,
|
|
176
|
-
config: issues.filter(i => i.category === "config").length,
|
|
177
|
-
data: issues.filter(i => i.category === "data").length,
|
|
178
|
-
metadata: issues.filter(i => i.category === "metadata").length,
|
|
179
|
-
},
|
|
180
|
-
};
|
|
181
|
-
const status = analysisSummary.by_severity.critical > 0
|
|
182
|
-
? "critical"
|
|
183
|
-
: analysisSummary.total_issues > 0
|
|
184
|
-
? "issues_found"
|
|
185
|
-
: "healthy";
|
|
186
|
-
// ── Decompile Workflow for LLM-native modifications ──
|
|
187
|
-
let workflowSpec;
|
|
188
|
-
let workflowSummary;
|
|
189
|
-
if (persona.workflow_def) {
|
|
190
|
-
const workflowDef = persona.workflow_def;
|
|
191
|
-
// Determine persona type
|
|
192
|
-
const protoConfig = persona.proto_config;
|
|
193
|
-
const projectSettings = protoConfig?.projectSettings;
|
|
194
|
-
const personaType = projectSettings?.projectType === 5 ? "voice" : "chat";
|
|
195
|
-
try {
|
|
196
|
-
workflowSpec = decompileWorkflow(workflowDef, personaType);
|
|
197
|
-
workflowSummary = summarizeSpec(workflowSpec);
|
|
198
|
-
}
|
|
199
|
-
catch (e) {
|
|
200
|
-
// Decompile failed - include error in response
|
|
201
|
-
workflowSummary = `Decompile failed: ${e instanceof Error ? e.message : String(e)}`;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
const result = {
|
|
205
|
-
status,
|
|
206
|
-
persona: { id: persona.id, name: persona.name },
|
|
207
|
-
summary: analysisSummary,
|
|
208
|
-
issues,
|
|
209
|
-
};
|
|
210
|
-
// Include workflow_spec for LLM-native modifications (unless include_spec=false)
|
|
211
|
-
const includeSpec = args.include_spec !== false;
|
|
212
|
-
if (includeSpec && workflowSpec) {
|
|
213
|
-
result.workflow_spec = workflowSpec;
|
|
214
|
-
result.workflow_summary = workflowSummary;
|
|
215
|
-
result.schema = WORKFLOW_SCHEMA_FOR_LLM;
|
|
216
|
-
result.modification_hint =
|
|
217
|
-
"To modify this workflow:\n" +
|
|
218
|
-
"1. Analyze the workflow_spec above\n" +
|
|
219
|
-
"2. Modify the spec (add/remove/update nodes, change inputs/outputs)\n" +
|
|
220
|
-
"3. Call persona(id=\"" + persona.id + "\", update={workflow_spec: <modified_spec>})\n" +
|
|
221
|
-
"\n" +
|
|
222
|
-
"The SDK will compile your WorkflowSpec to workflow_def and deploy it.";
|
|
223
|
-
// Add workflow-specific guidance based on state
|
|
224
|
-
const workflowGuidance = [];
|
|
225
|
-
// Check for Fallback in categorizers
|
|
226
|
-
if (!hasFallback(workflowSpec)) {
|
|
227
|
-
const fallbackRule = GUIDANCE_RULES.find(r => r.id === "categorizer-fallback");
|
|
228
|
-
if (fallbackRule) {
|
|
229
|
-
workflowGuidance.push(`⚠️ ${fallbackRule.title}: ${fallbackRule.do}`);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
// Add general workflow tips
|
|
233
|
-
const previewRule = GUIDANCE_RULES.find(r => r.id === "preview-before-deploy");
|
|
234
|
-
if (previewRule) {
|
|
235
|
-
workflowGuidance.push(`💡 ${previewRule.title}: ${previewRule.do}`);
|
|
236
|
-
}
|
|
237
|
-
if (workflowGuidance.length > 0) {
|
|
238
|
-
result.workflow_guidance = workflowGuidance;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
if (fix) {
|
|
242
|
-
// DEPRECATED: Return preview, not deployment result
|
|
243
|
-
result.deprecation_warning =
|
|
244
|
-
"fix=true is DEPRECATED. MCP should not auto-fix workflows.\n" +
|
|
245
|
-
"The agent should review issues, ask the user which to fix, " +
|
|
246
|
-
"build the workflow_spec, and call persona(update={workflow_spec: ...})";
|
|
247
|
-
if (fixPreview) {
|
|
248
|
-
result.fix_preview = {
|
|
249
|
-
status: "preview_only",
|
|
250
|
-
proposed_changes: fixPreview.changes,
|
|
251
|
-
proposed_spec: fixPreview.proposed_spec,
|
|
252
|
-
deploy_command: `persona(id="${persona.id}", update={workflow_spec: <proposed_spec above>})`,
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
else {
|
|
256
|
-
result.fix_preview = {
|
|
257
|
-
status: "no_fixes_available",
|
|
258
|
-
message: "No auto-fixable issues found. Review issues above and build fixes manually.",
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
else {
|
|
263
|
-
// Guide agent to the correct flow (no mention of fix=true)
|
|
264
|
-
result.next_steps = {
|
|
265
|
-
has_issues: issues.length > 0,
|
|
266
|
-
agent_action: issues.length > 0
|
|
267
|
-
? "Review issues above. Ask user which to fix. Build workflow_spec with fixes. Deploy via update."
|
|
268
|
-
: "No issues found. Persona is healthy.",
|
|
269
|
-
manual_actions: issues
|
|
270
|
-
.filter(i => !i.auto_fixable)
|
|
271
|
-
.map(i => `${i.title}: ${i.description}`),
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
return result;
|
|
275
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Persona COMPARE mode handler
|
|
3
|
-
*
|
|
4
|
-
* Compares two personas, optionally across environments.
|
|
5
|
-
*/
|
|
6
|
-
import { resolvePersona, comparePersonas } from "../utils.js";
|
|
7
|
-
/**
|
|
8
|
-
* Handle persona(mode="compare") - compare two personas
|
|
9
|
-
*
|
|
10
|
-
* @param args.id - First persona ID or name (required)
|
|
11
|
-
* @param args.compare_to - Second persona ID or name (required)
|
|
12
|
-
* @param args.compare_env - Optional environment for second persona
|
|
13
|
-
* @param createClientForEnv - Factory to create client for different env
|
|
14
|
-
*/
|
|
15
|
-
export async function handleCompare(args, client, createClientForEnv) {
|
|
16
|
-
const idOrName = (args.id ?? args.identifier);
|
|
17
|
-
if (!idOrName || !args.compare_to) {
|
|
18
|
-
return { error: "id and compare_to required for compare mode" };
|
|
19
|
-
}
|
|
20
|
-
const persona1 = await resolvePersona(client, idOrName);
|
|
21
|
-
const compareEnv = args.compare_env;
|
|
22
|
-
const client2 = compareEnv && createClientForEnv ? createClientForEnv(compareEnv) : client;
|
|
23
|
-
const persona2 = await resolvePersona(client2, args.compare_to);
|
|
24
|
-
if (!persona1 || !persona2) {
|
|
25
|
-
return { error: "One or both personas not found" };
|
|
26
|
-
}
|
|
27
|
-
return {
|
|
28
|
-
persona_1: { id: persona1.id, name: persona1.name },
|
|
29
|
-
persona_2: { id: persona2.id, name: persona2.name, env: compareEnv },
|
|
30
|
-
differences: comparePersonas(persona1, persona2),
|
|
31
|
-
};
|
|
32
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workflow Compile Handler
|
|
3
|
-
*
|
|
4
|
-
* Compiles a WorkflowSpec (simplified nodes array) into a full workflow_def.
|
|
5
|
-
*/
|
|
6
|
-
import { compileWorkflow } from "../../../sdk/workflow-generator.js";
|
|
7
|
-
import { ensureActionRegistry } from "../../../sdk/action-registry.js";
|
|
8
|
-
/**
|
|
9
|
-
* Handle workflow compile mode
|
|
10
|
-
*
|
|
11
|
-
* Accepts nodes in simplified format and compiles to workflow_def.
|
|
12
|
-
* Uses the same pattern as the monolith - passes through to compileWorkflow.
|
|
13
|
-
*/
|
|
14
|
-
export async function handleWorkflowCompile(args, client) {
|
|
15
|
-
const nodes = args.nodes;
|
|
16
|
-
const resultMappings = args.result_mappings;
|
|
17
|
-
if (!nodes) {
|
|
18
|
-
return { error: "nodes required for compile mode" };
|
|
19
|
-
}
|
|
20
|
-
// Load action registry for API-driven action versions/namespaces
|
|
21
|
-
const compileRegistry = await ensureActionRegistry(client);
|
|
22
|
-
// Build spec - use any to allow flexible node format from API
|
|
23
|
-
// compileWorkflow will validate and transform as needed
|
|
24
|
-
const spec = {
|
|
25
|
-
name: args.name || "Compiled Workflow",
|
|
26
|
-
description: args.description || "Generated workflow",
|
|
27
|
-
personaType: args.type || "chat",
|
|
28
|
-
nodes: nodes,
|
|
29
|
-
resultMappings: resultMappings || [],
|
|
30
|
-
};
|
|
31
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
|
-
const compiled = compileWorkflow(spec, { registry: compileRegistry });
|
|
33
|
-
return {
|
|
34
|
-
mode: "compile",
|
|
35
|
-
workflow_def: compiled.workflow_def,
|
|
36
|
-
proto_config: compiled.proto_config,
|
|
37
|
-
registry_loaded: compileRegistry.isLoaded(),
|
|
38
|
-
};
|
|
39
|
-
}
|