@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.

@@ -1,80 +1,22 @@
1
1
  /**
2
2
  * Workflow Optimize Handler
3
3
  *
4
- * Detects and fixes workflow issues.
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
- // Analyze and detect issues
93
- const issues = detectWorkflowIssues(existingWorkflow);
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: preview ? "preview" : "deployed",
37
+ status: "DEPRECATED - Use LLM analysis instead",
109
38
  persona_id: personaId,
110
39
  persona_name: persona.name,
111
- issues_found: issues.length,
112
- issues: issues.map(i => ({
113
- type: i.type,
114
- severity: i.severity,
115
- reason: i.reason,
116
- })),
117
- suggested_fixes: fixes,
118
- fixed_workflow: fixedWorkflow,
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
  }
@@ -28,12 +28,12 @@ const PROMPTS = {
28
28
  },
29
29
  {
30
30
  name: "context",
31
- description: "Context type: 'greenfield' (new workflow), 'brownfield' (extend existing), 'optimize' (improve 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/optimize context",
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 / Optimize / Analyze
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='generate'|'extend'|'optimize', ...)"
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, hangupInstructions: Sensible defaults
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: Preview Changes
1664
- \`workflow(persona_id="${args.persona_id}", mode="optimize", preview=true)\`
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
 
@@ -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",