@ema.co/mcp-toolkit 2026.1.26 → 2026.1.27-1

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 (44) hide show
  1. package/dist/mcp/handlers/action/index.js +17 -20
  2. package/dist/mcp/handlers/data/index.js +72 -6
  3. package/dist/mcp/handlers/deprecation.js +50 -0
  4. package/dist/mcp/handlers/env/index.js +3 -3
  5. package/dist/mcp/handlers/knowledge/index.js +44 -237
  6. package/dist/mcp/handlers/persona/create.js +47 -18
  7. package/dist/mcp/handlers/persona/index.js +9 -10
  8. package/dist/mcp/handlers/persona/update.js +4 -2
  9. package/dist/mcp/handlers/reference/index.js +15 -2
  10. package/dist/mcp/handlers/sync/index.js +3 -18
  11. package/dist/mcp/handlers/workflow/analyze.js +53 -105
  12. package/dist/mcp/handlers/workflow/deploy.js +129 -0
  13. package/dist/mcp/handlers/workflow/generate.js +8 -28
  14. package/dist/mcp/handlers/workflow/index.js +258 -85
  15. package/dist/mcp/handlers/workflow/modify.js +9 -29
  16. package/dist/mcp/handlers/workflow/optimize.js +22 -108
  17. package/dist/mcp/handlers/workflow/utils.js +0 -102
  18. package/dist/mcp/handlers-consolidated.js +15 -38
  19. package/dist/mcp/prompts.js +82 -44
  20. package/dist/mcp/resources.js +335 -3
  21. package/dist/mcp/server.js +242 -457
  22. package/dist/mcp/tools.js +44 -61
  23. package/dist/sdk/action-schema-parser.js +11 -5
  24. package/dist/sdk/client.js +46 -17
  25. package/dist/sdk/ema-client.js +11 -0
  26. package/dist/sdk/generated/deprecated-actions.js +171 -0
  27. package/dist/sdk/guidance.js +58 -35
  28. package/dist/sdk/index.js +8 -7
  29. package/dist/sdk/knowledge.js +216 -1932
  30. package/dist/sdk/quality-gates.js +60 -336
  31. package/dist/sdk/validation-rules.js +33 -0
  32. package/dist/sdk/workflow-fixer.js +29 -360
  33. package/dist/sdk/workflow-intent.js +43 -3
  34. package/dist/sdk/workflow-transformer.js +0 -342
  35. package/docs/dashboard-operations.md +35 -0
  36. package/docs/ema-user-guide.md +66 -0
  37. package/docs/mcp-tools-guide.md +74 -45
  38. package/package.json +2 -2
  39. package/dist/mcp/handlers/persona/analyze.js +0 -275
  40. package/dist/mcp/handlers/persona/compare.js +0 -32
  41. package/dist/mcp/handlers/workflow/compile.js +0 -39
  42. package/docs/DEBUG-ANALYSIS-unused-category-type-mismatch.md +0 -481
  43. package/docs/TODO-fix-analyzer-and-modify.md +0 -182
  44. package/resources/action-schema.json +0 -5678
@@ -30,11 +30,13 @@ import { dirname } from "node:path";
30
30
  const __filename = fileURLToPath(import.meta.url);
31
31
  const __dirname = dirname(__filename);
32
32
  // Import knowledge catalogs for dynamic resources
33
- import { AGENT_CATALOG, WORKFLOW_PATTERNS, WIDGET_CATALOG } from "../sdk/knowledge.js";
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";
39
+ import { VOICE_TEMPLATE_FALLBACK, VOICE_TEMPLATE_FIELD_DOCS, } from "../sdk/generated/template-fallbacks.js";
38
40
  // ─────────────────────────────────────────────────────────────────────────────
39
41
  // Security Utilities
40
42
  // ─────────────────────────────────────────────────────────────────────────────
@@ -207,6 +209,336 @@ const DYNAMIC_RESOURCES = [
207
209
  mimeType: "application/json",
208
210
  generate: async () => JSON.stringify(OPTIMIZATION_RULES, null, 2),
209
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
+ },
219
+ // Deprecated Actions - API-first with fallback
220
+ {
221
+ uri: "ema://rules/deprecated-actions",
222
+ name: "rules/deprecated-actions",
223
+ description: "Deprecated actions list: actions to avoid in new workflows, with their replacements and migration notes",
224
+ mimeType: "application/json",
225
+ generate: async (ctx) => {
226
+ // Try API first using existing client infrastructure
227
+ let apiDeprecated = [];
228
+ let source = "fallback";
229
+ try {
230
+ const client = getClientForEnvName(ctx.env);
231
+ if (client) {
232
+ const actions = await client.listActions();
233
+ apiDeprecated = actions.filter(a => a.deprecated).map(a => a.id);
234
+ source = "api";
235
+ }
236
+ }
237
+ catch {
238
+ // API unavailable, use fallback
239
+ }
240
+ // Merge API deprecated with fallback replacement info
241
+ const result = {
242
+ _note: "Actions marked deprecated should not be used in new workflows. Use replacements where available.",
243
+ _source: source,
244
+ _api_deprecated_count: apiDeprecated.length,
245
+ // From API (current deprecated status)
246
+ api_deprecated: apiDeprecated.length > 0 ? apiDeprecated : undefined,
247
+ // From fallback (includes replacement info)
248
+ with_replacement: DEPRECATED_ACTIONS_WITH_REPLACEMENT.map(d => ({
249
+ action: d.action,
250
+ version: d.version,
251
+ replacement: d.replacement,
252
+ replacement_version: d.replacementVersion,
253
+ migration_notes: d.migrationNotes,
254
+ })),
255
+ no_replacement: DEPRECATED_ACTIONS_NO_REPLACEMENT.map(d => ({
256
+ action: d.action,
257
+ version: d.version,
258
+ environment: d.environment,
259
+ notes: d.migrationNotes,
260
+ })),
261
+ _tip: "Check workflow(mode='get') for deprecation_warnings on specific workflows",
262
+ };
263
+ return JSON.stringify(result, null, 2);
264
+ },
265
+ },
266
+ {
267
+ uri: "ema://rules/deprecated-actions-summary",
268
+ name: "rules/deprecated-actions-summary",
269
+ description: "Deprecated actions summary: quick reference table of deprecated actions and replacements",
270
+ mimeType: "text/markdown",
271
+ generate: async (ctx) => {
272
+ // Try API first using existing client infrastructure
273
+ let apiDeprecated = [];
274
+ let source = "fallback";
275
+ try {
276
+ const client = getClientForEnvName(ctx.env);
277
+ if (client) {
278
+ const actions = await client.listActions();
279
+ apiDeprecated = actions.filter(a => a.deprecated).map(a => a.id);
280
+ source = "api";
281
+ }
282
+ }
283
+ catch {
284
+ // API unavailable, use fallback
285
+ }
286
+ let md = "# Deprecated Actions\n\n";
287
+ md += "> Do NOT use these actions in new workflows. Use replacements where available.\n\n";
288
+ md += `> Source: ${source} (${source === "api" ? "live" : "synced 2026-01-26"})\n\n`;
289
+ if (apiDeprecated.length > 0) {
290
+ md += "## From API (Current)\n\n";
291
+ md += "| Action ID | Status |\n";
292
+ md += "|-----------|--------|\n";
293
+ for (const id of apiDeprecated) {
294
+ md += `| \`${id}\` | DEPRECATED |\n`;
295
+ }
296
+ md += "\n";
297
+ }
298
+ md += "## Actions with Known Replacements\n\n";
299
+ md += "| Deprecated Action | Version | Replacement | Notes |\n";
300
+ md += "|-------------------|---------|-------------|-------|\n";
301
+ for (const d of DEPRECATED_ACTIONS_WITH_REPLACEMENT) {
302
+ const repl = Array.isArray(d.replacement) ? d.replacement.join(" or ") : d.replacement;
303
+ const replVer = d.replacementVersion ? ` ${d.replacementVersion}` : "";
304
+ md += `| \`${d.action}\` | ${d.version} | \`${repl}\`${replVer} | ${d.migrationNotes || "-"} |\n`;
305
+ }
306
+ md += "\n## Actions with No Known Replacement\n\n";
307
+ md += "| Action | Environment | Notes |\n";
308
+ md += "|--------|-------------|-------|\n";
309
+ for (const d of DEPRECATED_ACTIONS_NO_REPLACEMENT) {
310
+ md += `| \`${d.action}\` | ${d.environment || "all"} | ${d.migrationNotes || "-"} |\n`;
311
+ }
312
+ md += `\n**Total Known Deprecated**: ${ALL_DEPRECATED_ACTIONS.length} actions\n`;
313
+ md += `\n> **Best Practice**: Use \`workflow(mode="get")\` to check for deprecation warnings in specific workflows.\n`;
314
+ return md;
315
+ },
316
+ },
317
+ // Workflow Enabling Constraints - Requirements for persona activation
318
+ {
319
+ uri: "ema://rules/workflow-constraints",
320
+ name: "rules/workflow-constraints",
321
+ description: "Workflow enabling constraints: requirements that must be met before a persona can be activated",
322
+ mimeType: "application/json",
323
+ generate: async () => JSON.stringify({
324
+ _note: "These constraints are checked by the Ema backend when enabling a persona.",
325
+ _source: "ema/ema_backend/db/models/personas_model.py:672-756",
326
+ _last_synced: "2026-01-26",
327
+ enabling_constraints: WORKFLOW_ENABLING_CONSTRAINTS,
328
+ minimum_viable_workflows: MINIMUM_VIABLE_WORKFLOWS,
329
+ }, null, 2),
330
+ },
331
+ {
332
+ uri: "ema://rules/workflow-constraints-summary",
333
+ name: "rules/workflow-constraints-summary",
334
+ description: "Workflow constraints summary: checklist of requirements for enabling a persona",
335
+ mimeType: "text/markdown",
336
+ generate: async () => {
337
+ let md = "# Workflow Enabling Constraints\n\n";
338
+ md += "> These constraints must be satisfied for a persona to be activated.\n\n";
339
+ md += "> Source: ema_backend/db/models/personas_model.py (synced 2026-01-26)\n\n";
340
+ md += "## Required Checks\n\n";
341
+ md += "| # | Constraint | Error State | Fix |\n";
342
+ md += "|---|------------|-------------|-----|\n";
343
+ for (const c of WORKFLOW_ENABLING_CONSTRAINTS) {
344
+ const critical = c.critical ? "**" : "";
345
+ md += `| ${c.id} | ${critical}${c.name}${critical} | \`${c.errorState}\` | ${c.fix} |\n`;
346
+ }
347
+ md += "\n## Minimum Viable Workflows by Type\n\n";
348
+ for (const [type, mvw] of Object.entries(MINIMUM_VIABLE_WORKFLOWS)) {
349
+ md += `### ${type}\n\n`;
350
+ md += `**Structure**: \`${mvw.exampleStructure}\`\n\n`;
351
+ md += `- Required nodes: ${mvw.requiredNodes.map(n => `\`${n}\``).join(", ")}\n`;
352
+ md += `- Required outputs: ${mvw.requiredOutputs.map(o => `\`${o}\``).join(", ")}\n`;
353
+ if (mvw.requiredWidgets) {
354
+ md += `- Required widgets: ${mvw.requiredWidgets.map(w => `\`${w}\``).join(", ")}\n`;
355
+ }
356
+ if (mvw.notes) {
357
+ md += `- Notes: ${mvw.notes}\n`;
358
+ }
359
+ md += "\n";
360
+ }
361
+ md += "## Critical Rule: WORKFLOW_OUTPUT\n\n";
362
+ md += "**Every workflow must have a `results.WORKFLOW_OUTPUT` that maps to an action output.**\n\n";
363
+ md += "```json\n";
364
+ md += '{\n "results": {\n "WORKFLOW_OUTPUT": {\n "actionName": "respond_node",\n "outputName": "response_with_sources"\n }\n }\n}\n';
365
+ md += "```\n";
366
+ return md;
367
+ },
368
+ },
369
+ // ─────────────────────────────────────────────────────────────────────────────
370
+ // Workflow Requirements & Guidance
371
+ // NOT hardcoded templates - provide requirements and let LLM generate
372
+ // ─────────────────────────────────────────────────────────────────────────────
373
+ {
374
+ uri: "ema://templates/voice-ai/requirements",
375
+ name: "templates/voice-ai/requirements",
376
+ description: "Voice AI workflow requirements and guidance. Use workflow(mode='get') for schema, then generate workflow_def.",
377
+ mimeType: "application/json",
378
+ generate: async () => {
379
+ return JSON.stringify({
380
+ _note: "Requirements and guidance for Voice AI workflows. LLM generates workflow_def based on these.",
381
+ _usage: "1) workflow(mode='get', persona_id='...') for schema, 2) Generate workflow_def, 3) workflow(mode='deploy', ...)",
382
+ hard_requirements: {
383
+ workflow_output: {
384
+ rule: "MUST have results.WORKFLOW_OUTPUT mapped to final action output",
385
+ example: '{ "results": { "WORKFLOW_OUTPUT": { "actionName": "respond", "outputName": "response" } } }',
386
+ },
387
+ workflow_name: {
388
+ rule: "workflowName MUST be ['ema', 'personas', '<actual_persona_id>']",
389
+ },
390
+ trigger: {
391
+ rule: "Voice AI uses chat_trigger (type CHAT)",
392
+ namespace: ["triggers", "emainternal"],
393
+ },
394
+ response: {
395
+ rule: "Must produce a response output that wires to WORKFLOW_OUTPUT",
396
+ },
397
+ },
398
+ required_widgets: [
399
+ { name: "conversationSettings", type: 39, purpose: "Voice identity, welcome message, instructions" },
400
+ { name: "voiceSettings", type: 40, purpose: "Language, voice model" },
401
+ { name: "callSettings", type: 41, purpose: "Call forwarding, spam prevention" },
402
+ { name: "vadSettings", type: 42, purpose: "Voice activity detection, timeouts" },
403
+ ],
404
+ common_patterns: {
405
+ simple_qa: "chat_trigger → search → respond_for_external_actions → WORKFLOW_OUTPUT",
406
+ with_routing: "chat_trigger → chat_categorizer → [branch per intent] → respond → WORKFLOW_OUTPUT",
407
+ with_tools: "chat_trigger → categorizer → external_action_caller → respond_for_external_actions → WORKFLOW_OUTPUT",
408
+ },
409
+ best_practices: [
410
+ "Use search/v2 (NOT v0) with datastore_configs",
411
+ "Include Fallback category in every categorizer",
412
+ "Use respond_for_external_actions (NOT deprecated respond_with_sources)",
413
+ "Consider general_hitl before actions with side effects",
414
+ ],
415
+ _next_step: "Call workflow(mode='get', persona_id='...') to get full schema, then generate workflow_def",
416
+ }, null, 2);
417
+ },
418
+ },
419
+ {
420
+ uri: "ema://templates/voice-ai/config",
421
+ name: "templates/voice-ai/config",
422
+ description: "Voice AI configuration template (proto_config widgets). Customize values for your use case.",
423
+ mimeType: "application/json",
424
+ generate: async () => {
425
+ const config = {
426
+ _note: "Voice AI configuration template. Customize values for your use case.",
427
+ _usage: "persona(method='update', id='<ID>', config={widgets: [<these widgets with your values>]})",
428
+ widgets: [
429
+ {
430
+ name: "conversationSettings",
431
+ type: 39,
432
+ conversationSettings: {
433
+ ...VOICE_TEMPLATE_FALLBACK.conversationSettings,
434
+ },
435
+ },
436
+ {
437
+ name: "voiceSettings",
438
+ type: 40,
439
+ voiceSettings: {
440
+ ...VOICE_TEMPLATE_FALLBACK.voiceSettings,
441
+ },
442
+ },
443
+ {
444
+ name: "callSettings",
445
+ type: 41,
446
+ callSettings: {
447
+ ...VOICE_TEMPLATE_FALLBACK.callSettings,
448
+ },
449
+ },
450
+ {
451
+ name: "vadSettings",
452
+ type: 42,
453
+ vadSettings: {
454
+ ...VOICE_TEMPLATE_FALLBACK.vadSettings,
455
+ },
456
+ },
457
+ ],
458
+ field_docs: VOICE_TEMPLATE_FIELD_DOCS,
459
+ };
460
+ return JSON.stringify(config, null, 2);
461
+ },
462
+ },
463
+ {
464
+ uri: "ema://templates/voice-ai/guide",
465
+ name: "templates/voice-ai/guide",
466
+ description: "Voice AI creation guide with requirements and step-by-step process",
467
+ mimeType: "text/markdown",
468
+ generate: async () => {
469
+ return `# Voice AI Creation Guide
470
+
471
+ ## Process (Follow This Order)
472
+
473
+ ### 1. Create Persona
474
+ \`\`\`
475
+ persona(method="create", name="Your Voice AI", type="voice")
476
+ \`\`\`
477
+
478
+ ### 2. Get Workflow Schema
479
+ \`\`\`
480
+ workflow(mode="get", persona_id="<ID>")
481
+ \`\`\`
482
+ This returns:
483
+ - Current workflow (if any)
484
+ - Deprecation warnings (fix these first!)
485
+ - Generation schema (agents, constraints)
486
+ - Requirements and guidance
487
+
488
+ ### 3. Generate Workflow
489
+ Using the schema, generate a workflow_def that:
490
+ - Has WORKFLOW_OUTPUT in results
491
+ - Uses non-deprecated actions
492
+ - Follows the flow pattern: trigger → processing → response → OUTPUT
493
+
494
+ ### 4. Deploy Workflow
495
+ \`\`\`
496
+ workflow(mode="deploy", persona_id="<ID>", workflow_def={...}, preview=true)
497
+ \`\`\`
498
+ Always preview first!
499
+
500
+ ### 5. Configure Settings
501
+ \`\`\`
502
+ persona(method="update", id="<ID>", config={widgets: [...]})
503
+ \`\`\`
504
+
505
+ ### 6. Upload Knowledge
506
+ \`\`\`
507
+ persona(id="<ID>", data={method:"upload", path:"your-data.txt"})
508
+ \`\`\`
509
+
510
+ ## Hard Requirements
511
+
512
+ | Requirement | Why |
513
+ |-------------|-----|
514
+ | WORKFLOW_OUTPUT | Persona cannot be activated without it |
515
+ | workflowName format | API rejects invalid namespaces |
516
+ | Non-deprecated actions | Deprecated actions may fail |
517
+
518
+ ## Check for Deprecated Actions
519
+
520
+ \`workflow(mode="get")\` returns \`deprecation_warnings\` if any actions are deprecated.
521
+ **Fix these BEFORE deploying.**
522
+
523
+ ## Common Deprecated Actions
524
+
525
+ | Deprecated | Use Instead |
526
+ |------------|-------------|
527
+ | search/v0 | search/v2 (requires datastore_configs) |
528
+ | respond_with_sources | respond_for_external_actions |
529
+ | call_llm/v0 | call_llm/v2 |
530
+
531
+ ## Anti-Patterns
532
+
533
+ ❌ Cloning random existing persona
534
+ ❌ Skipping workflow_def
535
+ ❌ Using deprecated actions
536
+ ❌ Deploying without preview
537
+
538
+ ${VOICE_TEMPLATE_FIELD_DOCS}
539
+ `;
540
+ },
541
+ },
210
542
  // Persona Templates - Dynamic from API with fallback
211
543
  {
212
544
  uri: "ema://catalog/templates",
@@ -798,8 +1130,8 @@ To read a resource, use the \`resources/read\` endpoint:
798
1130
  | Get input/output compatibility rules | Resource: \`ema://rules/input-sources\` |
799
1131
  | Get persona templates | Resource: \`ema://catalog/persona-templates\` (API) or Tool: \`template(config="voice")\` |
800
1132
  | Query live persona data | Tool: \`persona\` |
801
- | Generate a workflow | Tool: \`workflow\` |
802
- | Analyze an existing workflow | Tool: \`workflow(mode="analyze")\` |
1133
+ | Get workflow data | Tool: \`workflow(mode="get")\` |
1134
+ | Deploy workflow | Tool: \`workflow(mode="deploy")\` |
803
1135
  `;
804
1136
  return {
805
1137
  uri: "ema://index/all-resources",