@ema.co/mcp-toolkit 2026.1.24 → 2026.1.26-3

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.

Files changed (81) hide show
  1. package/README.md +10 -2
  2. package/dist/mcp/handlers/action/index.js +3 -18
  3. package/dist/mcp/handlers/data/index.js +385 -41
  4. package/dist/mcp/handlers/data/templates.js +107 -0
  5. package/dist/mcp/handlers/deprecation.js +50 -0
  6. package/dist/mcp/handlers/env/index.js +8 -4
  7. package/dist/mcp/handlers/knowledge/index.js +44 -237
  8. package/dist/mcp/handlers/persona/create.js +5 -11
  9. package/dist/mcp/handlers/persona/index.js +5 -1
  10. package/dist/mcp/handlers/persona/version.js +234 -0
  11. package/dist/mcp/handlers/reference/index.js +101 -1
  12. package/dist/mcp/handlers/sync/index.js +3 -18
  13. package/dist/mcp/handlers/template/index.js +75 -10
  14. package/dist/mcp/handlers/workflow/analyze.js +171 -0
  15. package/dist/mcp/handlers/workflow/compare.js +70 -0
  16. package/dist/mcp/handlers/workflow/compile.js +39 -0
  17. package/dist/mcp/handlers/workflow/deploy.js +73 -0
  18. package/dist/mcp/handlers/workflow/generate.js +350 -0
  19. package/dist/mcp/handlers/workflow/index.js +136 -0
  20. package/dist/mcp/handlers/workflow/modify.js +456 -0
  21. package/dist/mcp/handlers/workflow/optimize.js +136 -0
  22. package/dist/mcp/handlers/workflow/types.js +4 -0
  23. package/dist/mcp/handlers/workflow/utils.js +132 -0
  24. package/dist/mcp/handlers-consolidated.js +62 -2691
  25. package/dist/mcp/prompts.js +13 -14
  26. package/dist/mcp/resources.js +55 -54
  27. package/dist/mcp/server.js +93 -124
  28. package/dist/mcp/{tools-v2.js → tools.js} +1 -1
  29. package/dist/mcp/workflow-operations.js +2 -2
  30. package/dist/sdk/client-adapter.js +267 -32
  31. package/dist/sdk/client.js +31 -15
  32. package/dist/sdk/ema-client.js +183 -0
  33. package/dist/sdk/generated/template-fallbacks.js +123 -0
  34. package/dist/sdk/guidance.js +65 -11
  35. package/dist/sdk/index.js +3 -1
  36. package/dist/sdk/knowledge.js +16 -86
  37. package/dist/sdk/workflow-intent.js +27 -0
  38. package/dist/sdk/workflow-transformer.js +0 -342
  39. package/docs/DEBUG-ANALYSIS-unused-category-type-mismatch.md +481 -0
  40. package/docs/TODO-fix-analyzer-and-modify.md +182 -0
  41. package/package.json +9 -4
  42. package/dist/mcp/tools-consolidated.js +0 -866
  43. package/dist/mcp/tools-legacy.js +0 -736
  44. package/docs/CODEBASE-ANALYSIS-2026-01-23.md +0 -936
  45. package/docs/CODEBASE-ANALYSIS-PRIORITIZED.md +0 -774
  46. package/docs/api-contracts.md +0 -216
  47. package/docs/auto-builder-analysis.md +0 -271
  48. package/docs/blog/mcp-tool-design-lessons.md +0 -309
  49. package/docs/data-architecture.md +0 -166
  50. package/docs/ema-auto-builder-guide.html +0 -394
  51. package/docs/lessons-learned.md +0 -209
  52. package/docs/llm-native-workflow-design.md +0 -252
  53. package/docs/local-generation.md +0 -508
  54. package/docs/mcp-flow-diagram.md +0 -135
  55. package/docs/migration/action-composition-migration.md +0 -270
  56. package/docs/naming-conventions.md +0 -278
  57. package/docs/proposals/HANDOFF-tool-restructure.md +0 -526
  58. package/docs/proposals/action-composition.md +0 -490
  59. package/docs/proposals/explicit-method-restructure.md +0 -328
  60. package/docs/proposals/mcp-tool-restructure-2026-01.md +0 -366
  61. package/docs/proposals/self-contained-guidance.md +0 -427
  62. package/docs/proto-sdk-generation.md +0 -242
  63. package/docs/release-impact.md +0 -102
  64. package/docs/release-process.md +0 -157
  65. package/docs/staging.RULE.md +0 -142
  66. package/docs/test-persona-creation.md +0 -196
  67. package/docs/tool-consolidation-v2.md +0 -225
  68. package/docs/tool-response-standards.md +0 -256
  69. package/resources/docs/getting-started.md +0 -97
  70. package/resources/templates/auto-builder-rules.md +0 -224
  71. package/resources/templates/chat-ai/README.md +0 -119
  72. package/resources/templates/chat-ai/persona-config.json +0 -111
  73. package/resources/templates/dashboard-ai/README.md +0 -156
  74. package/resources/templates/dashboard-ai/persona-config.json +0 -180
  75. package/resources/templates/demo-scenarios/README.md +0 -63
  76. package/resources/templates/demo-scenarios/test-published-package.md +0 -116
  77. package/resources/templates/document-gen-ai/README.md +0 -132
  78. package/resources/templates/document-gen-ai/persona-config.json +0 -316
  79. package/resources/templates/voice-ai/README.md +0 -123
  80. package/resources/templates/voice-ai/persona-config.json +0 -74
  81. package/resources/templates/voice-ai/workflow-prompt.md +0 -121
@@ -0,0 +1,481 @@
1
+ # MCP Toolkit Debug Analysis: Unused Category & Type Mismatch Issues
2
+
3
+ **Date:** 2026-01-25
4
+ **Context:** Debugging session for persona "EX - SKYE Self-Service AI Assistant"
5
+ **Persona ID:** `8ed79612-a8be-4c13-8736-daf5e4b0db96` (original), `2c5b7b2c-fee9-409f-8c87-d264fd4f27cd` (clone)
6
+
7
+ ---
8
+
9
+ ## Executive Summary
10
+
11
+ During workflow analysis and modification of a two-stage categorizer workflow, we discovered:
12
+
13
+ 1. **False Positive Bug**: The `unused_category` detection incorrectly attributes categories from one categorizer to another
14
+ 2. **NLU Misinterpretation**: The `modify` handler interprets "rewire input X" as "remove nodes referencing X"
15
+ 3. **Type Mismatch Guidance Gap**: No clear fix path for `TEXT_WITH_SOURCES` → `DOCUMENT` type mismatches
16
+
17
+ ---
18
+
19
+ ## Issue 1: Unused Category False Positive
20
+
21
+ ### Symptom
22
+
23
+ The analyzer reports:
24
+ ```
25
+ Category "Employment Verification" is defined in User Verification but has NO HANDLER
26
+ Category "Business Visa" is defined in User Verification but has NO HANDLER
27
+ ```
28
+
29
+ But in reality:
30
+ - **User Verification** (`chat_categorizermys4rq`) only has: `Fallback`, `ID Verified`
31
+ - **Categorize Conversations** (`chat_categorizeraoh3e8`) has: `Fallback`, `Employment Verification`, `Business Visa`
32
+
33
+ ### Root Cause
34
+
35
+ **File:** `src/sdk/knowledge.ts`, lines 2968-2976
36
+
37
+ ```typescript
38
+ // Find the enumType for this categorizer
39
+ const enumTypeName = categorizer.id + "_enumType";
40
+ const enumType = enumTypes.find(e => {
41
+ const name = e.name as Record<string, unknown> | undefined;
42
+ const innerName = name?.name as Record<string, unknown> | undefined;
43
+ const nameStr = String(innerName?.name ?? name?.name ?? "");
44
+ return nameStr.includes(categorizer.id) || nameStr.includes("enumType"); // BUG HERE
45
+ });
46
+ ```
47
+
48
+ **The Bug:**
49
+ - The condition `nameStr.includes("enumType")` is a fallback that matches ANY enumType
50
+ - When processing categorizer A, if the first condition fails, it grabs the FIRST enumType in the array (which might belong to categorizer B)
51
+ - This causes categories from enumType B to be attributed to categorizer A
52
+
53
+ **Evidence from workflow:**
54
+ ```
55
+ enumTypes[0] = chat_categorizeraoh3e8_enumType_8vok (Employment Verification, Business Visa, Fallback)
56
+ enumTypes[1] = chat_categorizermys4rq_enumType_f4k7 (Fallback, ID Verified)
57
+ ```
58
+
59
+ When processing `chat_categorizermys4rq`, if the ID matching fails, the fallback `includes("enumType")` matches `enumTypes[0]` instead of `enumTypes[1]`.
60
+
61
+ ### Fix Required
62
+
63
+ ```typescript
64
+ // BEFORE (buggy)
65
+ const enumType = enumTypes.find(e => {
66
+ const nameStr = extractEnumTypeName(e);
67
+ return nameStr.includes(categorizer.id) || nameStr.includes("enumType"); // Too loose!
68
+ });
69
+
70
+ // AFTER (correct)
71
+ const enumType = enumTypes.find(e => {
72
+ const nameStr = extractEnumTypeName(e);
73
+ // Only match if the enumType name contains this specific categorizer's ID
74
+ return nameStr.includes(categorizer.id);
75
+ });
76
+
77
+ // BETTER (robust) - Use the typeArguments reference
78
+ const categorizerAction = actions.find(a => a.name === categorizer.id);
79
+ const typeArgs = categorizerAction?.typeArguments as Record<string, unknown> | undefined;
80
+ const categoriesTypeArg = typeArgs?.categories as Record<string, unknown> | undefined;
81
+ const enumTypeRef = categoriesTypeArg?.enumType as Record<string, unknown> | undefined;
82
+ const enumTypeName = extractEnumTypeName({ name: enumTypeRef?.name });
83
+
84
+ const enumType = enumTypes.find(e => {
85
+ const name = extractEnumTypeName(e);
86
+ return name === enumTypeName;
87
+ });
88
+ ```
89
+
90
+ ### Test Case to Add
91
+
92
+ ```typescript
93
+ // test/knowledge.test.ts
94
+ describe("detectUnusedCategories", () => {
95
+ it("should correctly associate enumTypes with their categorizers in multi-categorizer workflows", () => {
96
+ const workflow = {
97
+ actions: [
98
+ { name: "chat_categorizermys4rq", /* User Verification */ },
99
+ { name: "chat_categorizeraoh3e8", /* Document Router */ },
100
+ ],
101
+ enumTypes: [
102
+ {
103
+ name: { name: { name: "chat_categorizeraoh3e8_enumType_8vok" } },
104
+ options: [
105
+ { name: "Fallback" },
106
+ { name: "Employment Verification" }, // Has handler
107
+ { name: "Business Visa" }, // Has handler
108
+ ]
109
+ },
110
+ {
111
+ name: { name: { name: "chat_categorizermys4rq_enumType_f4k7" } },
112
+ options: [
113
+ { name: "Fallback" }, // Has handler
114
+ { name: "ID Verified" }, // Has handler (triggers second categorizer)
115
+ ]
116
+ }
117
+ ]
118
+ };
119
+
120
+ const issues = detectWorkflowIssues(workflow);
121
+
122
+ // Should NOT report unused_category for Employment Verification or Business Visa
123
+ // on chat_categorizermys4rq (User Verification)
124
+ const falsePositives = issues.filter(i =>
125
+ i.type === "unused_category" &&
126
+ i.node === "chat_categorizermys4rq" &&
127
+ (i.category === "Employment Verification" || i.category === "Business Visa")
128
+ );
129
+
130
+ expect(falsePositives).toHaveLength(0);
131
+ });
132
+ });
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Issue 2: NLU Misinterpretation in Modify Handler
138
+
139
+ ### Symptom
140
+
141
+ Request: "rewire document_instructions input on EX - Hitachi Business Visa Letter Generation to use json_mapper5d37cx.output_json"
142
+
143
+ Result: Removes the entire `EX - Hitachi Business Visa Letter Generation` node instead of rewiring its input.
144
+
145
+ ### Root Cause
146
+
147
+ **File:** `src/mcp/handlers/workflow/modify.ts`, lines 545-607
148
+
149
+ The `analyzeModificationRequest` function has aggressive node removal logic:
150
+
151
+ ```typescript
152
+ // REMOVAL
153
+ if (lowerInput.includes("remove") || lowerInput.includes("delete") ||
154
+ lowerInput.includes("clean up") || lowerInput.includes("orphan")) {
155
+ intent = "remove nodes";
156
+
157
+ // ... but then it also matches node names mentioned in ANY context:
158
+ if (operations.length === 0) {
159
+ const nodeNames = actions.map(a => String(a.name || "")).filter(n => n.length > 0);
160
+ const foundNodes = nodeNames.filter(nodeName =>
161
+ input.toLowerCase().includes(nodeName.toLowerCase()) // BUG: Too aggressive
162
+ );
163
+ if (foundNodes.length > 0) {
164
+ operations.push({
165
+ type: "remove",
166
+ description: `Remove node(s): ${foundNodes.join(", ")}`,
167
+ target_nodes: foundNodes,
168
+ });
169
+ }
170
+ }
171
+ }
172
+ ```
173
+
174
+ The problem: When the input mentions node names in a "rewire" context, the removal detection still picks them up if no other operation is detected first.
175
+
176
+ ### Fix Required
177
+
178
+ 1. **Add explicit rewire detection before removal**:
179
+
180
+ ```typescript
181
+ // REWIRING (check BEFORE removal)
182
+ if (lowerInput.includes("rewire") ||
183
+ (lowerInput.includes("input") && lowerInput.includes("use")) ||
184
+ lowerInput.includes("change") && lowerInput.includes("to use")) {
185
+ intent = "rewire connections";
186
+
187
+ // Parse: "rewire X.input to use Y.output"
188
+ const rewireMatch = input.match(
189
+ /rewire\s+(?:the\s+)?(\w+)[\s.]+(\w+)\s+(?:input\s+)?(?:to\s+)?use\s+(\w+)\.(\w+)/i
190
+ );
191
+
192
+ if (rewireMatch) {
193
+ operations.push({
194
+ type: "rewire",
195
+ description: `Rewire ${rewireMatch[1]}.${rewireMatch[2]} to use ${rewireMatch[3]}.${rewireMatch[4]}`,
196
+ target: rewireMatch[1],
197
+ input_name: rewireMatch[2],
198
+ source: rewireMatch[3],
199
+ output_name: rewireMatch[4],
200
+ });
201
+ }
202
+ }
203
+ ```
204
+
205
+ 2. **Guard removal detection against rewire context**:
206
+
207
+ ```typescript
208
+ // Only do name-based removal if NOT in a rewire context
209
+ const isRewireContext = lowerInput.includes("rewire") ||
210
+ lowerInput.includes("to use") ||
211
+ lowerInput.includes("instead of");
212
+
213
+ if (!isRewireContext && operations.length === 0) {
214
+ // ... existing name-based removal logic
215
+ }
216
+ ```
217
+
218
+ 3. **Implement actual input rewiring in applyWorkflowModifications**:
219
+
220
+ ```typescript
221
+ case "rewire": {
222
+ const targetNodeName = op.target;
223
+ const inputName = op.input_name;
224
+ const sourceName = op.source;
225
+ const outputName = op.output_name;
226
+
227
+ const targetNode = actions.find(a =>
228
+ String(a.name || "").toLowerCase().includes(targetNodeName.toLowerCase())
229
+ );
230
+
231
+ if (targetNode && targetNode.inputs) {
232
+ const inputs = targetNode.inputs as Record<string, unknown>;
233
+
234
+ // Find the input to rewire
235
+ const inputKey = Object.keys(inputs).find(k =>
236
+ k.toLowerCase().includes(inputName.toLowerCase())
237
+ );
238
+
239
+ if (inputKey) {
240
+ // Rewire the input
241
+ inputs[inputKey] = {
242
+ actionOutput: {
243
+ actionName: sourceName,
244
+ output: outputName,
245
+ },
246
+ autoDetectedBinding: false,
247
+ };
248
+
249
+ nodesModified.push(String(targetNode.name));
250
+ connectionsChanged.push(`${targetNode.name}.${inputKey} ← ${sourceName}.${outputName}`);
251
+ changesApplied.push(`Rewired ${targetNode.name}.${inputKey} to use ${sourceName}.${outputName}`);
252
+ } else {
253
+ changesApplied.push(`Input "${inputName}" not found on "${targetNode.name}" - available: ${Object.keys(inputs).join(", ")}`);
254
+ }
255
+ }
256
+ break;
257
+ }
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Issue 3: Type Mismatch Guidance Gap
263
+
264
+ ### Symptom
265
+
266
+ The analyzer correctly detects:
267
+ ```
268
+ Type mismatch: WELL_KNOWN_TYPE_TEXT_WITH_SOURCES → WELL_KNOWN_TYPE_DOCUMENT
269
+ ```
270
+
271
+ But provides no actionable fix:
272
+ ```
273
+ # Either:
274
+ # 1. Use an intermediate conversion node
275
+ # 2. Connect to a named_inputs_* (accepts ANY type)
276
+ # 3. Use a different source output that matches the expected type
277
+ ```
278
+
279
+ ### Root Cause
280
+
281
+ **File:** `src/sdk/validation-rules.ts` - No rule for `TEXT_WITH_SOURCES` → `DOCUMENT` conversion
282
+
283
+ **File:** `src/sdk/knowledge.ts` - `suggestWorkflowFixes` returns generic guidance for type mismatches
284
+
285
+ ### Fix Required
286
+
287
+ 1. **Add type coercion guidance to validation-rules.ts**:
288
+
289
+ ```typescript
290
+ export interface TypeCoercionRule {
291
+ sourceType: string;
292
+ targetType: string;
293
+ severity: "critical" | "warning" | "info";
294
+ workarounds: Array<{
295
+ method: string;
296
+ description: string;
297
+ example?: string;
298
+ }>;
299
+ }
300
+
301
+ export const TYPE_COERCION_RULES: TypeCoercionRule[] = [
302
+ {
303
+ sourceType: "WELL_KNOWN_TYPE_TEXT_WITH_SOURCES",
304
+ targetType: "WELL_KNOWN_TYPE_DOCUMENT",
305
+ severity: "warning", // Not critical - often works via runtime coercion
306
+ workarounds: [
307
+ {
308
+ method: "named_inputs",
309
+ description: "Pass via named_inputs which accepts any type",
310
+ example: `inputs: {
311
+ named_inputs: {
312
+ namedInputs: [{
313
+ name: "document_content",
314
+ binding: { actionOutput: { actionName: "source", output: "text_with_sources" } }
315
+ }]
316
+ }
317
+ }`
318
+ },
319
+ {
320
+ method: "call_llm_intermediary",
321
+ description: "Use call_llm to format content, then pass to document generator",
322
+ example: "call_llm (formats content) → generate_document"
323
+ },
324
+ {
325
+ method: "accept_runtime_coercion",
326
+ description: "Ema's runtime often handles type coercion automatically. Test before fixing.",
327
+ }
328
+ ]
329
+ },
330
+ // ... other type coercion rules
331
+ ];
332
+ ```
333
+
334
+ 2. **Improve suggestWorkflowFixes for type_mismatch**:
335
+
336
+ ```typescript
337
+ case "type_mismatch": {
338
+ const coercionRule = TYPE_COERCION_RULES.find(r =>
339
+ issue.got?.includes(r.sourceType) && issue.expected?.includes(r.targetType)
340
+ );
341
+
342
+ if (coercionRule) {
343
+ fix = {
344
+ issue_type: issue.type,
345
+ description: `Fix type mismatch: ${issue.source} → ${issue.target}`,
346
+ severity: coercionRule.severity,
347
+ workarounds: coercionRule.workarounds,
348
+ note: coercionRule.severity === "warning"
349
+ ? "This may work at runtime due to automatic type coercion. Test before refactoring."
350
+ : undefined,
351
+ };
352
+ } else {
353
+ fix = { /* existing generic fix */ };
354
+ }
355
+ break;
356
+ }
357
+ ```
358
+
359
+ ---
360
+
361
+ ## Issue 4: Incomplete Input Source Rules for Persona Actions
362
+
363
+ ### Symptom
364
+
365
+ When calling external AI Employees (persona actions) like `44ac6ec7-f46f-4f63-9f0f-b16a5ca349c3`, the analyzer doesn't understand their input requirements.
366
+
367
+ ### Root Cause
368
+
369
+ **File:** `src/sdk/validation-rules.ts` - `INPUT_SOURCE_RULES` only covers internal action types, not persona actions.
370
+
371
+ ### Fix Required
372
+
373
+ 1. **Add dynamic input rule fetching for persona actions**:
374
+
375
+ ```typescript
376
+ // In knowledge.ts detectWorkflowIssues
377
+ async function getInputRulesForPersonaAction(
378
+ personaId: string,
379
+ client: EmaClient
380
+ ): Promise<InputSourceRule[]> {
381
+ // Fetch the persona's workflow to understand its inputs
382
+ const persona = await client.getPersonaById(personaId);
383
+ if (!persona?.workflow_def) return [];
384
+
385
+ // Extract workflow input schema
386
+ const workflowInputs = persona.workflow_def.workflowInputs;
387
+
388
+ // Generate rules based on input types
389
+ return Object.entries(workflowInputs).map(([inputName, inputDef]) => ({
390
+ actionPattern: personaId,
391
+ input: inputName,
392
+ expectedType: inputDef.type,
393
+ // ...
394
+ }));
395
+ }
396
+ ```
397
+
398
+ 2. **Cache persona action schemas**:
399
+
400
+ ```typescript
401
+ // src/sdk/persona-action-cache.ts
402
+ export class PersonaActionCache {
403
+ private cache = new Map<string, PersonaInputSchema>();
404
+
405
+ async getInputSchema(personaId: string, client: EmaClient): Promise<PersonaInputSchema | null> {
406
+ if (this.cache.has(personaId)) {
407
+ return this.cache.get(personaId)!;
408
+ }
409
+
410
+ // Fetch and cache
411
+ const schema = await fetchPersonaInputSchema(personaId, client);
412
+ if (schema) {
413
+ this.cache.set(personaId, schema);
414
+ }
415
+ return schema;
416
+ }
417
+ }
418
+ ```
419
+
420
+ ---
421
+
422
+ ## Summary of Required Changes
423
+
424
+ ### High Priority (Bug Fixes)
425
+
426
+ | File | Issue | Fix |
427
+ |------|-------|-----|
428
+ | `src/sdk/knowledge.ts:2968-2976` | enumType matching too loose | Use exact ID matching or typeArguments reference |
429
+ | `src/mcp/handlers/workflow/modify.ts:545-607` | Remove detection too aggressive | Add rewire context guard |
430
+
431
+ ### Medium Priority (Feature Gaps)
432
+
433
+ | File | Issue | Fix |
434
+ |------|-------|-----|
435
+ | `src/sdk/validation-rules.ts` | No type coercion rules | Add `TYPE_COERCION_RULES` |
436
+ | `src/sdk/knowledge.ts` | Generic type mismatch fixes | Specific workarounds per type pair |
437
+ | `src/mcp/handlers/workflow/modify.ts` | No input rewiring implementation | Add `rewire` case to `applyWorkflowModifications` |
438
+
439
+ ### Low Priority (Documentation/Knowledge)
440
+
441
+ | Area | Issue | Fix |
442
+ |------|-------|-----|
443
+ | `ema://rules/input-sources` | Missing persona action guidance | Document `named_inputs` pattern for persona actions |
444
+ | `ema://rules/anti-patterns` | No type mismatch pattern | Add `type-coercion-workarounds` pattern |
445
+ | Ema MCP Expert skill | Doesn't mention type coercion | Update skill with type mismatch guidance |
446
+
447
+ ---
448
+
449
+ ## Test Scenarios to Add
450
+
451
+ 1. **Multi-categorizer enumType association** - Verify correct enumType ↔ categorizer pairing
452
+ 2. **Rewire intent parsing** - Verify "rewire X to use Y" is not interpreted as "remove X and Y"
453
+ 3. **Type mismatch severity** - Verify TEXT_WITH_SOURCES → DOCUMENT is warning, not critical
454
+ 4. **Persona action input validation** - Verify external AIE inputs are validated correctly
455
+
456
+ ---
457
+
458
+ ## Reproduction Steps
459
+
460
+ To reproduce these issues:
461
+
462
+ ```bash
463
+ # 1. Analyze the original persona
464
+ persona(method="analyze", id="8ed79612-a8be-4c13-8736-daf5e4b0db96", env="demo")
465
+
466
+ # 2. Observe false positive unused_category issues
467
+
468
+ # 3. Try to fix with modify
469
+ persona(method="update", id="...", input="rewire document_instructions to use json_mapper.output_json", preview=true)
470
+
471
+ # 4. Observe nodes being removed instead of rewired
472
+ ```
473
+
474
+ ---
475
+
476
+ ## References
477
+
478
+ - Original persona: `8ed79612-a8be-4c13-8736-daf5e4b0db96`
479
+ - Cloned persona: `2c5b7b2c-fee9-409f-8c87-d264fd4f27cd`
480
+ - Environment: demo
481
+ - MCP Toolkit version: 0.0.0-dev
@@ -0,0 +1,182 @@
1
+ # TODO: Fix Analyzer and Modify Handler Issues
2
+
3
+ **Created:** 2026-01-25
4
+ **Status:** MOSTLY RESOLVED (2026-01-25)
5
+ **Context:** Debug session for SKYE Self-Service AI Assistant
6
+
7
+ > **UPDATE 2026-01-25**: The modify handler was completely refactored to remove
8
+ > ALL hardcoded NL parsing. See `.ctx/plans/2026-01-25-remove-hardcoded-logic.md`.
9
+ > Items 1-4 were addressed as part of the refactor (NL parsing removed entirely).
10
+
11
+ ---
12
+
13
+ ## Critical Bugs (Fix Immediately)
14
+
15
+ ### 1. [x] Fix enumType ↔ Categorizer Association Bug ✅ FIXED
16
+
17
+ **File:** `src/sdk/knowledge.ts`
18
+ **Line:** ~2968-2976
19
+ **Function:** `detectUnusedCategories`
20
+
21
+ **Problem:** The `enumTypes.find()` fallback `|| nameStr.includes("enumType")` matches ANY enumType, causing categories from categorizer B to be attributed to categorizer A.
22
+
23
+ **Fix:**
24
+ ```typescript
25
+ // Replace this:
26
+ return nameStr.includes(categorizer.id) || nameStr.includes("enumType");
27
+
28
+ // With this:
29
+ return nameStr.includes(categorizer.id);
30
+
31
+ // Or better - use typeArguments reference:
32
+ const categorizerAction = actions.find(a => a.name === categorizer.id);
33
+ const enumTypeRef = categorizerAction?.typeArguments?.categories?.enumType?.name?.name?.name;
34
+ const enumType = enumTypes.find(e => extractEnumTypeName(e) === enumTypeRef);
35
+ ```
36
+
37
+ **Test:** Add test for multi-categorizer workflows with distinct enumTypes.
38
+
39
+ ---
40
+
41
+ ### 2. [x] Fix Modify Handler Rewire vs Remove Confusion ✅ RESOLVED
42
+
43
+ **Status:** RESOLVED via architecture change.
44
+
45
+ **Resolution:** The entire NL-parsing approach was removed. The modify handler
46
+ now accepts STRUCTURED OPERATIONS from the Agent, not natural language.
47
+
48
+ The Agent (LLM) builds operations like:
49
+ ```typescript
50
+ {
51
+ type: "rewire",
52
+ rewire: {
53
+ target_node: "downstream_node",
54
+ target_input: "query",
55
+ source_node: "upstream_node",
56
+ source_output: "response",
57
+ }
58
+ }
59
+ ```
60
+
61
+ No regex parsing needed - the LLM naturally understands user intent.
62
+
63
+ ---
64
+
65
+ ## Medium Priority (Feature Gaps)
66
+
67
+ ### 3. [ ] Add Type Coercion Rules and Guidance
68
+
69
+ **File:** `src/sdk/validation-rules.ts`
70
+
71
+ **Status:** Still applicable. Consider adding for validation feedback.
72
+
73
+ ---
74
+
75
+ ### 4. [x] Implement Rewire Operation in applyWorkflowModifications ✅ DONE
76
+
77
+ **Status:** IMPLEMENTED in `src/mcp/handlers/workflow/modify.ts`
78
+
79
+ The `executeRewire()` function handles structured rewire operations:
80
+ ```typescript
81
+ const result = await handleWorkflowModify(args, client);
82
+ // With operations: [{ type: "rewire", rewire: { target_node, target_input, source_node, source_output } }]
83
+ ```
84
+
85
+ ---
86
+
87
+ ### 5. [ ] Improve Type Mismatch Fix Suggestions
88
+
89
+ **File:** `src/sdk/knowledge.ts`
90
+ **Function:** `suggestWorkflowFixes`
91
+
92
+ **Update type_mismatch case to use TYPE_COERCION_RULES:**
93
+ ```typescript
94
+ case "type_mismatch": {
95
+ const rule = TYPE_COERCION_RULES.find(r =>
96
+ issue.got?.includes(r.sourceType) && issue.expected?.includes(r.targetType)
97
+ );
98
+
99
+ fix = rule ? {
100
+ issue_type: issue.type,
101
+ severity: rule.severity,
102
+ workarounds: rule.workarounds,
103
+ note: rule.severity === "warning" ? "May work at runtime - test first" : undefined,
104
+ } : { /* existing generic fix */ };
105
+ break;
106
+ }
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Low Priority (Documentation)
112
+
113
+ ### 6. [ ] Update ema://rules/anti-patterns Resource
114
+
115
+ **File:** `src/mcp/resources.ts` or generated resource
116
+
117
+ **Add pattern:**
118
+ ```json
119
+ {
120
+ "id": "type-mismatch-persona-action",
121
+ "name": "Type Mismatch with Persona Actions",
122
+ "pattern": "Connecting TEXT_WITH_SOURCES output to persona action's DOCUMENT input",
123
+ "problem": "Persona actions have specific input type requirements",
124
+ "solution": "Use named_inputs which accepts any type, or test runtime coercion",
125
+ "severity": "warning"
126
+ }
127
+ ```
128
+
129
+ ---
130
+
131
+ ### 7. [ ] Update Ema MCP Expert Skill
132
+
133
+ **File:** `skills/ema-mcp-expert/SKILL.md` (generated)
134
+
135
+ **Add troubleshooting entry:**
136
+ ```markdown
137
+ | Symptom | Likely Cause | Fix |
138
+ |---------|--------------|-----|
139
+ | Type mismatch with persona action | Persona expects different type | Use named_inputs or test runtime |
140
+ | Unused category false positive | Multi-categorizer enumType bug | Verify actual enumType associations |
141
+ ```
142
+
143
+ ---
144
+
145
+ ### 8. [ ] Add Input Source Rule for Persona Actions
146
+
147
+ **File:** `src/sdk/validation-rules.ts`
148
+
149
+ **Add guidance:**
150
+ ```typescript
151
+ {
152
+ actionPattern: "persona_action", // Generic pattern for all persona actions
153
+ recommended: "named_inputs for flexible type handling",
154
+ avoid: ["direct document_instructions without type validation"],
155
+ reason: "Persona actions have specific workflow input schemas. Use named_inputs for flexibility.",
156
+ severity: "info",
157
+ fix: "Pass data via named_inputs: { namedInputs: [{ name: '...', binding: {...} }] }",
158
+ }
159
+ ```
160
+
161
+ ---
162
+
163
+ ## Testing Checklist
164
+
165
+ - [ ] Unit test: Multi-categorizer enumType association
166
+ - [ ] Unit test: Rewire intent parsing (should NOT trigger removal)
167
+ - [ ] Unit test: Type mismatch severity levels
168
+ - [ ] Integration test: Clone + modify workflow without false positives
169
+ - [ ] E2E test: SKYE workflow analysis returns 0 critical false positives
170
+
171
+ ---
172
+
173
+ ## Files Modified
174
+
175
+ | File | Changes |
176
+ |------|---------|
177
+ | `src/sdk/knowledge.ts` | Fix enumType matching, improve type mismatch fixes |
178
+ | `src/sdk/validation-rules.ts` | Add TYPE_COERCION_RULES |
179
+ | `src/mcp/handlers/workflow/modify.ts` | Add rewire detection and implementation |
180
+ | `src/mcp/resources.ts` | Update anti-patterns resource |
181
+ | `test/knowledge.test.ts` | Add multi-categorizer tests |
182
+ | `test/handlers/workflow/modify.test.ts` | Add rewire tests |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ema.co/mcp-toolkit",
3
- "version": "2026.1.24",
3
+ "version": "2026.1.26-3",
4
4
  "description": "Ema AI Employee toolkit - MCP server, CLI, and SDK for managing AI Employees across environments",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -52,18 +52,23 @@
52
52
  "lint:unused": "knip",
53
53
  "precommit": "npm run typecheck && npm run test",
54
54
  "prepare": "husky",
55
- "prepublishOnly": "npm run build && npm run test",
55
+ "check:publish": "tsx scripts/check-publish-content.ts",
56
+ "prepublishOnly": "npm run build && npm run test && npm run check:publish",
56
57
  "generate:types": "tsx scripts/generate-types.ts",
57
58
  "generate:api-client": "npx @hey-api/openapi-ts -i src/sdk/generated/openapi.json -o src/sdk/generated/api-client -c @hey-api/client-fetch",
58
59
  "generate:protos": "buf generate ../ema-repos/protos --template buf.gen.yaml",
59
60
  "generate:proto-fields": "tsx scripts/generate-proto-fields.ts",
60
61
  "generate:action-schema": "tsx scripts/generate-action-schema.ts",
61
- "generate:all": "npm run generate:protos && npm run generate:api-client",
62
+ "generate:skill": "tsx scripts/generate-skill.ts",
63
+ "generate:skill:check": "tsx scripts/generate-skill.ts --check",
64
+ "generate:templates": "tsx scripts/generate-templates.ts",
65
+ "generate:templates:check": "tsx scripts/generate-templates.ts --check",
66
+ "generate:all": "npm run generate:protos && npm run generate:api-client && npm run generate:templates && npm run generate:skill",
62
67
  "check:contracts": "tsx scripts/check-api-contracts.ts",
63
68
  "check:schema-sync": "tsx scripts/check-schema-handler-sync.ts",
64
69
  "check:prompt-refs": "tsx scripts/check-prompt-refs.ts",
65
70
  "check:action-aliases": "tsx scripts/check-action-aliases.ts",
66
- "check:all": "npm run check:contracts && npm run check:schema-sync && npm run check:prompt-refs && npm run check:action-aliases",
71
+ "check:all": "npm run check:contracts && npm run check:schema-sync && npm run check:prompt-refs && npm run check:action-aliases && npm run generate:skill:check",
67
72
  "release:check": "tsx scripts/check-release-need.ts"
68
73
  },
69
74
  "repository": {