@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
package/dist/mcp/server.js
CHANGED
|
@@ -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
|
|
46
|
-
|
|
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
|
-
//
|
|
1927
|
-
|
|
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
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
if (
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
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
|
-
//
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
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
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
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
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
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
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
:
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
"
|
|
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
|
|
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
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
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 (
|
|
2936
|
-
|
|
2937
|
-
|
|
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
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
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
|
|
2979
|
-
|
|
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:
|
|
2995
|
-
|
|
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:
|
|
3005
|
-
|
|
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:
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
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
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
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
|
-
//
|
|
3057
|
-
const
|
|
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:
|
|
3064
|
-
total_edges:
|
|
3065
|
-
has_trigger:
|
|
3066
|
-
|
|
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:
|
|
3070
|
-
hitl_nodes_count:
|
|
3071
|
-
has_parallel_branches:
|
|
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:
|
|
3081
|
-
:
|
|
2989
|
+
complexity_rating: nodes.length <= 5 ? "simple"
|
|
2990
|
+
: nodes.length <= 15 ? "moderate"
|
|
3082
2991
|
: "complex",
|
|
3083
2992
|
},
|
|
3084
|
-
|
|
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: {
|
|
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
|
-
//
|
|
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
|
-
|
|
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);
|