@ema.co/mcp-toolkit 2026.1.26 → 2026.1.27-10
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 +17 -20
- package/dist/mcp/handlers/data/index.js +75 -6
- package/dist/mcp/handlers/deprecation.js +50 -0
- package/dist/mcp/handlers/env/index.js +3 -3
- package/dist/mcp/handlers/knowledge/index.js +44 -237
- package/dist/mcp/handlers/persona/create.js +63 -18
- package/dist/mcp/handlers/persona/index.js +9 -10
- package/dist/mcp/handlers/persona/update.js +6 -3
- package/dist/mcp/handlers/reference/index.js +15 -2
- package/dist/mcp/handlers/sync/index.js +3 -18
- package/dist/mcp/handlers/workflow/analyze.js +53 -105
- package/dist/mcp/handlers/workflow/deploy.js +160 -1
- package/dist/mcp/handlers/workflow/generate.js +8 -28
- package/dist/mcp/handlers/workflow/index.js +258 -85
- package/dist/mcp/handlers/workflow/modify.js +69 -26
- package/dist/mcp/handlers/workflow/optimize.js +22 -108
- package/dist/mcp/handlers/workflow/utils.js +0 -102
- package/dist/mcp/handlers-consolidated.js +15 -38
- package/dist/mcp/prompts.js +82 -44
- package/dist/mcp/resources.js +335 -3
- package/dist/mcp/server.js +242 -457
- package/dist/mcp/tools.js +44 -61
- package/dist/sdk/action-schema-parser.js +11 -5
- package/dist/sdk/client.js +81 -25
- package/dist/sdk/ema-client.js +11 -0
- package/dist/sdk/generated/deprecated-actions.js +171 -0
- package/dist/sdk/guidance.js +58 -35
- package/dist/sdk/index.js +8 -7
- package/dist/sdk/knowledge.js +216 -1932
- 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-generator.js +2 -1
- package/dist/sdk/workflow-intent.js +43 -3
- package/dist/sdk/workflow-transformer.js +0 -342
- package/docs/dashboard-operations.md +35 -0
- package/docs/ema-user-guide.md +66 -0
- package/docs/mcp-tools-guide.md +74 -45
- package/package.json +2 -2
- package/dist/mcp/handlers/persona/analyze.js +0 -275
- package/dist/mcp/handlers/persona/compare.js +0 -32
- package/dist/mcp/handlers/workflow/compile.js +0 -39
- package/docs/DEBUG-ANALYSIS-unused-category-type-mismatch.md +0 -481
- package/docs/TODO-fix-analyzer-and-modify.md +0 -182
- 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)
|
|
@@ -92,6 +108,9 @@ function summarizeWorkflow(workflowDef) {
|
|
|
92
108
|
if (nodeNames.length > 0) {
|
|
93
109
|
lines.push(`\nNodes: ${nodeNames.slice(0, 20).join(", ")}${nodeNames.length > 20 ? "..." : ""}`);
|
|
94
110
|
}
|
|
111
|
+
// TODO: This uses keyword matching to detect workflow features.
|
|
112
|
+
// Consider using ActionRegistry metadata instead for consistency with LLM-driven architecture.
|
|
113
|
+
// Current approach is acceptable for Auto Builder descriptions but could be improved.
|
|
95
114
|
// Check for categorizer (indicates intent routing)
|
|
96
115
|
const hasCategorizer = Array.from(actionTypes.keys()).some(t => t.includes("categorizer"));
|
|
97
116
|
if (hasCategorizer) {
|
|
@@ -111,8 +130,19 @@ function summarizeWorkflow(workflowDef) {
|
|
|
111
130
|
* Apply automatic fixes to a workflow based on detected issues.
|
|
112
131
|
* This function attempts to fix common issues like missing WORKFLOW_OUTPUT,
|
|
113
132
|
* wrong input sources, and other structural problems.
|
|
133
|
+
*
|
|
134
|
+
* @deprecated This function violates the LLM-driven architecture principle.
|
|
135
|
+
* MCP should not auto-fix workflows - it should return suggestions for the Agent to apply.
|
|
136
|
+
* Per CLAUDE.md: "Auto-fix issues | ❌ FORBIDDEN | ✅ Proposes fixes"
|
|
137
|
+
*
|
|
138
|
+
* This function will be removed in a future version. Instead:
|
|
139
|
+
* 1. Use analyzeWorkflow() to detect issues
|
|
140
|
+
* 2. Return issues as suggestions to the Agent
|
|
141
|
+
* 3. Let Agent apply fixes via persona(id="...", operations=[...])
|
|
114
142
|
*/
|
|
115
143
|
function applyWorkflowFixes(workflowDef, issues, persona) {
|
|
144
|
+
// Log deprecation warning
|
|
145
|
+
console.warn("[DEPRECATED] applyWorkflowFixes() called - this function violates LLM-driven architecture and will be removed");
|
|
116
146
|
// Deep clone the workflow to avoid mutating the original
|
|
117
147
|
const fixedWorkflow = JSON.parse(JSON.stringify(workflowDef));
|
|
118
148
|
const appliedFixes = [];
|
|
@@ -1909,30 +1939,30 @@ const toolHandlers = {
|
|
|
1909
1939
|
// Track fixes applied
|
|
1910
1940
|
const appliedFixes = [];
|
|
1911
1941
|
let fixAttempted = false;
|
|
1912
|
-
//
|
|
1913
|
-
|
|
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: [] };
|
|
1914
1945
|
if (validateFirst && workflowDef) {
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
if (
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
}
|
|
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
|
+
};
|
|
1936
1966
|
}
|
|
1937
1967
|
}
|
|
1938
1968
|
// If validation failed and not forcing, return the issues
|
|
@@ -2124,152 +2154,27 @@ const toolHandlers = {
|
|
|
2124
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\")",
|
|
2125
2155
|
};
|
|
2126
2156
|
}
|
|
2127
|
-
//
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
// If no issues, workflow is healthy
|
|
2135
|
-
if (issues.length === 0) {
|
|
2136
|
-
const result = {
|
|
2137
|
-
success: true,
|
|
2138
|
-
persona: persona.name,
|
|
2139
|
-
status: "✅ Workflow is healthy",
|
|
2140
|
-
nodes: analysis.summary?.total_nodes ?? 0,
|
|
2141
|
-
message: "No issues found - workflow is already optimized!",
|
|
2142
|
-
};
|
|
2143
|
-
// If enhancement prompt was provided, note that it can't be auto-applied
|
|
2144
|
-
if (enhancementPrompt) {
|
|
2145
|
-
result.enhancement_note = `Enhancement requested: "${enhancementPrompt}". Since there are no issues to fix, use the Ema UI Auto Builder to make this change manually.`;
|
|
2146
|
-
}
|
|
2147
|
-
return result;
|
|
2148
|
-
}
|
|
2149
|
-
// Step 3: Apply fixes
|
|
2150
|
-
let fixedWorkflow = workflowDef;
|
|
2151
|
-
const appliedFixes = [];
|
|
2152
|
-
if (issues.length > 0) {
|
|
2153
|
-
const fixResult = applyWorkflowFixes(workflowDef, issues, persona);
|
|
2154
|
-
fixedWorkflow = fixResult.fixedWorkflow;
|
|
2155
|
-
appliedFixes.push(...fixResult.appliedFixes);
|
|
2156
|
-
}
|
|
2157
|
-
// Re-analyze after fixes
|
|
2158
|
-
const postFixAnalysis = analyzeWorkflow(fixedWorkflow, {
|
|
2159
|
-
persona_id: personaId,
|
|
2160
|
-
persona_name: persona.name ?? "Unknown",
|
|
2161
|
-
});
|
|
2162
|
-
const remainingIssues = postFixAnalysis.issues ?? [];
|
|
2163
|
-
const remainingCritical = remainingIssues.filter(i => i.severity === "critical");
|
|
2164
|
-
// If preview mode, show what would happen without deploying
|
|
2165
|
-
if (preview) {
|
|
2166
|
-
const fixedCount = appliedFixes.filter(f => f.applied).length;
|
|
2167
|
-
const failedCount = appliedFixes.filter(f => !f.applied).length;
|
|
2168
|
-
const previewResult = {
|
|
2169
|
-
success: true,
|
|
2170
|
-
persona: persona.name,
|
|
2171
|
-
status: "📋 Preview - Fixes Available",
|
|
2172
|
-
mode: "optimize",
|
|
2173
|
-
nodes: fixedWorkflow.actions?.length ?? 0,
|
|
2174
|
-
found_issues: issues.length,
|
|
2175
|
-
can_fix: fixedCount,
|
|
2176
|
-
cannot_fix: failedCount,
|
|
2177
|
-
ready_to_deploy: remainingCritical.length === 0,
|
|
2178
|
-
issues: issues.length > 0 ? issues.map(i => `${i.severity === "critical" ? "❌" : "⚠️"} ${i.type}: ${i.reason}`) : ["No issues found"],
|
|
2179
|
-
fixes: appliedFixes.length > 0 ? appliedFixes.map(f => `${f.applied ? "✅" : "❌"} ${f.description}`) : [],
|
|
2180
|
-
next_step: remainingCritical.length === 0
|
|
2181
|
-
? `Run workflow(persona_id="${personaId}", mode="optimize") without preview to deploy fixes.`
|
|
2182
|
-
: `${remainingCritical.length} critical issue(s) need manual fix in Ema UI.`,
|
|
2183
|
-
};
|
|
2184
|
-
if (enhancementPrompt) {
|
|
2185
|
-
previewResult.enhancement_note = `Enhancement "${enhancementPrompt}" noted. Auto-fixes will be applied first; use Ema UI Auto Builder for the enhancement.`;
|
|
2186
|
-
}
|
|
2187
|
-
return previewResult;
|
|
2188
|
-
}
|
|
2189
|
-
// Step 4: Deploy if no critical issues remain
|
|
2190
|
-
if (remainingCritical.length > 0) {
|
|
2191
|
-
return {
|
|
2192
|
-
success: false,
|
|
2193
|
-
persona: persona.name,
|
|
2194
|
-
status: "❌ Cannot auto-fix",
|
|
2195
|
-
fixed: appliedFixes.filter(f => f.applied).length,
|
|
2196
|
-
remaining_critical: remainingCritical.length,
|
|
2197
|
-
manual_fixes_needed: remainingCritical.map(i => ({
|
|
2198
|
-
problem: i.type,
|
|
2199
|
-
details: i.reason,
|
|
2200
|
-
fix: i.recommendation,
|
|
2201
|
-
})),
|
|
2202
|
-
message: `${remainingCritical.length} issue(s) need manual fix in the Ema UI Auto Builder.`,
|
|
2203
|
-
};
|
|
2204
|
-
}
|
|
2205
|
-
// Sanitize workflow before deployment
|
|
2206
|
-
// Fix enumTypes - check nested structure
|
|
2207
|
-
const enumTypes = fixedWorkflow.enumTypes;
|
|
2208
|
-
if (Array.isArray(enumTypes)) {
|
|
2209
|
-
const validEnumTypes = enumTypes.filter(e => {
|
|
2210
|
-
const outerName = e.name;
|
|
2211
|
-
const innerName = outerName?.name;
|
|
2212
|
-
const actualName = innerName?.name;
|
|
2213
|
-
return typeof actualName === "string" && actualName.trim().length > 0;
|
|
2214
|
-
});
|
|
2215
|
-
if (validEnumTypes.length > 0) {
|
|
2216
|
-
fixedWorkflow.enumTypes = validEnumTypes;
|
|
2217
|
-
}
|
|
2218
|
-
else {
|
|
2219
|
-
delete fixedWorkflow.enumTypes;
|
|
2220
|
-
}
|
|
2221
|
-
}
|
|
2222
|
-
// Copy workflowName from existing
|
|
2223
|
-
const existingWfName = workflowDef.workflowName;
|
|
2224
|
-
if (existingWfName) {
|
|
2225
|
-
fixedWorkflow.workflowName = JSON.parse(JSON.stringify(existingWfName));
|
|
2226
|
-
}
|
|
2227
|
-
// Sanitize existing proto_config widgets
|
|
2228
|
-
const mergedProtoConfig = mergeProtoConfig(persona.proto_config, undefined);
|
|
2229
|
-
// Build and send request
|
|
2230
|
-
const req = {
|
|
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);
|
|
2161
|
+
return {
|
|
2162
|
+
success: true,
|
|
2163
|
+
persona: persona?.name ?? "Unknown",
|
|
2231
2164
|
persona_id: personaId,
|
|
2232
|
-
|
|
2233
|
-
|
|
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
|
+
},
|
|
2234
2177
|
};
|
|
2235
|
-
const actionsArr = fixedWorkflow.actions ?? [];
|
|
2236
|
-
try {
|
|
2237
|
-
await client.updateAiEmployee(req);
|
|
2238
|
-
const fixedCount = appliedFixes.filter(f => f.applied).length;
|
|
2239
|
-
const deployResult = {
|
|
2240
|
-
success: true,
|
|
2241
|
-
persona: persona.name,
|
|
2242
|
-
status: "✅ Optimized & Deployed",
|
|
2243
|
-
mode: "optimized",
|
|
2244
|
-
issues_found: issues.length,
|
|
2245
|
-
issues_fixed: fixedCount,
|
|
2246
|
-
nodes: postFixAnalysis.summary?.total_nodes ?? actionsArr.length,
|
|
2247
|
-
message: fixedCount > 0
|
|
2248
|
-
? `Fixed ${fixedCount} issue(s) and deployed!`
|
|
2249
|
-
: "Deployed successfully!",
|
|
2250
|
-
next_step: "Test in the Ema simulator to verify behavior.",
|
|
2251
|
-
};
|
|
2252
|
-
if (enhancementPrompt) {
|
|
2253
|
-
deployResult.enhancement_note = `Fixes applied. Enhancement "${enhancementPrompt}" requires manual implementation via Ema UI Auto Builder.`;
|
|
2254
|
-
}
|
|
2255
|
-
return deployResult;
|
|
2256
|
-
}
|
|
2257
|
-
catch (err) {
|
|
2258
|
-
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
2259
|
-
// Extract EmaApiError body if available
|
|
2260
|
-
const apiBody = err?.body;
|
|
2261
|
-
const statusCode = err?.statusCode;
|
|
2262
|
-
return {
|
|
2263
|
-
success: false,
|
|
2264
|
-
persona: persona.name,
|
|
2265
|
-
status: "❌ Deploy Failed",
|
|
2266
|
-
error: errorMessage,
|
|
2267
|
-
status_code: statusCode,
|
|
2268
|
-
api_response: apiBody ? JSON.parse(apiBody) : undefined,
|
|
2269
|
-
fixes_attempted: appliedFixes.filter(f => f.applied).length,
|
|
2270
|
-
suggestion: "Check the api_response field for the actual backend error.",
|
|
2271
|
-
};
|
|
2272
|
-
}
|
|
2273
2178
|
},
|
|
2274
2179
|
// ─────────────────────────────────────────────────────────────────────────
|
|
2275
2180
|
// Action Handlers (Consolidated)
|
|
@@ -2884,40 +2789,41 @@ const toolHandlers = {
|
|
|
2884
2789
|
// Workflow Review & Audit Handlers
|
|
2885
2790
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
2886
2791
|
analyze_workflow: async (args) => {
|
|
2792
|
+
// DEPRECATED: MCP no longer pre-analyzes workflows
|
|
2793
|
+
// LLM should use ema://rules/* for analysis
|
|
2887
2794
|
const client = createClient(args.env);
|
|
2888
2795
|
const personaId = String(args.persona_id);
|
|
2889
2796
|
const persona = await client.getPersonaById(personaId);
|
|
2890
2797
|
if (!persona)
|
|
2891
2798
|
throw new Error(`AI Employee not found: ${personaId}`);
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
persona_name: persona.name,
|
|
2897
|
-
error: "AI Employee has no workflow_def",
|
|
2898
|
-
recommendation: "This persona may not have a generated workflow. Use Auto Builder to create one.",
|
|
2899
|
-
};
|
|
2900
|
-
}
|
|
2901
|
-
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,
|
|
2902
2803
|
persona_id: personaId,
|
|
2903
2804
|
persona_name: persona.name,
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
:
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
"
|
|
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",
|
|
2917
2820
|
],
|
|
2821
|
+
},
|
|
2918
2822
|
};
|
|
2919
2823
|
},
|
|
2920
2824
|
detect_workflow_issues: async (args) => {
|
|
2825
|
+
// DEPRECATED: MCP no longer detects workflow issues
|
|
2826
|
+
// LLM should use ema://rules/* for analysis
|
|
2921
2827
|
const workflowDef = args.workflow_def;
|
|
2922
2828
|
if (!workflowDef || typeof workflowDef !== "object") {
|
|
2923
2829
|
return {
|
|
@@ -2925,21 +2831,19 @@ const toolHandlers = {
|
|
|
2925
2831
|
hint: "Get workflow_def from get_ai_employee_full(persona_id).ai_employee.workflow_def",
|
|
2926
2832
|
};
|
|
2927
2833
|
}
|
|
2928
|
-
const
|
|
2929
|
-
const summary = {
|
|
2930
|
-
total: issues.length,
|
|
2931
|
-
critical: issues.filter(i => i.severity === "critical").length,
|
|
2932
|
-
warning: issues.filter(i => i.severity === "warning").length,
|
|
2933
|
-
info: issues.filter(i => i.severity === "info").length,
|
|
2934
|
-
};
|
|
2834
|
+
const nodes = parseWorkflowDef(workflowDef);
|
|
2935
2835
|
return {
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
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
|
+
},
|
|
2943
2847
|
};
|
|
2944
2848
|
},
|
|
2945
2849
|
validate_workflow_connections: async (args) => {
|
|
@@ -2976,38 +2880,25 @@ const toolHandlers = {
|
|
|
2976
2880
|
})),
|
|
2977
2881
|
};
|
|
2978
2882
|
},
|
|
2979
|
-
suggest_workflow_fixes: async (
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
return {
|
|
2983
|
-
error: "Invalid issues array",
|
|
2984
|
-
hint: "Pass issues from detect_workflow_issues or analyze_workflow",
|
|
2985
|
-
example_input: [
|
|
2986
|
-
{ type: "missing_fallback", severity: "critical", node: "intent_classifier", reason: "..." },
|
|
2987
|
-
{ type: "type_mismatch", severity: "critical", source: "trigger.chat_conversation", target: "search.query", reason: "..." },
|
|
2988
|
-
],
|
|
2989
|
-
};
|
|
2990
|
-
}
|
|
2991
|
-
if (issues.length === 0) {
|
|
2992
|
-
return {
|
|
2993
|
-
message: "No issues to fix",
|
|
2994
|
-
fixes: [],
|
|
2995
|
-
};
|
|
2996
|
-
}
|
|
2997
|
-
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/*
|
|
2998
2886
|
return {
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
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
|
+
},
|
|
3008
2897
|
};
|
|
3009
2898
|
},
|
|
3010
2899
|
compare_workflow_versions: async (args) => {
|
|
2900
|
+
// DEPRECATED: MCP no longer pre-analyzes for comparison
|
|
2901
|
+
// LLM should compare workflows directly
|
|
3011
2902
|
const client = createClient(args.env);
|
|
3012
2903
|
const idBefore = String(args.persona_id_before);
|
|
3013
2904
|
const idAfter = String(args.persona_id_after);
|
|
@@ -3019,59 +2910,38 @@ const toolHandlers = {
|
|
|
3019
2910
|
throw new Error(`AI Employee not found (before): ${idBefore}`);
|
|
3020
2911
|
if (!personaAfter)
|
|
3021
2912
|
throw new Error(`AI Employee not found (after): ${idAfter}`);
|
|
3022
|
-
const
|
|
3023
|
-
|
|
3024
|
-
: null;
|
|
3025
|
-
const analysisAfter = personaAfter.workflow_def
|
|
3026
|
-
? analyzeWorkflow(personaAfter.workflow_def, { persona_id: idAfter, persona_name: personaAfter.name })
|
|
3027
|
-
: null;
|
|
2913
|
+
const nodesBefore = personaBefore.workflow_def ? parseWorkflowDef(personaBefore.workflow_def) : [];
|
|
2914
|
+
const nodesAfter = personaAfter.workflow_def ? parseWorkflowDef(personaAfter.workflow_def) : [];
|
|
3028
2915
|
// Compare fingerprints
|
|
3029
2916
|
const fpBefore = personaBefore.workflow_def ? fingerprintPersona(personaBefore) : null;
|
|
3030
2917
|
const fpAfter = personaAfter.workflow_def ? fingerprintPersona(personaAfter) : null;
|
|
3031
2918
|
return {
|
|
3032
2919
|
environment: client["env"].name,
|
|
2920
|
+
status: "DEPRECATED - LLM should compare",
|
|
3033
2921
|
before: {
|
|
3034
2922
|
persona_id: idBefore,
|
|
3035
2923
|
name: personaBefore.name,
|
|
3036
2924
|
fingerprint: fpBefore,
|
|
3037
2925
|
has_workflow: !!personaBefore.workflow_def,
|
|
3038
|
-
node_count:
|
|
3039
|
-
|
|
3040
|
-
critical_issues: analysisBefore?.issue_summary.critical ?? 0,
|
|
3041
|
-
validation_passed: analysisBefore?.validation_passed ?? false,
|
|
2926
|
+
node_count: nodesBefore.length,
|
|
2927
|
+
workflow_def: personaBefore.workflow_def,
|
|
3042
2928
|
},
|
|
3043
2929
|
after: {
|
|
3044
2930
|
persona_id: idAfter,
|
|
3045
2931
|
name: personaAfter.name,
|
|
3046
2932
|
fingerprint: fpAfter,
|
|
3047
2933
|
has_workflow: !!personaAfter.workflow_def,
|
|
3048
|
-
node_count:
|
|
3049
|
-
|
|
3050
|
-
critical_issues: analysisAfter?.issue_summary.critical ?? 0,
|
|
3051
|
-
validation_passed: analysisAfter?.validation_passed ?? false,
|
|
2934
|
+
node_count: nodesAfter.length,
|
|
2935
|
+
workflow_def: personaAfter.workflow_def,
|
|
3052
2936
|
},
|
|
3053
2937
|
comparison: {
|
|
3054
2938
|
fingerprints_match: fpBefore === fpAfter,
|
|
3055
|
-
node_count_change:
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
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.",
|
|
3060
2944
|
},
|
|
3061
|
-
issues_before: analysisBefore?.issues.slice(0, 5) ?? [],
|
|
3062
|
-
issues_after: analysisAfter?.issues.slice(0, 5) ?? [],
|
|
3063
|
-
recommendations: fpBefore === fpAfter
|
|
3064
|
-
? ["No workflow changes detected between versions"]
|
|
3065
|
-
: [
|
|
3066
|
-
analysisAfter?.validation_passed
|
|
3067
|
-
? "After version passes validation"
|
|
3068
|
-
: "After version has validation issues - review before deploying",
|
|
3069
|
-
(analysisAfter?.issue_summary.critical ?? 0) > (analysisBefore?.issue_summary.critical ?? 0)
|
|
3070
|
-
? "Warning: More critical issues in after version"
|
|
3071
|
-
: (analysisAfter?.issue_summary.critical ?? 0) < (analysisBefore?.issue_summary.critical ?? 0)
|
|
3072
|
-
? "Good: Fewer critical issues in after version"
|
|
3073
|
-
: "Same number of critical issues",
|
|
3074
|
-
],
|
|
3075
2945
|
};
|
|
3076
2946
|
},
|
|
3077
2947
|
get_workflow_metrics: async (args) => {
|
|
@@ -3088,54 +2958,39 @@ const toolHandlers = {
|
|
|
3088
2958
|
error: "AI Employee has no workflow_def",
|
|
3089
2959
|
};
|
|
3090
2960
|
}
|
|
3091
|
-
const
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
const avgEdgesPerNode = analysis.summary.total_nodes > 0
|
|
3098
|
-
? (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)
|
|
3099
2967
|
: 0;
|
|
3100
|
-
//
|
|
3101
|
-
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");
|
|
3102
2972
|
return {
|
|
3103
2973
|
environment: client["env"].name,
|
|
3104
2974
|
persona_id: personaId,
|
|
3105
2975
|
persona_name: persona.name,
|
|
3106
2976
|
structure: {
|
|
3107
|
-
total_nodes:
|
|
3108
|
-
total_edges:
|
|
3109
|
-
has_trigger:
|
|
3110
|
-
|
|
2977
|
+
total_nodes: nodes.length,
|
|
2978
|
+
total_edges: totalEdges,
|
|
2979
|
+
has_trigger: hasTrigger,
|
|
2980
|
+
connection_count: connections.length,
|
|
3111
2981
|
},
|
|
3112
2982
|
routing: {
|
|
3113
|
-
categorizers_count:
|
|
3114
|
-
hitl_nodes_count:
|
|
3115
|
-
has_parallel_branches:
|
|
3116
|
-
},
|
|
3117
|
-
quality: {
|
|
3118
|
-
validation_passed: analysis.validation_passed,
|
|
3119
|
-
critical_issues: analysis.issue_summary.critical,
|
|
3120
|
-
warnings: analysis.issue_summary.warning,
|
|
2983
|
+
categorizers_count: categorizerCount,
|
|
2984
|
+
hitl_nodes_count: hitlCount,
|
|
2985
|
+
has_parallel_branches: categorizerCount > 0,
|
|
3121
2986
|
},
|
|
3122
2987
|
complexity: {
|
|
3123
2988
|
avg_edges_per_node: avgEdgesPerNode,
|
|
3124
|
-
complexity_rating:
|
|
3125
|
-
:
|
|
2989
|
+
complexity_rating: nodes.length <= 5 ? "simple"
|
|
2990
|
+
: nodes.length <= 15 ? "moderate"
|
|
3126
2991
|
: "complex",
|
|
3127
2992
|
},
|
|
3128
|
-
|
|
3129
|
-
analysis.summary.categorizers_count === 0 && analysis.summary.total_nodes > 3
|
|
3130
|
-
? "Consider adding intent routing with chat_categorizer for better user experience"
|
|
3131
|
-
: null,
|
|
3132
|
-
analysis.summary.hitl_nodes_count === 0
|
|
3133
|
-
? "No HITL nodes - consider adding human approval for sensitive actions"
|
|
3134
|
-
: null,
|
|
3135
|
-
analysis.issue_summary.critical > 0
|
|
3136
|
-
? `${analysis.issue_summary.critical} critical issues need attention - use analyze_workflow for details`
|
|
3137
|
-
: null,
|
|
3138
|
-
].filter(Boolean),
|
|
2993
|
+
_note: "Use ema://rules/anti-patterns for quality analysis. MCP no longer pre-computes issues.",
|
|
3139
2994
|
};
|
|
3140
2995
|
},
|
|
3141
2996
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -3856,7 +3711,7 @@ const toolHandlers = {
|
|
|
3856
3711
|
return handleEnv({}, () => getAvailableEnvironments().map(e => ({
|
|
3857
3712
|
name: e.name,
|
|
3858
3713
|
isDefault: e.name === getDefaultEnvName(),
|
|
3859
|
-
})), { name: TOOLKIT_NAME, version: TOOLKIT_VERSION });
|
|
3714
|
+
})), { name: TOOLKIT_NAME, version: TOOLKIT_VERSION, commit: TOOLKIT_COMMIT });
|
|
3860
3715
|
},
|
|
3861
3716
|
persona: async (args) => {
|
|
3862
3717
|
const targetEnv = args.env ?? getDefaultEnvName();
|
|
@@ -4104,7 +3959,11 @@ const toolHandlers = {
|
|
|
4104
3959
|
name: e.name,
|
|
4105
3960
|
isDefault: e.name === getDefaultEnvName(),
|
|
4106
3961
|
})),
|
|
4107
|
-
toolkit: {
|
|
3962
|
+
toolkit: {
|
|
3963
|
+
name: TOOLKIT_NAME,
|
|
3964
|
+
version: TOOLKIT_VERSION,
|
|
3965
|
+
commit: TOOLKIT_COMMIT,
|
|
3966
|
+
},
|
|
4108
3967
|
client,
|
|
4109
3968
|
});
|
|
4110
3969
|
},
|
|
@@ -4129,7 +3988,8 @@ const toolHandlers = {
|
|
|
4129
3988
|
}
|
|
4130
3989
|
switch (catalogType) {
|
|
4131
3990
|
case "actions": {
|
|
4132
|
-
//
|
|
3991
|
+
// API-FIRST: Try API, fall back to static AGENT_CATALOG
|
|
3992
|
+
// Senior review: Use API as primary source for live data
|
|
4133
3993
|
if (method === "recommend" && forUseCase) {
|
|
4134
3994
|
const suggestions = suggestAgentsForUseCase(forUseCase);
|
|
4135
3995
|
return {
|
|
@@ -4141,18 +4001,55 @@ const toolHandlers = {
|
|
|
4141
4001
|
: "No specific recommendations. Try browsing with catalog(method=\"list\", type=\"actions\").",
|
|
4142
4002
|
};
|
|
4143
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
|
|
4144
4041
|
if (id) {
|
|
4145
4042
|
const action = getAgentByName(id);
|
|
4146
4043
|
if (!action) {
|
|
4147
4044
|
return { error: `Action not found: ${id}` };
|
|
4148
4045
|
}
|
|
4149
|
-
return { type: "actions", action };
|
|
4046
|
+
return { type: "actions", action, source: "catalog" };
|
|
4150
4047
|
}
|
|
4151
4048
|
if (category) {
|
|
4152
4049
|
const actions = getAgentsByCategory(category);
|
|
4153
|
-
return { type: "actions", category, count: actions.length, actions };
|
|
4050
|
+
return { type: "actions", category, count: actions.length, actions, source: "catalog" };
|
|
4154
4051
|
}
|
|
4155
|
-
// List all
|
|
4052
|
+
// List all from catalog
|
|
4156
4053
|
let actions = Object.values(AGENT_CATALOG);
|
|
4157
4054
|
if (query) {
|
|
4158
4055
|
const q = query.toLowerCase();
|
|
@@ -4164,6 +4061,7 @@ const toolHandlers = {
|
|
|
4164
4061
|
type: "actions",
|
|
4165
4062
|
count: actions.length,
|
|
4166
4063
|
actions: actions.map(a => ({ name: a.actionName, displayName: a.displayName, category: a.category, description: a.description })),
|
|
4064
|
+
source: "catalog",
|
|
4167
4065
|
_tip: "Get details: catalog(method=\"get\", type=\"actions\", id=\"<name>\")",
|
|
4168
4066
|
_next_step: actions.length > 0 ? `catalog(method="get", type="actions", id="${actions[0].actionName}")` : undefined,
|
|
4169
4067
|
};
|
|
@@ -4316,110 +4214,27 @@ const legacyConsolidateDemoData = toolHandlers.consolidate_demo_data;
|
|
|
4316
4214
|
const legacyGenerateDemoDocument = toolHandlers.generate_demo_document;
|
|
4317
4215
|
const legacyValidateDemoDocument = toolHandlers.validate_demo_document;
|
|
4318
4216
|
const legacyGetDemoDataTemplate = toolHandlers.get_demo_data_template;
|
|
4319
|
-
//
|
|
4217
|
+
// Workflow tool: MCP provides data (get) and executes (deploy). LLM does all thinking.
|
|
4320
4218
|
toolHandlers.workflow = async (args) => {
|
|
4321
|
-
// Normalize persona type alias: tool schema uses "type", internal uses "persona_type"
|
|
4322
4219
|
const normalizedArgs = { ...(args ?? {}) };
|
|
4323
|
-
if (normalizedArgs.persona_type === undefined && normalizedArgs.type !== undefined) {
|
|
4324
|
-
normalizedArgs.persona_type = normalizedArgs.type;
|
|
4325
|
-
}
|
|
4326
4220
|
const personaId = normalizedArgs.persona_id ? String(normalizedArgs.persona_id) : undefined;
|
|
4327
4221
|
const workflowDef = normalizedArgs.workflow_def;
|
|
4328
|
-
const
|
|
4329
|
-
//
|
|
4330
|
-
const
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
(personaId && workflowDef ? "deploy" : // Direct deployment (no input required)
|
|
4340
|
-
personaId && inputProvided ? "modify" : // BROWNFIELD: existing persona + new requirements
|
|
4341
|
-
workflowDef || personaId ? "analyze" : // Inspect existing
|
|
4342
|
-
"generate"); // GREENFIELD: new workflow
|
|
4343
|
-
switch (effectiveMode) {
|
|
4344
|
-
case "generate": {
|
|
4345
|
-
const result = await legacyWorkflowTool({ ...normalizedArgs, mode: "generate" });
|
|
4346
|
-
// Ensure next_steps point at consolidated tools by default
|
|
4347
|
-
if (result && typeof result === "object") {
|
|
4348
|
-
const obj = result;
|
|
4349
|
-
const persona_id = obj.persona_id ? String(obj.persona_id) : personaId;
|
|
4350
|
-
const deployed = obj.deployed === true;
|
|
4351
|
-
obj.next_steps = deployed
|
|
4352
|
-
? persona_id
|
|
4353
|
-
? [`Verify: persona(id="${persona_id}", include_workflow=true)`]
|
|
4354
|
-
: ["Verify the deployed AI Employee in the Ema UI"]
|
|
4355
|
-
: persona_id
|
|
4356
|
-
? [
|
|
4357
|
-
`Deploy: workflow(mode="deploy", persona_id="${persona_id}", workflow_def=<workflow_def>, proto_config=<proto_config>)`,
|
|
4358
|
-
`Verify: persona(id="${persona_id}", include_workflow=true)`,
|
|
4359
|
-
]
|
|
4360
|
-
: [
|
|
4361
|
-
"Create an AI Employee in Ema, then deploy:",
|
|
4362
|
-
`workflow(mode="deploy", persona_id="<persona_id>", workflow_def=<workflow_def>, proto_config=<proto_config>)`,
|
|
4363
|
-
];
|
|
4364
|
-
}
|
|
4365
|
-
return result;
|
|
4366
|
-
}
|
|
4367
|
-
case "analyze": {
|
|
4368
|
-
const include = Array.isArray(normalizedArgs.include)
|
|
4369
|
-
? normalizedArgs.include.map(String)
|
|
4370
|
-
: ["issues", "connections", "fixes", "metrics"];
|
|
4371
|
-
let wf = workflowDef;
|
|
4372
|
-
let meta;
|
|
4373
|
-
if (!wf && personaId) {
|
|
4374
|
-
const client = createClient(normalizedArgs.env);
|
|
4375
|
-
const persona = await client.getPersonaById(personaId);
|
|
4376
|
-
if (!persona)
|
|
4377
|
-
throw new Error(`AI Employee not found: ${personaId}`);
|
|
4378
|
-
wf = persona.workflow_def;
|
|
4379
|
-
meta = {
|
|
4380
|
-
persona_id: personaId,
|
|
4381
|
-
persona_name: persona.name,
|
|
4382
|
-
environment: client["env"].name,
|
|
4383
|
-
};
|
|
4384
|
-
}
|
|
4385
|
-
if (!wf) {
|
|
4386
|
-
return {
|
|
4387
|
-
error: "Missing workflow to analyze. Provide persona_id or workflow_def.",
|
|
4388
|
-
hint: 'Examples: workflow(mode="analyze", persona_id="...") or workflow(mode="analyze", workflow_def={...})',
|
|
4389
|
-
};
|
|
4390
|
-
}
|
|
4391
|
-
const out = { mode: "analyze", ...(meta ?? {}) };
|
|
4392
|
-
// Issues + summary (source of truth)
|
|
4393
|
-
const issuesResult = await legacyDetectWorkflowIssues({ workflow_def: wf });
|
|
4394
|
-
const issues = issuesResult.issues;
|
|
4395
|
-
if (include.includes("issues")) {
|
|
4396
|
-
out.issues = issuesResult.issues;
|
|
4397
|
-
out.issue_summary = issuesResult.summary;
|
|
4398
|
-
out.validation_passed = issuesResult.validation_passed;
|
|
4399
|
-
}
|
|
4400
|
-
if (include.includes("connections")) {
|
|
4401
|
-
out.connections = await legacyValidateWorkflowConnections({ workflow_def: wf });
|
|
4402
|
-
}
|
|
4403
|
-
if (include.includes("fixes")) {
|
|
4404
|
-
out.fixes = Array.isArray(issues) ? await legacySuggestWorkflowFixes({ issues }) : { error: "No issues array available to generate fixes" };
|
|
4405
|
-
}
|
|
4406
|
-
if (include.includes("metrics")) {
|
|
4407
|
-
const actions = wf.actions ?? [];
|
|
4408
|
-
const edges = wf.edges ?? [];
|
|
4409
|
-
out.metrics = {
|
|
4410
|
-
node_count: Array.isArray(actions) ? actions.length : 0,
|
|
4411
|
-
edge_count: Array.isArray(edges) ? edges.length : 0,
|
|
4412
|
-
};
|
|
4413
|
-
}
|
|
4414
|
-
return out;
|
|
4222
|
+
const mode = normalizedArgs.mode ? String(normalizedArgs.mode) : undefined;
|
|
4223
|
+
// Route to handleWorkflow for get/deploy (the only public modes)
|
|
4224
|
+
const client = createClient(normalizedArgs.env);
|
|
4225
|
+
switch (mode) {
|
|
4226
|
+
case "get": {
|
|
4227
|
+
// Return workflow data for LLM to analyze/modify
|
|
4228
|
+
return handleWorkflow({
|
|
4229
|
+
mode: "get",
|
|
4230
|
+
persona_id: personaId,
|
|
4231
|
+
env: normalizedArgs.env,
|
|
4232
|
+
}, client, () => undefined);
|
|
4415
4233
|
}
|
|
4416
4234
|
case "deploy": {
|
|
4417
4235
|
if (!personaId) {
|
|
4418
|
-
|
|
4236
|
+
return { error: 'persona_id is required for workflow(mode="deploy")' };
|
|
4419
4237
|
}
|
|
4420
|
-
// Use clean deploy path via handleWorkflow (no brownfield merge, no excessive validation)
|
|
4421
|
-
// User can still request validation via validate=true, but default is now direct deploy
|
|
4422
|
-
const client = createClient(normalizedArgs.env);
|
|
4423
4238
|
// Optional: pre-deploy version snapshot
|
|
4424
4239
|
const targetEnv = normalizedArgs.env ?? getDefaultEnvName();
|
|
4425
4240
|
let versionCreated;
|
|
@@ -4445,8 +4260,7 @@ toolHandlers.workflow = async (args) => {
|
|
|
4445
4260
|
catch {
|
|
4446
4261
|
// Version tracking is best-effort
|
|
4447
4262
|
}
|
|
4448
|
-
// Route to
|
|
4449
|
-
// This path: sanitizes workflow → calls client.updateAiEmployee → done
|
|
4263
|
+
// Route to handleWorkflow deploy
|
|
4450
4264
|
const deployResult = await handleWorkflow({
|
|
4451
4265
|
mode: "deploy",
|
|
4452
4266
|
persona_id: personaId,
|
|
@@ -4460,58 +4274,25 @@ toolHandlers.workflow = async (args) => {
|
|
|
4460
4274
|
}
|
|
4461
4275
|
return deployResult;
|
|
4462
4276
|
}
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
return legacyOptimizeWorkflow({
|
|
4475
|
-
identifier: idOrIdentifier ?? personaId,
|
|
4476
|
-
persona_id: personaId,
|
|
4477
|
-
prompt,
|
|
4478
|
-
type: normalizedArgs.persona_type,
|
|
4479
|
-
preview,
|
|
4480
|
-
env: normalizedArgs.env,
|
|
4481
|
-
});
|
|
4482
|
-
}
|
|
4483
|
-
case "compare": {
|
|
4484
|
-
if (!personaId) {
|
|
4485
|
-
throw new Error('persona_id is required for workflow(mode="compare")');
|
|
4486
|
-
}
|
|
4487
|
-
const compareTo = normalizedArgs.compare_to ? String(normalizedArgs.compare_to) : undefined;
|
|
4488
|
-
if (!compareTo) {
|
|
4489
|
-
throw new Error('compare_to is required for workflow(mode="compare")');
|
|
4490
|
-
}
|
|
4491
|
-
return legacyCompareWorkflowVersions({
|
|
4492
|
-
persona_id_before: personaId,
|
|
4493
|
-
persona_id_after: compareTo,
|
|
4494
|
-
env: normalizedArgs.env,
|
|
4495
|
-
});
|
|
4496
|
-
}
|
|
4497
|
-
case "compile": {
|
|
4498
|
-
// Compile requires node specs + result mappings.
|
|
4499
|
-
if (!normalizedArgs.name || !normalizedArgs.description) {
|
|
4500
|
-
throw new Error('workflow(mode="compile") requires name and description');
|
|
4501
|
-
}
|
|
4502
|
-
if (!normalizedArgs.persona_type) {
|
|
4503
|
-
throw new Error('workflow(mode="compile") requires persona type via type="voice|chat|dashboard"');
|
|
4504
|
-
}
|
|
4505
|
-
return legacyCompileWorkflow({
|
|
4506
|
-
name: normalizedArgs.name,
|
|
4507
|
-
description: normalizedArgs.description,
|
|
4508
|
-
persona_type: normalizedArgs.persona_type,
|
|
4509
|
-
nodes: normalizedArgs.nodes,
|
|
4510
|
-
result_mappings: normalizedArgs.result_mappings,
|
|
4511
|
-
});
|
|
4277
|
+
// REMOVED modes - LLM does these
|
|
4278
|
+
case "analyze":
|
|
4279
|
+
case "compare":
|
|
4280
|
+
case "compile":
|
|
4281
|
+
case "optimize":
|
|
4282
|
+
case "generate": {
|
|
4283
|
+
return {
|
|
4284
|
+
error: `Mode "${mode}" removed - LLM does this thinking`,
|
|
4285
|
+
hint: "Use workflow(mode='get') to fetch data, then analyze/generate in your reasoning. Deploy with workflow(mode='deploy').",
|
|
4286
|
+
valid_modes: ["get", "deploy"],
|
|
4287
|
+
};
|
|
4512
4288
|
}
|
|
4513
4289
|
default: {
|
|
4514
|
-
|
|
4290
|
+
// No mode or unknown mode - require explicit mode
|
|
4291
|
+
return {
|
|
4292
|
+
error: `Mode required. Valid modes: get, deploy`,
|
|
4293
|
+
hint: "workflow(mode='get') returns data for LLM. workflow(mode='deploy') executes LLM's workflow_def.",
|
|
4294
|
+
example: `workflow(mode="get", persona_id="...")`,
|
|
4295
|
+
};
|
|
4515
4296
|
}
|
|
4516
4297
|
}
|
|
4517
4298
|
};
|
|
@@ -5017,7 +4798,11 @@ export async function startMcpServer() {
|
|
|
5017
4798
|
});
|
|
5018
4799
|
const transport = new StdioServerTransport();
|
|
5019
4800
|
await server.connect(transport);
|
|
5020
|
-
|
|
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}`);
|
|
5021
4806
|
}
|
|
5022
4807
|
startMcpServer().catch((error) => {
|
|
5023
4808
|
console.error("Failed to start MCP server:", error);
|