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

@@ -27,6 +27,22 @@ const packageJsonPath = join(__dirname, "..", "..", "package.json");
27
27
  const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
28
28
  export const TOOLKIT_NAME = packageJson.name;
29
29
  export const TOOLKIT_VERSION = packageJson.version;
30
+ // Try to read git commit for build info (optional, doesn't fail if not available)
31
+ import { execSync } from "node:child_process";
32
+ function getGitCommit() {
33
+ try {
34
+ const commit = execSync("git rev-parse --short HEAD", {
35
+ cwd: join(__dirname, "..", ".."),
36
+ encoding: "utf-8",
37
+ stdio: ["pipe", "pipe", "pipe"]
38
+ }).trim();
39
+ return commit;
40
+ }
41
+ catch {
42
+ return undefined;
43
+ }
44
+ }
45
+ export const TOOLKIT_COMMIT = getGitCommit();
30
46
  // Prompts, Resources, and Guidance
31
47
  import { PromptRegistry, isPromptError } from "./prompts.js";
32
48
  import { ResourceRegistry, isResourceError } from "./resources.js";
@@ -42,8 +58,8 @@ import { getToolkitRoot } from "../sdk/paths.js";
42
58
  import { SYNC_METADATA_KEY } from "../sdk/models.js";
43
59
  // Auto Builder Knowledge
44
60
  import { AGENT_CATALOG, WIDGET_CATALOG, WORKFLOW_PATTERNS, QUALIFYING_QUESTIONS, PLATFORM_CONCEPTS, WORKFLOW_EXECUTION_MODEL, COMMON_MISTAKES, DEBUG_CHECKLIST, GUIDANCE_TOPICS, PROJECT_TYPES, getAgentsByCategory, getAgentByName, getWidgetsForPersonaType, checkTypeCompatibility, getQualifyingQuestionsByCategory, getConceptByTerm, suggestAgentsForUseCase, validateWorkflowPrompt,
45
- // Workflow Analysis
46
- analyzeWorkflow, detectWorkflowIssues, validateWorkflowConnections, suggestWorkflowFixes, } from "../sdk/knowledge.js";
61
+ // Workflow Data (LLM does analysis with rules from ema://rules/*)
62
+ validateWorkflowConnections, parseWorkflowDef, } from "../sdk/knowledge.js";
47
63
  // Template fallbacks (generated from protos, used when API unavailable)
48
64
  import { getTemplateFallback, getTemplateFieldDocs, VOICE_TEMPLATE_FALLBACK } from "../sdk/generated/template-fallbacks.js";
49
65
  // Workflow Compiler (Template-driven)
@@ -1923,40 +1939,30 @@ const toolHandlers = {
1923
1939
  // Track fixes applied
1924
1940
  const appliedFixes = [];
1925
1941
  let fixAttempted = false;
1926
- // Validate workflow if requested and provided
1927
- let validationResults = { valid: true, issues: [] };
1942
+ // Validation is now done by backend when workflow is deployed
1943
+ // LLM should use ema://rules/* for pre-validation guidance
1944
+ const validationResults = { valid: true, issues: [] };
1928
1945
  if (validateFirst && workflowDef) {
1929
- const analysis = analyzeWorkflow(workflowDef, {
1930
- persona_id: personaId,
1931
- persona_name: persona.name ?? "Unknown"
1932
- });
1933
- if (!analysis.validation_passed) {
1934
- validationResults.valid = false;
1935
- validationResults.issues = analysis.issues ?? [];
1936
- // Auto-fix handling (DEPRECATED - returns suggestions instead of auto-applying)
1937
- if (autoFix && validationResults.issues.length > 0) {
1938
- // DEPRECATED: autoFix no longer auto-applies fixes.
1939
- // Per LLM-driven architecture, MCP returns suggestions for Agent to apply.
1940
- return {
1941
- environment: client["env"].name,
1942
- success: false,
1943
- persona_id: personaId,
1944
- persona_name: persona.name,
1945
- validation_failed: true,
1946
- issues: validationResults.issues,
1947
- // Return suggestions instead of applying fixes
1948
- suggestions: validationResults.issues.map(issue => ({
1949
- issue_type: issue.type,
1950
- severity: issue.severity,
1951
- node: issue.node,
1952
- reason: issue.reason,
1953
- suggested_action: `Fix "${issue.type}" on node "${issue.node ?? 'workflow'}": ${issue.reason}`,
1954
- })),
1955
- applied_fixes: [], // Empty - no longer auto-applying
1956
- _deprecated: "autoFix is deprecated. Fixes are no longer auto-applied. Use the suggestions to build explicit workflow modifications.",
1957
- _tip: "Apply fixes using: persona(id=\"...\", operations=[{type:\"rewire\", ...}]) or persona(id=\"...\", workflow_spec={...})",
1958
- };
1959
- }
1946
+ // DEPRECATED: MCP no longer pre-validates workflows
1947
+ // Backend validation happens on deploy
1948
+ // LLM uses ema://rules/anti-patterns for analysis
1949
+ console.warn("[DEPRECATED] validateFirst is deprecated - backend validates on deploy");
1950
+ if (autoFix) {
1951
+ // Auto-fix is removed - return guidance for LLM
1952
+ return {
1953
+ environment: client["env"].name,
1954
+ success: false,
1955
+ persona_id: personaId,
1956
+ persona_name: persona.name,
1957
+ _deprecation_notice: "autoFix is deprecated. Use LLM analysis with ema://rules/* instead.",
1958
+ _guidance: [
1959
+ "1. Fetch ema://rules/anti-patterns",
1960
+ "2. Analyze workflow against rules",
1961
+ "3. Make structured modifications",
1962
+ "4. Deploy via workflow(mode='deploy')",
1963
+ ],
1964
+ applied_fixes: [],
1965
+ };
1960
1966
  }
1961
1967
  }
1962
1968
  // If validation failed and not forcing, return the issues
@@ -2148,84 +2154,27 @@ const toolHandlers = {
2148
2154
  message: "This AI Employee has no workflow. Use prompt parameter to generate one: optimize_workflow(persona_id=\"...\", prompt=\"description of what it should do\")",
2149
2155
  };
2150
2156
  }
2151
- // Step 2: Analyze workflow for issues
2152
- const analysis = analyzeWorkflow(workflowDef, {
2153
- persona_id: personaId,
2154
- persona_name: persona.name ?? "Unknown",
2155
- });
2156
- const issues = analysis.issues ?? [];
2157
- const criticalIssues = issues.filter(i => i.severity === "critical");
2158
- // If no issues, workflow is healthy
2159
- if (issues.length === 0) {
2160
- const result = {
2161
- success: true,
2162
- persona: persona.name,
2163
- status: "✅ Workflow is healthy",
2164
- nodes: analysis.summary?.total_nodes ?? 0,
2165
- message: "No issues found - workflow is already optimized!",
2166
- };
2167
- // If enhancement prompt was provided, note that it can't be auto-applied
2168
- if (enhancementPrompt) {
2169
- result.enhancement_note = `Enhancement requested: "${enhancementPrompt}". Since there are no issues to fix, use the Ema UI Auto Builder to make this change manually.`;
2170
- }
2171
- return result;
2172
- }
2173
- // Step 3: Return suggestions instead of auto-applying (DEPRECATED behavior)
2174
- // Per LLM-driven architecture, MCP returns suggestions for Agent to apply.
2175
- const suggestions = issues.map(issue => ({
2176
- issue_type: issue.type,
2177
- severity: issue.severity,
2178
- node: issue.node,
2179
- reason: issue.reason,
2180
- suggested_action: `Fix "${issue.type}" on node "${issue.node ?? 'workflow'}": ${issue.reason}`,
2181
- }));
2182
- // DEPRECATED: No longer auto-apply fixes
2183
- // The appliedFixes array is now always empty
2184
- const appliedFixes = [];
2185
- const fixedWorkflow = workflowDef; // Workflow unchanged
2186
- // No re-analysis needed since we don't auto-fix
2187
- const remainingIssues = issues;
2188
- const remainingCritical = remainingIssues.filter(i => i.severity === "critical");
2189
- // If preview mode, show suggestions (DEPRECATED: no longer auto-applies)
2190
- if (preview) {
2191
- const previewResult = {
2192
- success: true,
2193
- persona: persona.name,
2194
- status: "📋 Preview - Suggestions Available",
2195
- mode: "optimize",
2196
- nodes: fixedWorkflow.actions?.length ?? 0,
2197
- found_issues: issues.length,
2198
- // Return suggestions instead of applied fixes
2199
- suggestions: suggestions,
2200
- issues: issues.length > 0 ? issues.map(i => `${i.severity === "critical" ? "❌" : "⚠️"} ${i.type}: ${i.reason}`) : ["No issues found"],
2201
- applied_fixes: [], // Empty - no longer auto-applying
2202
- _deprecated: "Auto-fix is deprecated. Review suggestions and apply fixes explicitly.",
2203
- _tip: "Apply fixes using: persona(id=\"...\", operations=[...]) or use Ema UI Auto Builder",
2204
- };
2205
- if (enhancementPrompt) {
2206
- previewResult.enhancement_note = `Enhancement "${enhancementPrompt}" noted. Use Ema UI Auto Builder to make this change.`;
2207
- }
2208
- return previewResult;
2209
- }
2210
- // Step 4: Return suggestions for issues (DEPRECATED: no longer auto-deploys)
2211
- // Per LLM-driven architecture, return suggestions for Agent to review and apply
2212
- // The Agent should use explicit operations to apply fixes, then deploy separately.
2157
+ // DEPRECATED: MCP no longer pre-analyzes workflows
2158
+ // LLM should use ema://rules/* for analysis
2159
+ console.warn("[DEPRECATED] optimize_workflow tool is deprecated - use LLM analysis with ema://rules/*");
2160
+ const nodes = parseWorkflowDef(workflowDef);
2213
2161
  return {
2214
2162
  success: true,
2215
- persona: persona.name,
2216
- status: remainingCritical.length > 0
2217
- ? "⚠️ Issues Found - Review Suggestions"
2218
- : "📋 Minor Issues - Review Suggestions",
2219
- found_issues: issues.length,
2220
- critical_issues: remainingCritical.length,
2221
- suggestions: suggestions,
2222
- applied_fixes: [], // Empty - no longer auto-applying
2223
- _deprecated: "Auto-fix and auto-deploy are deprecated. Review suggestions and apply fixes explicitly.",
2224
- _tip: remainingCritical.length > 0
2225
- ? "Critical issues found. Review suggestions and fix in Ema UI Auto Builder or use explicit operations."
2226
- : "Apply fixes using: persona(id=\"...\", operations=[...]) or use Ema UI Auto Builder",
2163
+ persona: persona?.name ?? "Unknown",
2164
+ persona_id: personaId,
2165
+ status: "DEPRECATED - use LLM analysis",
2166
+ node_count: nodes.length,
2167
+ workflow_def: workflowDef,
2168
+ _deprecation_notice: {
2169
+ message: "optimize_workflow is deprecated. MCP does not pre-compute issues.",
2170
+ new_workflow: [
2171
+ "1. Fetch rules: ema://rules/anti-patterns, ema://rules/optimizations",
2172
+ "2. Apply rules to find issues (LLM does this, not MCP)",
2173
+ "3. Make structured modifications based on your analysis",
2174
+ "4. Deploy via workflow(mode='deploy', persona_id='...', workflow_def={...})",
2175
+ ],
2176
+ },
2227
2177
  };
2228
- // Note: Auto-deploy code removed (was dead code after deprecation)
2229
2178
  },
2230
2179
  // ─────────────────────────────────────────────────────────────────────────
2231
2180
  // Action Handlers (Consolidated)
@@ -2840,40 +2789,41 @@ const toolHandlers = {
2840
2789
  // Workflow Review & Audit Handlers
2841
2790
  // ─────────────────────────────────────────────────────────────────────────────
2842
2791
  analyze_workflow: async (args) => {
2792
+ // DEPRECATED: MCP no longer pre-analyzes workflows
2793
+ // LLM should use ema://rules/* for analysis
2843
2794
  const client = createClient(args.env);
2844
2795
  const personaId = String(args.persona_id);
2845
2796
  const persona = await client.getPersonaById(personaId);
2846
2797
  if (!persona)
2847
2798
  throw new Error(`AI Employee not found: ${personaId}`);
2848
- if (!persona.workflow_def) {
2849
- return {
2850
- environment: client["env"].name,
2851
- persona_id: personaId,
2852
- persona_name: persona.name,
2853
- error: "AI Employee has no workflow_def",
2854
- recommendation: "This persona may not have a generated workflow. Use Auto Builder to create one.",
2855
- };
2856
- }
2857
- const analysis = analyzeWorkflow(persona.workflow_def, {
2799
+ const nodes = persona.workflow_def ? parseWorkflowDef(persona.workflow_def) : [];
2800
+ const connections = persona.workflow_def ? validateWorkflowConnections(persona.workflow_def) : [];
2801
+ return {
2802
+ environment: client["env"].name,
2858
2803
  persona_id: personaId,
2859
2804
  persona_name: persona.name,
2860
- environment: client["env"].name,
2861
- });
2862
- return {
2863
- ...analysis,
2864
- recommendations: analysis.validation_passed
2865
- ? ["Workflow structure looks valid. Test with sample queries to verify behavior."]
2866
- : [
2867
- "Fix all critical issues before deployment",
2868
- ...analysis.issues
2869
- .filter(i => i.severity === "critical")
2870
- .slice(0, 3)
2871
- .map(i => `Priority: ${i.reason}`),
2872
- "Use suggest_workflow_fixes to get fix proposals",
2805
+ status: "DEPRECATED - use LLM analysis",
2806
+ node_count: nodes.length,
2807
+ workflow_def: persona.workflow_def,
2808
+ connections: connections.map(c => ({
2809
+ edge: c.edge_id,
2810
+ source_type: c.source_type,
2811
+ target_type: c.target_type,
2812
+ compatible: c.compatible,
2813
+ })),
2814
+ _deprecation_notice: {
2815
+ message: "analyze_workflow is deprecated. MCP does not pre-compute issues.",
2816
+ new_workflow: [
2817
+ "1. Fetch rules: ema://rules/anti-patterns",
2818
+ "2. Apply rules to find issues (LLM does this, not MCP)",
2819
+ "3. Report your findings",
2873
2820
  ],
2821
+ },
2874
2822
  };
2875
2823
  },
2876
2824
  detect_workflow_issues: async (args) => {
2825
+ // DEPRECATED: MCP no longer detects workflow issues
2826
+ // LLM should use ema://rules/* for analysis
2877
2827
  const workflowDef = args.workflow_def;
2878
2828
  if (!workflowDef || typeof workflowDef !== "object") {
2879
2829
  return {
@@ -2881,21 +2831,19 @@ const toolHandlers = {
2881
2831
  hint: "Get workflow_def from get_ai_employee_full(persona_id).ai_employee.workflow_def",
2882
2832
  };
2883
2833
  }
2884
- const issues = detectWorkflowIssues(workflowDef);
2885
- const summary = {
2886
- total: issues.length,
2887
- critical: issues.filter(i => i.severity === "critical").length,
2888
- warning: issues.filter(i => i.severity === "warning").length,
2889
- info: issues.filter(i => i.severity === "info").length,
2890
- };
2834
+ const nodes = parseWorkflowDef(workflowDef);
2891
2835
  return {
2892
- issues,
2893
- summary,
2894
- validation_passed: summary.critical === 0,
2895
- issue_types_found: [...new Set(issues.map(i => i.type))],
2896
- next_steps: issues.length > 0
2897
- ? ["Use suggest_workflow_fixes(issues) to get fix proposals"]
2898
- : ["No issues detected - workflow structure looks valid"],
2836
+ status: "DEPRECATED - use LLM analysis",
2837
+ node_count: nodes.length,
2838
+ nodes: nodes.map(n => ({ id: n.id, action: n.action_name })),
2839
+ _deprecation_notice: {
2840
+ message: "detect_workflow_issues is deprecated. MCP does not pre-compute issues.",
2841
+ new_workflow: [
2842
+ "1. Fetch rules: ema://rules/anti-patterns",
2843
+ "2. Apply rules to this workflow (LLM does this)",
2844
+ "3. Report issues YOU find",
2845
+ ],
2846
+ },
2899
2847
  };
2900
2848
  },
2901
2849
  validate_workflow_connections: async (args) => {
@@ -2932,38 +2880,25 @@ const toolHandlers = {
2932
2880
  })),
2933
2881
  };
2934
2882
  },
2935
- suggest_workflow_fixes: async (args) => {
2936
- const issues = args.issues;
2937
- if (!Array.isArray(issues)) {
2938
- return {
2939
- error: "Invalid issues array",
2940
- hint: "Pass issues from detect_workflow_issues or analyze_workflow",
2941
- example_input: [
2942
- { type: "missing_fallback", severity: "critical", node: "intent_classifier", reason: "..." },
2943
- { type: "type_mismatch", severity: "critical", source: "trigger.chat_conversation", target: "search.query", reason: "..." },
2944
- ],
2945
- };
2946
- }
2947
- if (issues.length === 0) {
2948
- return {
2949
- message: "No issues to fix",
2950
- fixes: [],
2951
- };
2952
- }
2953
- const fixes = suggestWorkflowFixes(issues);
2883
+ suggest_workflow_fixes: async (_args) => {
2884
+ // DEPRECATED: MCP no longer suggests fixes
2885
+ // LLM should reason about fixes using ema://rules/*
2954
2886
  return {
2955
- issue_count: issues.length,
2956
- fix_count: fixes.length,
2957
- fixes,
2958
- application_order: [
2959
- "1. Fix critical issues first (missing_fallback, incomplete_hitl, cycles)",
2960
- "2. Fix type mismatches (may require adding intermediate nodes)",
2961
- "3. Address warnings (wrong_input_source, orphan nodes)",
2962
- "4. Re-run detect_workflow_issues to verify fixes",
2963
- ],
2887
+ status: "DEPRECATED - use LLM reasoning",
2888
+ _deprecation_notice: {
2889
+ message: "suggest_workflow_fixes is deprecated. LLM should propose fixes.",
2890
+ new_workflow: [
2891
+ "1. Analyze workflow with ema://rules/anti-patterns",
2892
+ "2. For each issue found, reason about the fix",
2893
+ "3. Build structured modifications",
2894
+ "4. Apply via persona(mode='update', operations=[...])",
2895
+ ],
2896
+ },
2964
2897
  };
2965
2898
  },
2966
2899
  compare_workflow_versions: async (args) => {
2900
+ // DEPRECATED: MCP no longer pre-analyzes for comparison
2901
+ // LLM should compare workflows directly
2967
2902
  const client = createClient(args.env);
2968
2903
  const idBefore = String(args.persona_id_before);
2969
2904
  const idAfter = String(args.persona_id_after);
@@ -2975,59 +2910,38 @@ const toolHandlers = {
2975
2910
  throw new Error(`AI Employee not found (before): ${idBefore}`);
2976
2911
  if (!personaAfter)
2977
2912
  throw new Error(`AI Employee not found (after): ${idAfter}`);
2978
- const analysisBefore = personaBefore.workflow_def
2979
- ? analyzeWorkflow(personaBefore.workflow_def, { persona_id: idBefore, persona_name: personaBefore.name })
2980
- : null;
2981
- const analysisAfter = personaAfter.workflow_def
2982
- ? analyzeWorkflow(personaAfter.workflow_def, { persona_id: idAfter, persona_name: personaAfter.name })
2983
- : null;
2913
+ const nodesBefore = personaBefore.workflow_def ? parseWorkflowDef(personaBefore.workflow_def) : [];
2914
+ const nodesAfter = personaAfter.workflow_def ? parseWorkflowDef(personaAfter.workflow_def) : [];
2984
2915
  // Compare fingerprints
2985
2916
  const fpBefore = personaBefore.workflow_def ? fingerprintPersona(personaBefore) : null;
2986
2917
  const fpAfter = personaAfter.workflow_def ? fingerprintPersona(personaAfter) : null;
2987
2918
  return {
2988
2919
  environment: client["env"].name,
2920
+ status: "DEPRECATED - LLM should compare",
2989
2921
  before: {
2990
2922
  persona_id: idBefore,
2991
2923
  name: personaBefore.name,
2992
2924
  fingerprint: fpBefore,
2993
2925
  has_workflow: !!personaBefore.workflow_def,
2994
- node_count: analysisBefore?.summary.total_nodes ?? 0,
2995
- edge_count: analysisBefore?.summary.total_edges ?? 0,
2996
- critical_issues: analysisBefore?.issue_summary.critical ?? 0,
2997
- validation_passed: analysisBefore?.validation_passed ?? false,
2926
+ node_count: nodesBefore.length,
2927
+ workflow_def: personaBefore.workflow_def,
2998
2928
  },
2999
2929
  after: {
3000
2930
  persona_id: idAfter,
3001
2931
  name: personaAfter.name,
3002
2932
  fingerprint: fpAfter,
3003
2933
  has_workflow: !!personaAfter.workflow_def,
3004
- node_count: analysisAfter?.summary.total_nodes ?? 0,
3005
- edge_count: analysisAfter?.summary.total_edges ?? 0,
3006
- critical_issues: analysisAfter?.issue_summary.critical ?? 0,
3007
- validation_passed: analysisAfter?.validation_passed ?? false,
2934
+ node_count: nodesAfter.length,
2935
+ workflow_def: personaAfter.workflow_def,
3008
2936
  },
3009
2937
  comparison: {
3010
2938
  fingerprints_match: fpBefore === fpAfter,
3011
- node_count_change: (analysisAfter?.summary.total_nodes ?? 0) - (analysisBefore?.summary.total_nodes ?? 0),
3012
- edge_count_change: (analysisAfter?.summary.total_edges ?? 0) - (analysisBefore?.summary.total_edges ?? 0),
3013
- critical_issues_change: (analysisAfter?.issue_summary.critical ?? 0) - (analysisBefore?.issue_summary.critical ?? 0),
3014
- validation_improved: !analysisBefore?.validation_passed && (analysisAfter?.validation_passed ?? false),
3015
- validation_regressed: (analysisBefore?.validation_passed ?? false) && !analysisAfter?.validation_passed,
2939
+ node_count_change: nodesAfter.length - nodesBefore.length,
2940
+ },
2941
+ _deprecation_notice: {
2942
+ message: "compare_workflow_versions is deprecated. LLM should compare workflows directly.",
2943
+ guidance: "Compare the workflow_def objects returned above. Use ema://rules/anti-patterns to check each.",
3016
2944
  },
3017
- issues_before: analysisBefore?.issues.slice(0, 5) ?? [],
3018
- issues_after: analysisAfter?.issues.slice(0, 5) ?? [],
3019
- recommendations: fpBefore === fpAfter
3020
- ? ["No workflow changes detected between versions"]
3021
- : [
3022
- analysisAfter?.validation_passed
3023
- ? "After version passes validation"
3024
- : "After version has validation issues - review before deploying",
3025
- (analysisAfter?.issue_summary.critical ?? 0) > (analysisBefore?.issue_summary.critical ?? 0)
3026
- ? "Warning: More critical issues in after version"
3027
- : (analysisAfter?.issue_summary.critical ?? 0) < (analysisBefore?.issue_summary.critical ?? 0)
3028
- ? "Good: Fewer critical issues in after version"
3029
- : "Same number of critical issues",
3030
- ],
3031
2945
  };
3032
2946
  },
3033
2947
  get_workflow_metrics: async (args) => {
@@ -3044,54 +2958,39 @@ const toolHandlers = {
3044
2958
  error: "AI Employee has no workflow_def",
3045
2959
  };
3046
2960
  }
3047
- const analysis = analyzeWorkflow(persona.workflow_def, {
3048
- persona_id: personaId,
3049
- persona_name: persona.name,
3050
- environment: client["env"].name,
3051
- });
3052
- // Calculate complexity indicators
3053
- const avgEdgesPerNode = analysis.summary.total_nodes > 0
3054
- ? (analysis.summary.total_edges / analysis.summary.total_nodes).toFixed(2)
2961
+ const nodes = parseWorkflowDef(persona.workflow_def);
2962
+ const connections = validateWorkflowConnections(persona.workflow_def);
2963
+ // Calculate basic metrics
2964
+ const totalEdges = nodes.reduce((sum, n) => sum + (n.incoming_edges?.length ?? 0), 0);
2965
+ const avgEdgesPerNode = nodes.length > 0
2966
+ ? (totalEdges / nodes.length).toFixed(2)
3055
2967
  : 0;
3056
- // Estimate parallel vs sequential
3057
- const hasParallelBranches = analysis.summary.categorizers_count > 0;
2968
+ // Check for categorizers and HITL
2969
+ const categorizerCount = nodes.filter(n => n.action_name?.includes("categorizer")).length;
2970
+ const hitlCount = nodes.filter(n => n.action_name?.includes("hitl") || n.id?.includes("hitl")).length;
2971
+ const hasTrigger = nodes.some(n => n.action_name === "trigger" || n.id === "trigger");
3058
2972
  return {
3059
2973
  environment: client["env"].name,
3060
2974
  persona_id: personaId,
3061
2975
  persona_name: persona.name,
3062
2976
  structure: {
3063
- total_nodes: analysis.summary.total_nodes,
3064
- total_edges: analysis.summary.total_edges,
3065
- has_trigger: analysis.summary.has_trigger,
3066
- has_workflow_output: analysis.summary.has_workflow_output,
2977
+ total_nodes: nodes.length,
2978
+ total_edges: totalEdges,
2979
+ has_trigger: hasTrigger,
2980
+ connection_count: connections.length,
3067
2981
  },
3068
2982
  routing: {
3069
- categorizers_count: analysis.summary.categorizers_count,
3070
- hitl_nodes_count: analysis.summary.hitl_nodes_count,
3071
- has_parallel_branches: hasParallelBranches,
3072
- },
3073
- quality: {
3074
- validation_passed: analysis.validation_passed,
3075
- critical_issues: analysis.issue_summary.critical,
3076
- warnings: analysis.issue_summary.warning,
2983
+ categorizers_count: categorizerCount,
2984
+ hitl_nodes_count: hitlCount,
2985
+ has_parallel_branches: categorizerCount > 0,
3077
2986
  },
3078
2987
  complexity: {
3079
2988
  avg_edges_per_node: avgEdgesPerNode,
3080
- complexity_rating: analysis.summary.total_nodes <= 5 ? "simple"
3081
- : analysis.summary.total_nodes <= 15 ? "moderate"
2989
+ complexity_rating: nodes.length <= 5 ? "simple"
2990
+ : nodes.length <= 15 ? "moderate"
3082
2991
  : "complex",
3083
2992
  },
3084
- recommendations: [
3085
- analysis.summary.categorizers_count === 0 && analysis.summary.total_nodes > 3
3086
- ? "Consider adding intent routing with chat_categorizer for better user experience"
3087
- : null,
3088
- analysis.summary.hitl_nodes_count === 0
3089
- ? "No HITL nodes - consider adding human approval for sensitive actions"
3090
- : null,
3091
- analysis.issue_summary.critical > 0
3092
- ? `${analysis.issue_summary.critical} critical issues need attention - use analyze_workflow for details`
3093
- : null,
3094
- ].filter(Boolean),
2993
+ _note: "Use ema://rules/anti-patterns for quality analysis. MCP no longer pre-computes issues.",
3095
2994
  };
3096
2995
  },
3097
2996
  // ─────────────────────────────────────────────────────────────────────────
@@ -3812,7 +3711,7 @@ const toolHandlers = {
3812
3711
  return handleEnv({}, () => getAvailableEnvironments().map(e => ({
3813
3712
  name: e.name,
3814
3713
  isDefault: e.name === getDefaultEnvName(),
3815
- })), { name: TOOLKIT_NAME, version: TOOLKIT_VERSION });
3714
+ })), { name: TOOLKIT_NAME, version: TOOLKIT_VERSION, commit: TOOLKIT_COMMIT });
3816
3715
  },
3817
3716
  persona: async (args) => {
3818
3717
  const targetEnv = args.env ?? getDefaultEnvName();
@@ -4060,7 +3959,11 @@ const toolHandlers = {
4060
3959
  name: e.name,
4061
3960
  isDefault: e.name === getDefaultEnvName(),
4062
3961
  })),
4063
- toolkit: { name: TOOLKIT_NAME, version: TOOLKIT_VERSION },
3962
+ toolkit: {
3963
+ name: TOOLKIT_NAME,
3964
+ version: TOOLKIT_VERSION,
3965
+ commit: TOOLKIT_COMMIT,
3966
+ },
4064
3967
  client,
4065
3968
  });
4066
3969
  },
@@ -4085,7 +3988,8 @@ const toolHandlers = {
4085
3988
  }
4086
3989
  switch (catalogType) {
4087
3990
  case "actions": {
4088
- // Route to action handler
3991
+ // API-FIRST: Try API, fall back to static AGENT_CATALOG
3992
+ // Senior review: Use API as primary source for live data
4089
3993
  if (method === "recommend" && forUseCase) {
4090
3994
  const suggestions = suggestAgentsForUseCase(forUseCase);
4091
3995
  return {
@@ -4097,18 +4001,55 @@ const toolHandlers = {
4097
4001
  : "No specific recommendations. Try browsing with catalog(method=\"list\", type=\"actions\").",
4098
4002
  };
4099
4003
  }
4004
+ // Try API first for live data
4005
+ try {
4006
+ let apiActions = await client.listActions();
4007
+ if (id) {
4008
+ const action = apiActions.find(a => a.name === id || a.name?.toLowerCase() === id.toLowerCase());
4009
+ if (action) {
4010
+ // Merge with docs from catalog for richer info
4011
+ const docs = getAgentByName(id);
4012
+ return {
4013
+ type: "actions",
4014
+ action: { ...action, documentation: docs },
4015
+ source: "api",
4016
+ };
4017
+ }
4018
+ // Fall through to catalog
4019
+ }
4020
+ if (category) {
4021
+ apiActions = apiActions.filter(a => a.category === category);
4022
+ }
4023
+ if (query) {
4024
+ const q = query.toLowerCase();
4025
+ apiActions = apiActions.filter(a => a.name?.toLowerCase().includes(q) ||
4026
+ a.category?.toLowerCase().includes(q));
4027
+ }
4028
+ return {
4029
+ type: "actions",
4030
+ count: apiActions.length,
4031
+ actions: apiActions.map(a => ({ name: a.name, category: a.category, enabled: a.enabled })),
4032
+ source: "api",
4033
+ _tip: "Get details: catalog(method=\"get\", type=\"actions\", id=\"<name>\")",
4034
+ _next_step: apiActions.length > 0 ? `catalog(method="get", type="actions", id="${apiActions[0].name}")` : undefined,
4035
+ };
4036
+ }
4037
+ catch {
4038
+ // Fallback to static catalog
4039
+ }
4040
+ // Fallback: Use static AGENT_CATALOG
4100
4041
  if (id) {
4101
4042
  const action = getAgentByName(id);
4102
4043
  if (!action) {
4103
4044
  return { error: `Action not found: ${id}` };
4104
4045
  }
4105
- return { type: "actions", action };
4046
+ return { type: "actions", action, source: "catalog" };
4106
4047
  }
4107
4048
  if (category) {
4108
4049
  const actions = getAgentsByCategory(category);
4109
- return { type: "actions", category, count: actions.length, actions };
4050
+ return { type: "actions", category, count: actions.length, actions, source: "catalog" };
4110
4051
  }
4111
- // List all
4052
+ // List all from catalog
4112
4053
  let actions = Object.values(AGENT_CATALOG);
4113
4054
  if (query) {
4114
4055
  const q = query.toLowerCase();
@@ -4120,6 +4061,7 @@ const toolHandlers = {
4120
4061
  type: "actions",
4121
4062
  count: actions.length,
4122
4063
  actions: actions.map(a => ({ name: a.actionName, displayName: a.displayName, category: a.category, description: a.description })),
4064
+ source: "catalog",
4123
4065
  _tip: "Get details: catalog(method=\"get\", type=\"actions\", id=\"<name>\")",
4124
4066
  _next_step: actions.length > 0 ? `catalog(method="get", type="actions", id="${actions[0].actionName}")` : undefined,
4125
4067
  };
@@ -4856,7 +4798,11 @@ export async function startMcpServer() {
4856
4798
  });
4857
4799
  const transport = new StdioServerTransport();
4858
4800
  await server.connect(transport);
4859
- console.error("Ema MCP Server started (multi-env) with prompts and resources");
4801
+ // Log startup with version and commit info
4802
+ const buildInfo = TOOLKIT_COMMIT
4803
+ ? `${TOOLKIT_VERSION} (${TOOLKIT_COMMIT})`
4804
+ : TOOLKIT_VERSION;
4805
+ console.error(`Ema MCP Server started: ${TOOLKIT_NAME}@${buildInfo}`);
4860
4806
  }
4861
4807
  startMcpServer().catch((error) => {
4862
4808
  console.error("Failed to start MCP server:", error);