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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @ema.co/mcp-toolkit might be problematic. Click here for more details.

Files changed (44) hide show
  1. package/dist/mcp/handlers/action/index.js +17 -20
  2. package/dist/mcp/handlers/data/index.js +72 -6
  3. package/dist/mcp/handlers/deprecation.js +50 -0
  4. package/dist/mcp/handlers/env/index.js +3 -3
  5. package/dist/mcp/handlers/knowledge/index.js +44 -237
  6. package/dist/mcp/handlers/persona/create.js +47 -18
  7. package/dist/mcp/handlers/persona/index.js +9 -10
  8. package/dist/mcp/handlers/persona/update.js +4 -2
  9. package/dist/mcp/handlers/reference/index.js +15 -2
  10. package/dist/mcp/handlers/sync/index.js +3 -18
  11. package/dist/mcp/handlers/workflow/analyze.js +53 -105
  12. package/dist/mcp/handlers/workflow/deploy.js +129 -0
  13. package/dist/mcp/handlers/workflow/generate.js +8 -28
  14. package/dist/mcp/handlers/workflow/index.js +258 -85
  15. package/dist/mcp/handlers/workflow/modify.js +9 -29
  16. package/dist/mcp/handlers/workflow/optimize.js +22 -108
  17. package/dist/mcp/handlers/workflow/utils.js +0 -102
  18. package/dist/mcp/handlers-consolidated.js +15 -38
  19. package/dist/mcp/prompts.js +82 -44
  20. package/dist/mcp/resources.js +335 -3
  21. package/dist/mcp/server.js +242 -457
  22. package/dist/mcp/tools.js +44 -61
  23. package/dist/sdk/action-schema-parser.js +11 -5
  24. package/dist/sdk/client.js +46 -17
  25. package/dist/sdk/ema-client.js +11 -0
  26. package/dist/sdk/generated/deprecated-actions.js +171 -0
  27. package/dist/sdk/guidance.js +58 -35
  28. package/dist/sdk/index.js +8 -7
  29. package/dist/sdk/knowledge.js +216 -1932
  30. package/dist/sdk/quality-gates.js +60 -336
  31. package/dist/sdk/validation-rules.js +33 -0
  32. package/dist/sdk/workflow-fixer.js +29 -360
  33. package/dist/sdk/workflow-intent.js +43 -3
  34. package/dist/sdk/workflow-transformer.js +0 -342
  35. package/docs/dashboard-operations.md +35 -0
  36. package/docs/ema-user-guide.md +66 -0
  37. package/docs/mcp-tools-guide.md +74 -45
  38. package/package.json +2 -2
  39. package/dist/mcp/handlers/persona/analyze.js +0 -275
  40. package/dist/mcp/handlers/persona/compare.js +0 -32
  41. package/dist/mcp/handlers/workflow/compile.js +0 -39
  42. package/docs/DEBUG-ANALYSIS-unused-category-type-mismatch.md +0 -481
  43. package/docs/TODO-fix-analyzer-and-modify.md +0 -182
  44. package/resources/action-schema.json +0 -5678
package/dist/mcp/tools.js CHANGED
@@ -76,8 +76,8 @@ ${LIMITATIONS.map(l => `- ${l}`).join("\n")}
76
76
  ## Data Operations (sub-resource, requires id)
77
77
  - \`persona(id="abc", data={method:"list"})\` - list data items
78
78
  - \`persona(id="abc", data={method:"stats"})\` - get file counts by status (total, pending, success, failed)
79
- - \`persona(id="abc", data={method:"upload", path:"/file.pdf"})\` - upload file
80
- - \`persona(id="abc", data={method:"upload", items:[{...}]})\` - upload LLM-generated content
79
+ - \`persona(id="abc", data={method:"upload", path:"/file.pdf"})\` - upload file to knowledge base
80
+ - \`persona(id="abc", data={method:"upload", items:[{...}]})\` - create dashboard rows with data
81
81
  - \`persona(id="abc", data={method:"copy", from:"source-id"})\` - copy from another persona
82
82
  - \`persona(id="abc", data={method:"copy", from:"src", sanitize:true})\` - copy with PII masking
83
83
  - \`persona(id="abc", data={method:"replicate", from:"source-id"})\` - replicate by reference (no copy, fast)
@@ -88,7 +88,20 @@ ${LIMITATIONS.map(l => `- ${l}`).join("\n")}
88
88
  - \`persona(id="abc", data={method:"regenerate", document_id:"doc-123", project_id:"proj-456", selection:"intro", query:"make it shorter"})\` - modify document section
89
89
  - \`persona(id="abc", data={method:"replace", document_id:"doc-123", project_id:"proj-456", content:"new content"})\` - replace entire document content
90
90
 
91
- Note: For documents, \`replace\` is full content replacement (no partial update). For dashboard fixed inputs, use upload with existing row context.
91
+ ### Dashboard Row Upload with Files
92
+ Create dashboard rows with file attachments (triggers workflow execution):
93
+ \`\`\`
94
+ persona(id="abc", data={
95
+ method: "upload",
96
+ items: [{
97
+ "Input Document": { file: "/path/to/invoice.pdf" },
98
+ "Customer Name": "Acme Corp"
99
+ }]
100
+ })
101
+ \`\`\`
102
+ Item values support: strings, numbers, booleans, file attachments ({ file: "path" }), or inline documents ({ contents: "...", mime_type: "..." }).
103
+
104
+ Note: \`path\` uploads to knowledge base (Chat personas). \`items\` creates dashboard rows (Dashboard personas).
92
105
 
93
106
  ## Action Composition (post-operation actions)
94
107
  Use \`actions\` array for multi-step operations instead of flag parameters.
@@ -127,8 +140,8 @@ persona(
127
140
  // === EXPLICIT METHOD (required) ===
128
141
  method: {
129
142
  type: "string",
130
- enum: ["list", "get", "create", "update", "delete", "analyze", "sanitize", "schema", "snapshot", "history", "restore", "compare"],
131
- description: "Operation to perform (required unless using data sub-resource)"
143
+ enum: ["list", "get", "create", "update", "delete", "sanitize", "schema", "snapshot", "history", "restore"],
144
+ description: "Operation to perform. LLM does analysis/comparison - use 'get' to retrieve data, then reason about it."
132
145
  },
133
146
  // === Identity ===
134
147
  id: {
@@ -197,11 +210,6 @@ persona(
197
210
  type: "string",
198
211
  description: "Version to restore, e.g. 'v3' (for method=restore)"
199
212
  },
200
- // === Compare params (for method=compare) ===
201
- to: {
202
- type: "string",
203
- description: "Persona ID or snapshot to compare to (for method=compare)"
204
- },
205
213
  // === Data sub-resource ===
206
214
  data: {
207
215
  type: "object",
@@ -229,11 +237,17 @@ persona(
229
237
  },
230
238
  wait: { type: "boolean", description: "Wait for replication to complete (for method=replicate, default: true)" },
231
239
  // upload params
232
- path: { type: "string", description: "File path to upload (for method=upload)" },
233
- content: {
240
+ path: { type: "string", description: "File path to upload to knowledge base (for method=upload)" },
241
+ items: {
234
242
  type: "array",
235
243
  items: { type: "object" },
236
- description: "Content array - LLM-generated (for method=upload)"
244
+ description: "Dashboard rows to create. Each object maps column names to values. Values can be: strings, numbers, booleans, file attachments { file: '/path' }, or inline docs { contents: '...', mime_type: '...' }"
245
+ },
246
+ // widget_name - for targeting specific widgets in upload or filtering stats
247
+ // Especially important for Document Generation personas with multiple upload widgets
248
+ widget_name: {
249
+ type: "string",
250
+ description: "Target widget for upload OR filter for stats. For Document Proposal Manager: 'upload' (Content Repository), 'upload1' (Service Line Docs), 'upload2' (Style Guide). Default: 'fileUpload'. See catalog(type='widgets') for reference."
237
251
  },
238
252
  // delete params
239
253
  file_id: { type: "string", description: "File/item ID to delete (for method=delete)" },
@@ -241,8 +255,6 @@ persona(
241
255
  enabled: { type: "boolean", description: "Enable/disable embeddings (for method=embed)" },
242
256
  // search params
243
257
  query: { type: "string", description: "Search query (for method=search)" },
244
- // stats params
245
- widget_name: { type: "string", description: "Filter by widget name (for method=stats)" },
246
258
  },
247
259
  required: ["method"],
248
260
  },
@@ -359,76 +371,47 @@ persona(
359
371
  },
360
372
  },
361
373
  // ═══════════════════════════════════════════════════════════════════════════
362
- // 3. WORKFLOW - Complex workflow generation (separate from persona modifications)
374
+ // 3. WORKFLOW - Data retrieval and deployment ONLY (LLM does all thinking)
363
375
  // ═══════════════════════════════════════════════════════════════════════════
364
376
  {
365
377
  name: "workflow",
366
- description: `Generate, analyze, or compile complex multi-node workflows.
367
-
368
- Use this for COMPLEX workflows (categorizers, multi-path routing, entity extraction chains).
369
- For SIMPLE modifications to existing personas, use persona(method="update", input="...") instead.
378
+ description: `Get workflow data or deploy LLM-generated workflows.
370
379
 
371
- ## Generate (from requirements)
372
- - \`workflow(mode="generate", input="Voice AI for support with routing")\` - new workflow
373
- - \`workflow(mode="generate", input="...", persona_id="abc")\` - with widget context from existing persona
374
- - Returns \`llm_prompt\` for complex workflows - includes available widgets if persona_id provided
375
- - Returns \`available_widgets\` array showing what widget names to use in bindings
380
+ **MCP provides data. LLM does the thinking.**
376
381
 
377
- ## Analyze (existing workflow)
378
- - \`workflow(mode="analyze", persona_id="abc")\` - detect issues, get WorkflowSpec
382
+ ## Get (return data for LLM to work with)
383
+ - \`workflow(mode="get", persona_id="abc")\` - returns workflow_def, schema, patterns, widgets
384
+ - LLM analyzes, compares, and generates workflows using this data
379
385
 
380
- ## Compile (spec to def)
381
- - \`workflow(mode="compile", workflow_spec={...})\` - convert WorkflowSpec to workflow_def
386
+ ## Deploy (execute LLM's result)
387
+ - \`workflow(mode="deploy", persona_id="abc", workflow_def={...})\` - deploy LLM-generated workflow
382
388
 
383
- ## Deploy (to persona)
384
- - \`workflow(mode="deploy", persona_id="abc", workflow_def={...})\` - apply workflow to persona
385
-
386
- **Workflow for complex creation:**
387
- 1. \`workflow(mode="generate", input="...", persona_id="abc")\` → get llm_prompt + available_widgets
388
- 2. Use LLM to generate WorkflowSpec (use widget names from available_widgets)
389
- 3. \`workflow(mode="compile", workflow_spec={...})\` → get workflow_def
390
- 4. \`persona(method="update", id="abc", workflow_def={...})\` → deploy
391
-
392
- **IMPORTANT**: Workflows reference widgets by name (widgetName). Pass persona_id to get available widget names.`,
389
+ **Workflow for creation:**
390
+ 1. \`workflow(mode="get", persona_id="abc")\` get schema, patterns, current workflow
391
+ 2. LLM generates workflow_def (LLM does the work)
392
+ 3. \`workflow(mode="deploy", persona_id="abc", workflow_def={...})\` → MCP deploys`,
393
393
  inputSchema: {
394
394
  type: "object",
395
395
  properties: {
396
396
  mode: {
397
397
  type: "string",
398
- enum: ["generate", "analyze", "compile", "deploy", "optimize"],
399
- description: "Operation to perform",
400
- },
401
- input: {
402
- type: "string",
403
- description: "Natural language requirements (for mode=generate)",
398
+ enum: ["get", "deploy"],
399
+ description: "get = return data for LLM, deploy = execute LLM's workflow_def",
404
400
  },
405
401
  persona_id: {
406
402
  type: "string",
407
- description: "Persona ID (for mode=analyze, deploy, optimize)",
408
- },
409
- workflow_spec: {
410
- type: "object",
411
- description: "WorkflowSpec object (for mode=compile)",
403
+ description: "Persona ID (required)",
412
404
  },
413
405
  workflow_def: {
414
406
  type: "object",
415
- description: "workflow_def JSON (for mode=deploy)",
416
- },
417
- type: {
418
- type: "string",
419
- enum: ["voice", "chat", "dashboard"],
420
- description: "Persona type (for mode=generate)",
421
- },
422
- preview: {
423
- type: "boolean",
424
- description: "Preview without deploying (default: true)",
407
+ description: "workflow_def JSON (for mode=deploy) - LLM generates this",
425
408
  },
426
409
  env: {
427
410
  type: "string",
428
411
  description: envDescription,
429
412
  },
430
413
  },
431
- required: ["mode"],
414
+ required: ["mode", "persona_id"],
432
415
  },
433
416
  },
434
417
  // ═══════════════════════════════════════════════════════════════════════════
@@ -343,10 +343,16 @@ export function generateSchemaBundle(config) {
343
343
  // Type Compatibility Matrix
344
344
  // ============================================================================
345
345
  /**
346
- * Type compatibility rules for workflow validation
346
+ * Schema validation type compatibility - maps well-known types to compatible input names.
347
+ * Used by isTypeCompatible() for workflow validation.
348
+ *
349
+ * NOTE: This is SEPARATE from knowledge.ts TYPE_COMPATIBILITY which is user-facing documentation.
350
+ * This focuses on input NAME matching for schema validation.
351
+ *
352
+ * Canonical type documentation: src/sdk/knowledge.ts → TYPE_COMPATIBILITY
347
353
  */
348
- export const TYPE_COMPATIBILITY = {
349
- // What inputs can accept each type
354
+ export const SCHEMA_TYPE_COMPATIBILITY = {
355
+ // What input names can accept each type
350
356
  WELL_KNOWN_TYPE_CHAT_CONVERSATION: ["conversation", "chat_conversation"],
351
357
  WELL_KNOWN_TYPE_TEXT_WITH_SOURCES: ["query", "text", "user_query", "input"],
352
358
  WELL_KNOWN_TYPE_SEARCH_RESULT: ["search_results", "results"],
@@ -368,8 +374,8 @@ export function isTypeCompatible(sourceType, targetType, targetInputName) {
368
374
  if (targetInputName?.startsWith("named_inputs"))
369
375
  return true;
370
376
  // Check explicit compatibility
371
- if (sourceType && TYPE_COMPATIBILITY[sourceType]) {
372
- const compatible = TYPE_COMPATIBILITY[sourceType];
377
+ if (sourceType && SCHEMA_TYPE_COMPATIBILITY[sourceType]) {
378
+ const compatible = SCHEMA_TYPE_COMPATIBILITY[sourceType];
373
379
  if (compatible.includes("*"))
374
380
  return true;
375
381
  if (targetInputName && compatible.some((c) => targetInputName.includes(c)))
@@ -17,7 +17,7 @@
17
17
  * const client = new EmaClientV2(env);
18
18
  * ```
19
19
  *
20
- * See docs/lessons-learned.md for migration guidance.
20
+ * See .ctx/docs/lessons-learned.md for migration guidance.
21
21
  */
22
22
  import { GrpcClient } from "./grpc-client.js";
23
23
  import { toJson } from "@bufbuild/protobuf";
@@ -393,27 +393,43 @@ export class EmaClient {
393
393
  const existingPersona = await this.getPersonaById(req.persona_id);
394
394
  const existingWorkflow = existingPersona?.workflow_def;
395
395
  const existingWfName = existingWorkflow?.workflowName;
396
+ // Deep clone workflow for modifications
397
+ const fixedWorkflow = JSON.parse(JSON.stringify(req.workflow));
396
398
  if (existingWfName?.name) {
397
- // Deep clone workflow and fix the namespace
398
- const fixedWorkflow = JSON.parse(JSON.stringify(req.workflow));
399
+ // Copy namespace from existing workflow
399
400
  fixedWorkflow.workflowName = existingWfName;
400
- // Also fix results format if needed - API expects "<actionName>.<outputName>" keys
401
- const results = fixedWorkflow.results;
402
- if (results) {
403
- const fixedResults = {};
404
- for (const [key, value] of Object.entries(results)) {
405
- if (value.actionName && value.outputName) {
406
- const correctKey = `${value.actionName}.${value.outputName}`;
407
- fixedResults[correctKey] = value;
408
- }
409
- }
410
- fixedWorkflow.results = fixedResults;
401
+ if (opts?.verbose || process.env.EMA_DEBUG) {
402
+ console.error("[EmaClient] Copied workflow namespace from existing:", existingWfName.name);
411
403
  }
412
- finalReq = { ...req, workflow: fixedWorkflow };
404
+ }
405
+ else {
406
+ // CRITICAL FIX: Generate a valid namespace for personas without existing workflows.
407
+ // The API requires workflowName to match the persona's namespace format.
408
+ // Format: ["ema", "personas", "<persona_id>"] with name "workflow"
409
+ const generatedWfName = {
410
+ name: {
411
+ namespaces: ["ema", "personas", req.persona_id],
412
+ name: "workflow",
413
+ },
414
+ };
415
+ fixedWorkflow.workflowName = generatedWfName;
413
416
  if (opts?.verbose || process.env.EMA_DEBUG) {
414
- console.error("[EmaClient] Fixed workflow namespace:", existingWfName.name);
417
+ console.error("[EmaClient] Generated workflow namespace for new persona:", generatedWfName.name);
415
418
  }
416
419
  }
420
+ // Also fix results format if needed - API expects "<actionName>.<outputName>" keys
421
+ const results = fixedWorkflow.results;
422
+ if (results) {
423
+ const fixedResults = {};
424
+ for (const [key, value] of Object.entries(results)) {
425
+ if (value.actionName && value.outputName) {
426
+ const correctKey = `${value.actionName}.${value.outputName}`;
427
+ fixedResults[correctKey] = value;
428
+ }
429
+ }
430
+ fixedWorkflow.results = fixedResults;
431
+ }
432
+ finalReq = { ...req, workflow: fixedWorkflow };
417
433
  }
418
434
  // Debug logging for troubleshooting
419
435
  if (opts?.verbose || process.env.EMA_DEBUG) {
@@ -534,10 +550,23 @@ export class EmaClient {
534
550
  });
535
551
  if (!resp.ok) {
536
552
  const body = await resp.text();
553
+ // Try to extract error details from response
554
+ let errorDetail = "";
555
+ try {
556
+ const parsed = JSON.parse(body);
557
+ errorDetail = parsed.error || parsed.message || parsed.detail || "";
558
+ }
559
+ catch {
560
+ errorDetail = body.slice(0, 200);
561
+ }
562
+ // Build informative error message
563
+ const message = errorDetail
564
+ ? `create_ai_employee failed (${this.env.name}): ${errorDetail}`
565
+ : `create_ai_employee failed (${this.env.name}) - status ${resp.status}`;
537
566
  throw new EmaApiError({
538
567
  statusCode: resp.status,
539
568
  body,
540
- message: `create_ai_employee failed (${this.env.name})`,
569
+ message,
541
570
  });
542
571
  }
543
572
  return (await resp.json());
@@ -166,6 +166,17 @@ export class EmaClientV2 {
166
166
  // Copy namespace from existing workflow
167
167
  workflow = { ...workflow, workflowName: existingWfName };
168
168
  }
169
+ else {
170
+ // Generate a valid namespace for personas without existing workflows
171
+ // Format: ["ema", "personas", "<persona_id>"] with name "workflow"
172
+ const generatedWfName = {
173
+ name: {
174
+ namespaces: ["ema", "personas", personaId],
175
+ name: "workflow",
176
+ },
177
+ };
178
+ workflow = { ...workflow, workflowName: generatedWfName };
179
+ }
169
180
  }
170
181
  const result = await api.updatePersona({
171
182
  client: this.restClient,
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Auto-generated deprecated actions list from ema repository.
3
+ *
4
+ * PRIMARY SOURCE: API via client.listActions() where deprecated === true
5
+ * This file is FALLBACK only when API is unavailable.
6
+ *
7
+ * Generated at: 2026-01-26T00:00:00.000Z
8
+ * Source: manual (pending first GH Actions sync)
9
+ *
10
+ * DO NOT EDIT MANUALLY - regenerate with: npm run generate:deprecated-actions
11
+ */
12
+ /**
13
+ * Deprecated actions with known replacements.
14
+ * Use the replacement action instead.
15
+ */
16
+ export const DEPRECATED_ACTIONS_WITH_REPLACEMENT = [
17
+ {
18
+ action: "search",
19
+ version: "v0",
20
+ replacement: "search",
21
+ replacementVersion: "v2",
22
+ migrationNotes: "v2 requires datastore_configs input (mandatory)",
23
+ source: "registered_actions.py",
24
+ },
25
+ {
26
+ action: "web_search",
27
+ version: "v0",
28
+ replacement: "live_web_search or ai_web_search",
29
+ migrationNotes: "Split into two specialized actions",
30
+ source: "registered_actions.py",
31
+ },
32
+ {
33
+ action: "call_llm",
34
+ version: "v0",
35
+ replacement: "call_llm",
36
+ replacementVersion: "v2",
37
+ migrationNotes: "v2 uses meta_respond_v2",
38
+ source: "registered_actions.py",
39
+ },
40
+ {
41
+ action: "combine_external_action_and_search_results",
42
+ version: "v0",
43
+ replacement: "respond_for_external_actions",
44
+ migrationNotes: "Migration in progress",
45
+ source: "registered_actions.py",
46
+ },
47
+ {
48
+ action: "human_collaboration",
49
+ version: "v0",
50
+ replacement: "general_hitl",
51
+ migrationNotes: "More flexible HITL patterns",
52
+ source: "registered_actions.py",
53
+ },
54
+ {
55
+ action: "fixed_response",
56
+ version: "v0",
57
+ replacement: "fixed_response",
58
+ replacementVersion: "v1",
59
+ source: "registered_actions.py",
60
+ },
61
+ {
62
+ action: "custom_agent",
63
+ version: "v0",
64
+ replacement: "custom_agent",
65
+ replacementVersion: "v1",
66
+ source: "registered_actions.py",
67
+ },
68
+ {
69
+ action: "json_mapper",
70
+ version: "v0",
71
+ replacement: "json_mapper",
72
+ replacementVersion: "v1",
73
+ source: "registered_actions.py",
74
+ },
75
+ {
76
+ action: "rule_validation_with_documents",
77
+ version: "v0",
78
+ replacement: "rule_validation_with_documents",
79
+ replacementVersion: "v1",
80
+ source: "registered_actions.py",
81
+ },
82
+ {
83
+ action: "text_categorizer",
84
+ version: "v0",
85
+ replacement: "text_categorizer",
86
+ replacementVersion: "v1",
87
+ source: "registered_actions.py",
88
+ },
89
+ {
90
+ action: "respond_with_sources",
91
+ version: "v0",
92
+ replacement: "respond_for_external_actions",
93
+ migrationNotes: "Better tool result handling",
94
+ source: "registered_actions.py",
95
+ },
96
+ ];
97
+ /**
98
+ * Deprecated actions without documented replacements.
99
+ * These are typically internal actions or superseded functionality.
100
+ */
101
+ export const DEPRECATED_ACTIONS_NO_REPLACEMENT = [
102
+ { action: "meta_respond", version: "all", environment: "all", migrationNotes: "Internal LLM response - do not use directly", source: "system_agents" },
103
+ { action: "meta_respond_v2", version: "all", environment: "all", migrationNotes: "Internal LLM response - do not use directly", source: "system_agents" },
104
+ { action: "ticket_respond_with_sources", version: "all", environment: "all", source: "system_agents" },
105
+ { action: "text_with_sources_translation", version: "all", environment: "all", source: "system_agents" },
106
+ { action: "document_categorizer", version: "all", environment: "all", source: "system_agents" },
107
+ { action: "document_metasearch", version: "all", environment: "all", source: "system_agents" },
108
+ { action: "combine_search_results", version: "all", environment: "all", source: "system_agents" },
109
+ { action: "combine_text_with_sources", version: "all", environment: "all", source: "system_agents" },
110
+ { action: "develop_outline", version: "all", environment: "all", source: "system_agents" },
111
+ { action: "generate_document_using_outline", version: "all", environment: "all", source: "system_agents" },
112
+ { action: "write_document", version: "all", environment: "all", source: "system_agents" },
113
+ { action: "external_action_caller_v1", version: "all", environment: "dev", migrationNotes: "Use external_action_caller v0", source: "system_agents" },
114
+ { action: "ticket_thread_sink", version: "all", environment: "dev", source: "system_agents" },
115
+ { action: "call_llm_v1", version: "all", environment: "all", migrationNotes: "Use call_llm v2", source: "system_agents" },
116
+ ];
117
+ /**
118
+ * All deprecated actions combined.
119
+ */
120
+ export const ALL_DEPRECATED_ACTIONS = [
121
+ ...DEPRECATED_ACTIONS_WITH_REPLACEMENT,
122
+ ...DEPRECATED_ACTIONS_NO_REPLACEMENT,
123
+ ];
124
+ /**
125
+ * Deprecated agent types (legacy).
126
+ */
127
+ export const DEPRECATED_AGENT_TYPES = [
128
+ "VISUALIZE_DATA",
129
+ "VISUALIZE_DATA_FOLLOWUP",
130
+ "PARSE_DELIMITED_FILES",
131
+ "GITHUB_DEMO",
132
+ "GITHUB_FOLLOWUP",
133
+ "RULES_VALIDATOR_AGENT",
134
+ "SUBJECT_RULE_VALIDATOR_AGENT",
135
+ "PRIOR_AUTHORISATION_AGENT",
136
+ "CHATBOT_AGENT",
137
+ "DEMO_PROPOSAL_MANAGEMENT_AGENT",
138
+ "GENERALISED_RULE_VALIDATION_AGENT",
139
+ ];
140
+ /**
141
+ * Quick lookup map: "action/version" -> replacement info
142
+ */
143
+ export const DEPRECATED_ACTIONS_MAP = {
144
+ "search/v0": { replacement: "search/v2", notes: "v2 requires datastore_configs input (mandatory)" },
145
+ "web_search/v0": { replacement: "live_web_search or ai_web_search", notes: "Split into two specialized actions" },
146
+ "call_llm/v0": { replacement: "call_llm/v2", notes: "v2 uses meta_respond_v2" },
147
+ "combine_external_action_and_search_results/v0": { replacement: "respond_for_external_actions", notes: "Migration in progress" },
148
+ "human_collaboration/v0": { replacement: "general_hitl", notes: "More flexible HITL patterns" },
149
+ "fixed_response/v0": { replacement: "fixed_response/v1" },
150
+ "custom_agent/v0": { replacement: "custom_agent/v1" },
151
+ "json_mapper/v0": { replacement: "json_mapper/v1" },
152
+ "rule_validation_with_documents/v0": { replacement: "rule_validation_with_documents/v1" },
153
+ "text_categorizer/v0": { replacement: "text_categorizer/v1" },
154
+ "respond_with_sources/v0": { replacement: "respond_for_external_actions", notes: "Better tool result handling" },
155
+ "meta_respond/all": { notes: "Internal LLM response - do not use directly" },
156
+ "meta_respond_v2/all": { notes: "Internal LLM response - do not use directly" },
157
+ "external_action_caller_v1/all": { notes: "Use external_action_caller v0" },
158
+ "call_llm_v1/all": { notes: "Use call_llm v2" },
159
+ };
160
+ /**
161
+ * Check if an action is deprecated.
162
+ */
163
+ export function isActionDeprecated(actionName, version = "v0") {
164
+ return `${actionName}/${version}` in DEPRECATED_ACTIONS_MAP;
165
+ }
166
+ /**
167
+ * Get replacement for a deprecated action.
168
+ */
169
+ export function getActionReplacement(actionName, version = "v0") {
170
+ return DEPRECATED_ACTIONS_MAP[`${actionName}/${version}`];
171
+ }
@@ -71,30 +71,55 @@ await persona({ id: "abc", update: { workflow_spec: spec } }); // Then deploy`,
71
71
  { name: "Fallback", description: "Anything else" } // Required!
72
72
  ]`,
73
73
  },
74
+ {
75
+ id: "data-sources-before-search",
76
+ level: "critical",
77
+ category: "workflow",
78
+ title: "Upload Data Sources Before Search",
79
+ applies: "When creating workflows with search/RAG nodes",
80
+ description: "Workflows with search/v2 nodes require data sources (documents) to be uploaded to the persona. " +
81
+ "Without documents, search returns empty results and RAG is useless.",
82
+ do: "1. Upload documents: persona(id='...', data={method:'upload', path:'/path/to/doc.pdf'})\n" +
83
+ "2. Check status: persona(id='...', data={method:'stats'}) → verify 'success' count > 0\n" +
84
+ "3. Then deploy workflow with search node",
85
+ dont: "Deploy search-based workflows before uploading any documents to the knowledge base.",
86
+ example: `// Upload documents first
87
+ await persona({ id: "abc", data: { method: "upload", path: "/docs/product-faq.pdf" } });
88
+ await persona({ id: "abc", data: { method: "upload", path: "/docs/pricing.pdf" } });
89
+
90
+ // Check they're indexed
91
+ const stats = await persona({ id: "abc", data: { method: "stats" } });
92
+ // stats.success should be > 0
93
+
94
+ // Then deploy workflow with search
95
+ await workflow({ mode: "deploy", persona_id: "abc", workflow_def: workflowWithSearch });`,
96
+ antiExample: `// BAD: Deploy search workflow with no documents
97
+ await workflow({ mode: "deploy", persona_id: "abc", workflow_def: workflowWithSearch });
98
+ // Search will return empty results!`,
99
+ related: ["analyze-before-modify"],
100
+ },
74
101
  {
75
102
  id: "workflow-modification-scope",
76
103
  level: "critical",
77
104
  category: "workflow",
78
105
  title: "Structure vs Content Changes",
79
106
  applies: "When modifying workflows",
80
- description: "workflow(mode='modify') only supports STRUCTURAL changes (add/remove nodes, rewire connections). " +
107
+ description: "persona(method='update', input='...') only supports STRUCTURAL changes (add/remove nodes, rewire connections). " +
81
108
  "CONTENT changes (fixed_response data, call_llm prompts) require manual workflow_def editing.",
82
- do: "For content changes: 1) Get workflow with persona(include_workflow=true), " +
109
+ do: "For content changes: 1) Get workflow with persona(method='get', id='...'), " +
83
110
  "2) Edit the node's stringValue in the JSON, 3) Deploy with workflow(mode='deploy', workflow_def={...})",
84
- dont: "Use workflow(mode='modify') for content changes - it will silently return 0 changes.",
85
- example: `// STRUCTURAL change (supported via structured operations)
86
- persona({ mode: "modify", id: "abc", operations: [
87
- { type: "insert", insert: { action_type: "hitl", insert_before: "send_email" }}
88
- ]})
111
+ dont: "Use update with input for content changes - it will silently return 0 changes.",
112
+ example: `// STRUCTURAL change (supported via update + input)
113
+ persona({ method: "update", id: "abc", input: "add HITL before send_email" })
89
114
 
90
115
  // CONTENT change (requires manual edit)
91
- const p = await persona({ id: "abc", include_workflow: true });
116
+ const p = await persona({ method: "get", id: "abc" });
92
117
  const wf = p.workflow_def;
93
118
  // Find and edit the fixed_response node's inputs.data.stringValue
94
119
  wf.actions[2].inputs.data.stringValue = JSON.stringify(newData);
95
120
  await workflow({ mode: "deploy", persona_id: "abc", workflow_def: wf });`,
96
- antiExample: `// This will silently fail - content change via modify
97
- persona({ mode: "modify", id: "abc", operations: [...] }) // for content changes`,
121
+ antiExample: `// This will silently fail - content change via update
122
+ persona({ method: "update", id: "abc", input: "change the welcome message" }) // for content changes`,
98
123
  related: ["workflow-spec-not-def", "llm-driven-modifications"],
99
124
  },
100
125
  {
@@ -103,15 +128,16 @@ persona({ mode: "modify", id: "abc", operations: [...] }) // for content change
103
128
  category: "workflow",
104
129
  title: "LLM-Driven Workflow Modifications",
105
130
  applies: "When modifying workflows structurally",
106
- description: "The MCP does NOT parse natural language for modifications. YOU (the Agent/LLM) must build structured operations. " +
107
- "Call persona(mode='modify') first to get workflow context, then build operations array.",
108
- do: "1) Get context: persona(mode='modify', id='...') returns current_nodes, available_actions, example_operations. " +
109
- "2) Build structured operations array. 3) Execute: persona(mode='modify', id='...', operations=[...])",
110
- dont: "Pass natural language strings expecting MCP to parse them into operations.",
111
- example: `// Step 1: Get context (returns current_nodes, available_actions, examples)
112
- const ctx = await persona({ mode: "modify", id: "abc" });
113
-
114
- // Step 2: YOU build structured operations based on user intent
131
+ description: "Use persona(method='update', input='...') for incremental changes. The MCP routes this to the workflow modify handler. " +
132
+ "For structured operations, use persona(method='update', operations=[...]).",
133
+ do: "1) Analyze workflow: persona(method='analyze', id='...') returns workflow_spec and issues. " +
134
+ "2) Make changes: persona(method='update', id='...', input='add HITL before email') " +
135
+ "3) Or use structured operations: persona(method='update', id='...', operations=[...])",
136
+ dont: "Expect operations to work without understanding the current workflow structure first.",
137
+ example: `// Option 1: Natural language input (routed to modify handler)
138
+ await persona({ method: "update", id: "abc", input: "add HITL approval before send_email" });
139
+
140
+ // Option 2: Structured operations
115
141
  const operations = [
116
142
  {
117
143
  type: "insert",
@@ -123,12 +149,10 @@ const operations = [
123
149
  }
124
150
  }
125
151
  ];
126
-
127
- // Step 3: Execute
128
- await persona({ mode: "modify", id: "abc", operations });`,
129
- antiExample: `// DON'T: Pass natural language expecting MCP to understand it
130
- await persona({ mode: "modify", id: "abc", input: "add approval before email" })
131
- // The MCP will NOT parse this - it returns context instead`,
152
+ await persona({ method: "update", id: "abc", operations });`,
153
+ antiExample: `// DON'T: Try to modify without analyzing first
154
+ await persona({ method: "update", id: "abc", operations: [...] })
155
+ // Always analyze the workflow first to understand structure`,
132
156
  related: ["workflow-modification-scope", "analyze-before-modify"],
133
157
  },
134
158
  // ─────────────────────────────────────────────────────────────────────────
@@ -240,14 +264,14 @@ export const TOOL_GUIDANCE = {
240
264
  example: 'persona(id="abc", analyze=true)',
241
265
  },
242
266
  {
243
- name: "Get modify context",
244
- description: "Get current_nodes, available_actions for building operations",
245
- example: 'persona(mode="modify", id="abc")',
267
+ name: "Update with input",
268
+ description: "Incremental workflow changes via natural language",
269
+ example: 'persona(method="update", id="abc", input="add HITL before email")',
246
270
  },
247
271
  {
248
- name: "Execute modify",
272
+ name: "Update with operations",
249
273
  description: "Apply structured operations (insert/remove/rewire)",
250
- example: 'persona(mode="modify", id="abc", operations=[...])',
274
+ example: 'persona(method="update", id="abc", operations=[...])',
251
275
  },
252
276
  {
253
277
  name: "Create",
@@ -406,12 +430,11 @@ ${critical.map((r) => `- **${r.title}**: ${r.do}`).join("\n")}
406
430
  ${important.map((r) => `- **${r.title}**: ${r.do}`).join("\n")}
407
431
 
408
432
  ## LLM-Driven Architecture (CRITICAL)
409
- The MCP does NOT parse natural language for workflow modifications.
410
- **YOU (the Agent) must build structured operations:**
433
+ Use \`persona(method="update", input="...")\` for workflow modifications:
411
434
 
412
- 1. Get context: \`persona(mode="modify", id="...")\` → returns current_nodes, available_actions
413
- 2. Build operations array based on user intent
414
- 3. Execute: \`persona(mode="modify", id="...", operations=[...])\`
435
+ 1. Analyze: \`persona(method="analyze", id="...")\` → returns workflow_spec, issues
436
+ 2. Update with natural language: \`persona(method="update", id="...", input="add HITL before email")\`
437
+ 3. Or use structured operations: \`persona(method="update", id="...", operations=[...])\`
415
438
 
416
439
  Example operations: insert, remove, rewire, update_config
417
440