@ema.co/mcp-toolkit 2026.1.26-4 → 2026.1.27
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 +14 -2
- package/dist/mcp/handlers/data/index.js +72 -6
- package/dist/mcp/handlers/env/index.js +3 -3
- package/dist/mcp/handlers/reference/index.js +15 -2
- package/dist/mcp/handlers/workflow/analyze.js +53 -105
- package/dist/mcp/handlers/workflow/deploy.js +15 -0
- package/dist/mcp/handlers/workflow/generate.js +3 -6
- package/dist/mcp/handlers/workflow/index.js +18 -3
- package/dist/mcp/handlers/workflow/modify.js +9 -29
- package/dist/mcp/handlers/workflow/optimize.js +22 -108
- package/dist/mcp/prompts.js +12 -15
- package/dist/mcp/resources.js +8 -0
- package/dist/mcp/server.js +196 -250
- package/dist/mcp/tools.js +25 -8
- package/dist/sdk/action-schema-parser.js +11 -5
- package/dist/sdk/client.js +1 -1
- package/dist/sdk/guidance.js +58 -35
- package/dist/sdk/index.js +8 -7
- package/dist/sdk/knowledge.js +99 -1938
- 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-intent.js +43 -3
- package/docs/dashboard-operations.md +35 -0
- package/docs/ema-user-guide.md +66 -0
- package/docs/mcp-tools-guide.md +40 -3
- package/package.json +1 -2
- package/resources/action-schema.json +0 -5678
|
@@ -1,80 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Workflow Optimize Handler
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* DEPRECATED: This handler returned pre-computed fixes which violates
|
|
5
|
+
* the "MCP = data, LLM = logic" principle.
|
|
6
|
+
*
|
|
7
|
+
* The LLM should:
|
|
8
|
+
* 1. Get workflow with workflow(mode="get")
|
|
9
|
+
* 2. Fetch rules from ema://rules/anti-patterns
|
|
10
|
+
* 3. Apply rules and propose fixes
|
|
11
|
+
* 4. Deploy via workflow(mode="deploy")
|
|
5
12
|
*/
|
|
6
|
-
import { detectWorkflowIssues, suggestWorkflowFixes } from "../../../sdk/knowledge.js";
|
|
7
|
-
/**
|
|
8
|
-
* Apply simple workflow fixes for auto_fixable issues
|
|
9
|
-
* Handles orphan removal with cascading dependency cleanup
|
|
10
|
-
*/
|
|
11
|
-
function applySimpleFixes(workflow, issues) {
|
|
12
|
-
// Deep clone to avoid mutating original
|
|
13
|
-
const fixed = JSON.parse(JSON.stringify(workflow));
|
|
14
|
-
// Get nodes array from workflow (handle different structures)
|
|
15
|
-
const getNodes = (w) => {
|
|
16
|
-
if (Array.isArray(w.nodes))
|
|
17
|
-
return w.nodes;
|
|
18
|
-
const wd = w.workflow_def;
|
|
19
|
-
if (wd && Array.isArray(wd.nodes))
|
|
20
|
-
return wd.nodes;
|
|
21
|
-
return undefined;
|
|
22
|
-
};
|
|
23
|
-
const nodes = getNodes(fixed);
|
|
24
|
-
if (!nodes)
|
|
25
|
-
return fixed;
|
|
26
|
-
// Collect all orphan node IDs to remove
|
|
27
|
-
const nodesToRemove = new Set();
|
|
28
|
-
for (const issue of issues) {
|
|
29
|
-
if (issue.type === "orphan" && issue.auto_fixable && issue.node) {
|
|
30
|
-
nodesToRemove.add(issue.node);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
if (nodesToRemove.size === 0)
|
|
34
|
-
return fixed;
|
|
35
|
-
// Step 1: Remove orphan nodes
|
|
36
|
-
const filteredNodes = nodes.filter(n => {
|
|
37
|
-
const nodeId = n.id;
|
|
38
|
-
return nodeId && !nodesToRemove.has(nodeId);
|
|
39
|
-
});
|
|
40
|
-
// Step 2: Clean up dangling references in remaining nodes
|
|
41
|
-
for (const node of filteredNodes) {
|
|
42
|
-
const incomingEdges = node.incoming_edges;
|
|
43
|
-
if (incomingEdges && Array.isArray(incomingEdges)) {
|
|
44
|
-
// Filter out edges that reference removed nodes
|
|
45
|
-
node.incoming_edges = incomingEdges.filter(edge => {
|
|
46
|
-
const sourceNodeId = edge.source_node_id;
|
|
47
|
-
return sourceNodeId && !nodesToRemove.has(sourceNodeId);
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
// Also clean up inputBindings if they exist (different workflow format)
|
|
51
|
-
const inputBindings = node.inputBindings;
|
|
52
|
-
if (inputBindings && Array.isArray(inputBindings)) {
|
|
53
|
-
node.inputBindings = inputBindings.filter(binding => {
|
|
54
|
-
const actionOutput = binding.actionOutput;
|
|
55
|
-
const actionName = actionOutput?.actionName;
|
|
56
|
-
return !actionName || !nodesToRemove.has(actionName);
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
// Update nodes in the workflow
|
|
61
|
-
if (Array.isArray(fixed.nodes)) {
|
|
62
|
-
fixed.nodes = filteredNodes;
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
const wd = fixed.workflow_def;
|
|
66
|
-
if (wd) {
|
|
67
|
-
wd.nodes = filteredNodes;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return fixed;
|
|
71
|
-
}
|
|
72
13
|
/**
|
|
73
14
|
* Handle workflow optimize mode
|
|
15
|
+
*
|
|
16
|
+
* NOW DEPRECATED - Returns guidance for LLM to do the analysis.
|
|
74
17
|
*/
|
|
75
18
|
export async function handleWorkflowOptimize(args, client) {
|
|
76
19
|
const personaId = args.persona_id;
|
|
77
|
-
const preview = args.preview !== false;
|
|
78
20
|
if (!personaId) {
|
|
79
21
|
return { error: "persona_id required for optimize mode" };
|
|
80
22
|
}
|
|
@@ -89,48 +31,20 @@ export async function handleWorkflowOptimize(args, client) {
|
|
|
89
31
|
hint: "Use mode='generate' to create a workflow first",
|
|
90
32
|
};
|
|
91
33
|
}
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
const fixes = suggestWorkflowFixes(issues);
|
|
95
|
-
if (issues.length === 0) {
|
|
96
|
-
return {
|
|
97
|
-
mode: "optimize",
|
|
98
|
-
status: "✅ No issues found",
|
|
99
|
-
persona_id: personaId,
|
|
100
|
-
persona_name: persona.name,
|
|
101
|
-
workflow_healthy: true,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
// Apply fixes
|
|
105
|
-
const fixedWorkflow = applySimpleFixes(existingWorkflow, issues);
|
|
106
|
-
const result = {
|
|
34
|
+
// Return the workflow and guidance for LLM to do the analysis
|
|
35
|
+
return {
|
|
107
36
|
mode: "optimize",
|
|
108
|
-
status:
|
|
37
|
+
status: "DEPRECATED - Use LLM analysis instead",
|
|
109
38
|
persona_id: personaId,
|
|
110
39
|
persona_name: persona.name,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
40
|
+
// Return the workflow for LLM to analyze
|
|
41
|
+
workflow_def: existingWorkflow,
|
|
42
|
+
_next_steps: [
|
|
43
|
+
"1. Use workflow(mode='get') to get workflow data",
|
|
44
|
+
"2. Fetch ema://rules/anti-patterns",
|
|
45
|
+
"3. Apply rules to find issues (LLM does this, not MCP)",
|
|
46
|
+
"4. Modify workflow based on analysis",
|
|
47
|
+
"5. Deploy via workflow(mode='deploy', workflow_def={...})",
|
|
48
|
+
],
|
|
119
49
|
};
|
|
120
|
-
// If preview=false, deploy the fixed workflow
|
|
121
|
-
if (!preview) {
|
|
122
|
-
await client.updateAiEmployee({
|
|
123
|
-
persona_id: personaId,
|
|
124
|
-
workflow: fixedWorkflow,
|
|
125
|
-
proto_config: args.proto_config || persona.proto_config,
|
|
126
|
-
});
|
|
127
|
-
result.deployed = true;
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
result.next_steps = [
|
|
131
|
-
"Review the suggested fixes and fixed_workflow",
|
|
132
|
-
`Deploy with: workflow(mode="optimize", persona_id="${personaId}", preview=false)`,
|
|
133
|
-
];
|
|
134
|
-
}
|
|
135
|
-
return result;
|
|
136
50
|
}
|
package/dist/mcp/prompts.js
CHANGED
|
@@ -28,12 +28,12 @@ const PROMPTS = {
|
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
name: "context",
|
|
31
|
-
description: "Context type: 'greenfield' (new workflow), 'brownfield' (extend existing)
|
|
31
|
+
description: "Context type: 'greenfield' (new workflow), 'brownfield' (extend existing)",
|
|
32
32
|
required: false,
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
name: "persona_id",
|
|
36
|
-
description: "Existing persona ID if brownfield
|
|
36
|
+
description: "Existing persona ID if brownfield context",
|
|
37
37
|
required: false,
|
|
38
38
|
},
|
|
39
39
|
],
|
|
@@ -53,10 +53,12 @@ ${args.persona_id ? `**Existing Persona**: ${args.persona_id}` : ""}
|
|
|
53
53
|
|
|
54
54
|
### Step 1: Parse the Request
|
|
55
55
|
Identify what the user is asking for:
|
|
56
|
-
- **Action Type**: Create new / Extend existing
|
|
56
|
+
- **Action Type**: Create new / Extend existing
|
|
57
57
|
- **Scope**: Single intent / Multi-intent / Full workflow
|
|
58
58
|
- **Specificity**: Vague / Partially specified / Detailed
|
|
59
59
|
|
|
60
|
+
Note: For analysis/optimization, use \`workflow(mode="get")\` then analyze with \`ema://rules/*\` resources.
|
|
61
|
+
|
|
60
62
|
### Step 2: Check for Required Information
|
|
61
63
|
|
|
62
64
|
#### For ALL requests, verify:
|
|
@@ -92,13 +94,6 @@ For external side-effect actions (send_email, create_ticket, update_record):
|
|
|
92
94
|
| **Data Dependencies** | ✓/✗ | What existing data to use? |
|
|
93
95
|
| **Impact Assessment** | ✓/✗ | What might break? |
|
|
94
96
|
|
|
95
|
-
#### For OPTIMIZE:
|
|
96
|
-
| Dimension | Status | Missing Info |
|
|
97
|
-
|-----------|--------|--------------|
|
|
98
|
-
| **Problem Areas** | ✓/✗ | What's not working? |
|
|
99
|
-
| **Performance Goals** | ✓/✗ | Speed, accuracy, cost? |
|
|
100
|
-
| **Constraints** | ✓/✗ | What can't change? |
|
|
101
|
-
|
|
102
97
|
### Step 3: Identify Ambiguities
|
|
103
98
|
|
|
104
99
|
Look for:
|
|
@@ -159,7 +154,7 @@ Based on analysis, output ONE of:
|
|
|
159
154
|
"confidence": "high|medium",
|
|
160
155
|
"summary": "Brief summary of understood requirements",
|
|
161
156
|
"inferred_defaults": ["List any assumptions made"],
|
|
162
|
-
"next_action": "workflow(mode='
|
|
157
|
+
"next_action": "workflow(mode='get'|'deploy', ...)"
|
|
163
158
|
}
|
|
164
159
|
\`\`\`
|
|
165
160
|
|
|
@@ -1253,7 +1248,8 @@ ${args.type === "voice" ? `
|
|
|
1253
1248
|
The generated proto_config includes voice settings:
|
|
1254
1249
|
- welcomeMessage: Generated greeting
|
|
1255
1250
|
- identityAndPurpose: Generated from description
|
|
1256
|
-
- takeActionInstructions
|
|
1251
|
+
- takeActionInstructions: Auto-generated from workflow tools (if tools specified)
|
|
1252
|
+
- hangupInstructions: Sensible defaults
|
|
1257
1253
|
|
|
1258
1254
|
These are set automatically but can be customized via proto_config override.
|
|
1259
1255
|
` : ""}`,
|
|
@@ -1660,11 +1656,12 @@ Verify:
|
|
|
1660
1656
|
- [ ] All new nodes properly linked
|
|
1661
1657
|
- [ ] Existing functionality preserved
|
|
1662
1658
|
|
|
1663
|
-
### Step 4.2:
|
|
1664
|
-
\`
|
|
1659
|
+
### Step 4.2: Validate with Rules
|
|
1660
|
+
Fetch \`ema://rules/anti-patterns\` and verify no issues.
|
|
1665
1661
|
|
|
1666
1662
|
### Step 4.3: Deploy
|
|
1667
|
-
\`workflow(persona_id="${args.persona_id}", mode="deploy", workflow_def=<extended_workflow
|
|
1663
|
+
\`workflow(persona_id="${args.persona_id}", mode="deploy", workflow_def=<extended_workflow>, preview=true)\`
|
|
1664
|
+
Then without preview to commit.
|
|
1668
1665
|
|
|
1669
1666
|
## Extension Patterns
|
|
1670
1667
|
|
package/dist/mcp/resources.js
CHANGED
|
@@ -32,6 +32,7 @@ const __dirname = dirname(__filename);
|
|
|
32
32
|
// Import knowledge catalogs for dynamic resources
|
|
33
33
|
import { AGENT_CATALOG, WORKFLOW_PATTERNS, WIDGET_CATALOG, ALL_DEPRECATED_ACTIONS, DEPRECATED_ACTIONS_WITH_REPLACEMENT, DEPRECATED_ACTIONS_NO_REPLACEMENT, WORKFLOW_ENABLING_CONSTRAINTS, MINIMUM_VIABLE_WORKFLOWS, } from "../sdk/knowledge.js";
|
|
34
34
|
import { INPUT_SOURCE_RULES, ANTI_PATTERNS, OPTIMIZATION_RULES } from "../sdk/validation-rules.js";
|
|
35
|
+
import { STRUCTURAL_INVARIANTS } from "../sdk/structural-rules.js";
|
|
35
36
|
import { EmaClient } from "../sdk/client.js";
|
|
36
37
|
import { APISchemaRegistry } from "../sdk/workflow-validator.js";
|
|
37
38
|
import { loadConfigFromJsonEnv, loadConfigOptional, resolveBearerToken, getEnvByName, getMasterEnv, } from "../sdk/config.js";
|
|
@@ -208,6 +209,13 @@ const DYNAMIC_RESOURCES = [
|
|
|
208
209
|
mimeType: "application/json",
|
|
209
210
|
generate: async () => JSON.stringify(OPTIMIZATION_RULES, null, 2),
|
|
210
211
|
},
|
|
212
|
+
{
|
|
213
|
+
uri: "ema://rules/structural-invariants",
|
|
214
|
+
name: "rules/structural-invariants",
|
|
215
|
+
description: "Structural invariants: hard constraints that workflows must satisfy (no cycles, reachability, etc.)",
|
|
216
|
+
mimeType: "application/json",
|
|
217
|
+
generate: async () => JSON.stringify(STRUCTURAL_INVARIANTS, null, 2),
|
|
218
|
+
},
|
|
211
219
|
// Deprecated Actions - API-first with fallback
|
|
212
220
|
{
|
|
213
221
|
uri: "ema://rules/deprecated-actions",
|