@ema.co/mcp-toolkit 2026.1.25 → 2026.1.26-4

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 (87) hide show
  1. package/README.md +10 -2
  2. package/dist/mcp/handlers/action/index.js +3 -18
  3. package/dist/mcp/handlers/data/index.js +385 -41
  4. package/dist/mcp/handlers/data/templates.js +107 -0
  5. package/dist/mcp/handlers/deprecation.js +50 -0
  6. package/dist/mcp/handlers/env/index.js +8 -4
  7. package/dist/mcp/handlers/knowledge/index.js +44 -237
  8. package/dist/mcp/handlers/persona/create.js +47 -18
  9. package/dist/mcp/handlers/persona/index.js +14 -11
  10. package/dist/mcp/handlers/persona/update.js +4 -2
  11. package/dist/mcp/handlers/persona/version.js +234 -0
  12. package/dist/mcp/handlers/sync/index.js +3 -18
  13. package/dist/mcp/handlers/template/index.js +75 -10
  14. package/dist/mcp/handlers/workflow/analyze.js +171 -0
  15. package/dist/mcp/handlers/workflow/compare.js +70 -0
  16. package/dist/mcp/handlers/workflow/deploy.js +73 -0
  17. package/dist/mcp/handlers/workflow/generate.js +350 -0
  18. package/dist/mcp/handlers/workflow/index.js +294 -0
  19. package/dist/mcp/handlers/workflow/modify.js +456 -0
  20. package/dist/mcp/handlers/workflow/optimize.js +136 -0
  21. package/dist/mcp/handlers/workflow/types.js +4 -0
  22. package/dist/mcp/handlers/workflow/utils.js +30 -0
  23. package/dist/mcp/handlers-consolidated.js +73 -2696
  24. package/dist/mcp/prompts.js +83 -43
  25. package/dist/mcp/resources.js +382 -57
  26. package/dist/mcp/server.js +199 -391
  27. package/dist/mcp/{tools-v2.js → tools.js} +20 -54
  28. package/dist/mcp/workflow-operations.js +2 -2
  29. package/dist/sdk/client-adapter.js +267 -32
  30. package/dist/sdk/client.js +45 -16
  31. package/dist/sdk/ema-client.js +183 -0
  32. package/dist/sdk/generated/deprecated-actions.js +171 -0
  33. package/dist/sdk/generated/template-fallbacks.js +123 -0
  34. package/dist/sdk/guidance.js +65 -11
  35. package/dist/sdk/index.js +3 -1
  36. package/dist/sdk/knowledge.js +139 -86
  37. package/dist/sdk/workflow-intent.js +27 -0
  38. package/dist/sdk/workflow-transformer.js +0 -342
  39. package/docs/mcp-tools-guide.md +37 -45
  40. package/package.json +10 -4
  41. package/dist/mcp/handlers/persona/analyze.js +0 -275
  42. package/dist/mcp/handlers/persona/compare.js +0 -32
  43. package/dist/mcp/tools-consolidated.js +0 -875
  44. package/dist/mcp/tools-legacy.js +0 -736
  45. package/docs/CODEBASE-ANALYSIS-2026-01-23.md +0 -936
  46. package/docs/CODEBASE-ANALYSIS-PRIORITIZED.md +0 -774
  47. package/docs/api-contracts.md +0 -216
  48. package/docs/auto-builder-analysis.md +0 -271
  49. package/docs/blog/mcp-tool-design-lessons.md +0 -309
  50. package/docs/data-architecture.md +0 -166
  51. package/docs/demos/ap-invoice-generation.md +0 -347
  52. package/docs/demos/ap-invoice-processing.md +0 -271
  53. package/docs/ema-auto-builder-guide.html +0 -394
  54. package/docs/lessons-learned.md +0 -209
  55. package/docs/llm-native-workflow-design.md +0 -252
  56. package/docs/local-generation.md +0 -508
  57. package/docs/mcp-flow-diagram.md +0 -135
  58. package/docs/migration/action-composition-migration.md +0 -270
  59. package/docs/naming-conventions.md +0 -278
  60. package/docs/proposals/HANDOFF-tool-restructure.md +0 -526
  61. package/docs/proposals/action-composition.md +0 -490
  62. package/docs/proposals/explicit-method-restructure.md +0 -328
  63. package/docs/proposals/mcp-tool-restructure-2026-01.md +0 -366
  64. package/docs/proposals/self-contained-guidance.md +0 -427
  65. package/docs/proto-sdk-generation.md +0 -242
  66. package/docs/release-impact.md +0 -102
  67. package/docs/release-process.md +0 -157
  68. package/docs/staging.RULE.md +0 -142
  69. package/docs/test-persona-creation.md +0 -196
  70. package/docs/tool-consolidation-v2.md +0 -225
  71. package/docs/tool-response-standards.md +0 -256
  72. package/resources/demo-kits/README.md +0 -175
  73. package/resources/demo-kits/finance-ap/manifest.json +0 -150
  74. package/resources/demo-kits/tags.json +0 -91
  75. package/resources/docs/getting-started.md +0 -97
  76. package/resources/templates/auto-builder-rules.md +0 -224
  77. package/resources/templates/chat-ai/README.md +0 -119
  78. package/resources/templates/chat-ai/persona-config.json +0 -111
  79. package/resources/templates/dashboard-ai/README.md +0 -156
  80. package/resources/templates/dashboard-ai/persona-config.json +0 -180
  81. package/resources/templates/demo-scenarios/README.md +0 -63
  82. package/resources/templates/demo-scenarios/test-published-package.md +0 -116
  83. package/resources/templates/document-gen-ai/README.md +0 -132
  84. package/resources/templates/document-gen-ai/persona-config.json +0 -316
  85. package/resources/templates/voice-ai/README.md +0 -123
  86. package/resources/templates/voice-ai/persona-config.json +0 -74
  87. package/resources/templates/voice-ai/workflow-prompt.md +0 -121
@@ -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,
@@ -287,6 +298,178 @@ export class EmaClientV2 {
287
298
  const personas = await this.listPersonas();
288
299
  return personas.filter(p => this.getSyncMetadata(p) !== null);
289
300
  }
301
+ // ─────────────────────────────────────────────────────────────────────────────
302
+ // File Upload/Delete - REST API (not yet in OpenAPI spec)
303
+ // These are hand-written until the OpenAPI spec is updated
304
+ // ─────────────────────────────────────────────────────────────────────────────
305
+ /**
306
+ * Upload a file to an AI Employee's knowledge base.
307
+ * API endpoint: POST /api/v2/upload/files (multipart/form-data)
308
+ */
309
+ async uploadDataSource(personaId, fileContent, filename, opts) {
310
+ const widgetName = opts?.widgetName ?? "fileUpload";
311
+ const tags = opts?.tags ?? widgetName;
312
+ const mimeType = opts?.mimeType ?? this.detectMimeType(filename);
313
+ // Build multipart form data manually
314
+ const boundary = `----EmaUploadBoundary${Date.now()}`;
315
+ const parts = [];
316
+ // persona_id field
317
+ parts.push(`--${boundary}\r\nContent-Disposition: form-data; name="persona_id"\r\n\r\n${personaId}`);
318
+ // widget_name field
319
+ parts.push(`--${boundary}\r\nContent-Disposition: form-data; name="widget_name"\r\n\r\n${widgetName}`);
320
+ // tags field (required by API, defaults to widget_name)
321
+ parts.push(`--${boundary}\r\nContent-Disposition: form-data; name="tags"\r\n\r\n${tags}`);
322
+ // Build the file part
323
+ const filePart = [
324
+ `--${boundary}`,
325
+ `Content-Disposition: form-data; name="file"; filename="${filename}"`,
326
+ `Content-Type: ${mimeType}`,
327
+ "",
328
+ "", // Content will be appended as binary
329
+ ].join("\r\n");
330
+ // Combine everything
331
+ const headerBuffer = Buffer.from(parts.join("\r\n") + "\r\n" + filePart);
332
+ const fileBuffer = Buffer.isBuffer(fileContent) ? fileContent : Buffer.from(fileContent);
333
+ const footerBuffer = Buffer.from(`\r\n--${boundary}--\r\n`);
334
+ const bodyBuffer = Buffer.concat([headerBuffer, fileBuffer, footerBuffer]);
335
+ const controller = new AbortController();
336
+ const timeoutId = setTimeout(() => controller.abort(), 60_000);
337
+ try {
338
+ const resp = await fetch(`${this.env.baseUrl.replace(/\/$/, "")}/api/v2/upload/files`, {
339
+ method: "POST",
340
+ headers: {
341
+ Authorization: `Bearer ${this.env.bearerToken}`,
342
+ "Content-Type": `multipart/form-data; boundary=${boundary}`,
343
+ "X-Persona-Id": personaId,
344
+ },
345
+ body: bodyBuffer,
346
+ signal: controller.signal,
347
+ });
348
+ if (!resp.ok) {
349
+ const errorBody = await resp.text();
350
+ throw new EmaApiError({
351
+ statusCode: resp.status,
352
+ body: errorBody,
353
+ message: `uploadDataSource failed (${this.env.name})`,
354
+ });
355
+ }
356
+ const result = await resp.json();
357
+ return {
358
+ fileId: result.file_id ?? result.id ?? "",
359
+ status: result.status ?? "uploaded",
360
+ filename: filename,
361
+ };
362
+ }
363
+ finally {
364
+ clearTimeout(timeoutId);
365
+ }
366
+ }
367
+ /**
368
+ * Delete a data source file from an AI Employee's knowledge base.
369
+ * API endpoint: DELETE /api/v2/upload/files/{file_id}
370
+ */
371
+ async deleteDataSource(personaId, fileId) {
372
+ const controller = new AbortController();
373
+ const timeoutId = setTimeout(() => controller.abort(), 30_000);
374
+ try {
375
+ const resp = await fetch(`${this.env.baseUrl.replace(/\/$/, "")}/api/v2/upload/files/${fileId}?persona_id=${personaId}`, {
376
+ method: "DELETE",
377
+ headers: {
378
+ Authorization: `Bearer ${this.env.bearerToken}`,
379
+ "X-Persona-Id": personaId,
380
+ },
381
+ signal: controller.signal,
382
+ });
383
+ if (!resp.ok && resp.status !== 404) {
384
+ const errorBody = await resp.text();
385
+ throw new EmaApiError({
386
+ statusCode: resp.status,
387
+ body: errorBody,
388
+ message: `deleteDataSource failed (${this.env.name})`,
389
+ });
390
+ }
391
+ return { success: true, fileId };
392
+ }
393
+ finally {
394
+ clearTimeout(timeoutId);
395
+ }
396
+ }
397
+ /**
398
+ * Delete a persona
399
+ * API endpoint: DELETE /api/personas/{persona_id}
400
+ */
401
+ async deletePersona(personaId) {
402
+ const controller = new AbortController();
403
+ const timeoutId = setTimeout(() => controller.abort(), 30_000);
404
+ try {
405
+ const resp = await fetch(`${this.env.baseUrl.replace(/\/$/, "")}/api/personas/${personaId}`, {
406
+ method: "DELETE",
407
+ headers: {
408
+ Authorization: `Bearer ${this.env.bearerToken}`,
409
+ },
410
+ signal: controller.signal,
411
+ });
412
+ if (!resp.ok) {
413
+ const errorBody = await resp.text();
414
+ throw new EmaApiError({
415
+ statusCode: resp.status,
416
+ body: errorBody,
417
+ message: `deletePersona failed (${this.env.name})`,
418
+ });
419
+ }
420
+ return { success: true };
421
+ }
422
+ finally {
423
+ clearTimeout(timeoutId);
424
+ }
425
+ }
426
+ /**
427
+ * Wait for data replication to complete (polling helper)
428
+ */
429
+ async waitForReplication(requestId, personaId, opts) {
430
+ const timeout = opts?.timeoutMs ?? 60_000;
431
+ const interval = opts?.pollIntervalMs ?? 2_000;
432
+ const start = Date.now();
433
+ while (Date.now() - start < timeout) {
434
+ const result = await this.getReplicationStatus(requestId);
435
+ if (result.status === 2 /* COMPLETED */) {
436
+ return { status: "completed" };
437
+ }
438
+ if (result.status === 3 /* FAILED */) {
439
+ return {
440
+ status: "failed",
441
+ failedReason: result.failedReason || "Unknown error"
442
+ };
443
+ }
444
+ await new Promise(resolve => setTimeout(resolve, interval));
445
+ }
446
+ return { status: "timeout" };
447
+ }
448
+ // ─────────────────────────────────────────────────────────────────────────────
449
+ // Private Helpers
450
+ // ─────────────────────────────────────────────────────────────────────────────
451
+ /** Detect MIME type from filename extension */
452
+ detectMimeType(filename) {
453
+ const ext = filename.split('.').pop()?.toLowerCase() ?? '';
454
+ const mimeTypes = {
455
+ 'pdf': 'application/pdf',
456
+ 'txt': 'text/plain',
457
+ 'csv': 'text/csv',
458
+ 'json': 'application/json',
459
+ 'xml': 'application/xml',
460
+ 'html': 'text/html',
461
+ 'md': 'text/markdown',
462
+ 'doc': 'application/msword',
463
+ 'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
464
+ 'xls': 'application/vnd.ms-excel',
465
+ 'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
466
+ 'png': 'image/png',
467
+ 'jpg': 'image/jpeg',
468
+ 'jpeg': 'image/jpeg',
469
+ 'gif': 'image/gif',
470
+ };
471
+ return mimeTypes[ext] ?? 'application/octet-stream';
472
+ }
290
473
  }
291
474
  // Default export for convenience
292
475
  export default EmaClientV2;
@@ -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
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Auto-generated fallback templates from proto definitions.
3
+ *
4
+ * These are used ONLY when the API is unavailable.
5
+ * The API templates (getPersonaTemplates()) are the source of truth.
6
+ *
7
+ * Generated at: 2026-01-25T21:51:05.018Z
8
+ *
9
+ * DO NOT EDIT MANUALLY - regenerate with: npm run generate:templates
10
+ */
11
+ /* eslint-disable @typescript-eslint/no-explicit-any */
12
+ /**
13
+ * Voice AI persona config template (fallback).
14
+ *
15
+ * Fields derived from proto definitions:
16
+ * - VoicebotConversationSettingsWidget
17
+ * - VoicebotVoiceSettingsWidget
18
+ * - VoicebotCallSettingsWidget
19
+ * - VoicebotVADSettingsWidget
20
+ * - VoicebotDataStorageSettingsWidget
21
+ */
22
+ export const VOICE_TEMPLATE_FALLBACK = {
23
+ "conversationSettings": {
24
+ "name": "{AI Name}",
25
+ "welcomeMessage": "Hello, thank you for calling {Company}. This is {AI Name}. How can I help you today?",
26
+ "speechCharacteristics": "Keep tone friendly and concise. Use brief pauses between sentences.",
27
+ "identityAndPurpose": "You are {AI Name}, an AI assistant for {Company}. Your responsibilities: 1) {Task 1} 2) {Task 2}",
28
+ "hangupInstructions": "End the call when: The caller explicitly says goodbye, or confirms they have no more questions.",
29
+ "takeActionInstructions": "</Case 1>\\n{Action Name}\\n\\nTrigger When: {Condition}\\n\\nIntent for tool call: \"{Intent}\"\\n\\nRequired parameters: { \"{param}\": \"\" }\\n</Case 1>",
30
+ "systemPrompt": "Tool Calling Instructions: Always collect required parameters before calling tools. Confirm with caller before executing.",
31
+ "waitMessage": "One moment while I look that up for you...",
32
+ "formFillingInstructions": "When collecting information, ask one question at a time. Confirm values before proceeding.",
33
+ "transferCallInstructions": "Transfer when: The caller explicitly requests a human agent, or the issue is beyond AI capabilities."
34
+ },
35
+ "voiceSettings": {
36
+ "languageHints": [
37
+ "en-US"
38
+ ],
39
+ "voiceModel": "default"
40
+ },
41
+ "callSettings": {
42
+ "enableCallForwarding": false,
43
+ "callForwardingNumber": "",
44
+ "enableSpamCallPrevention": true,
45
+ "enableDisconnectCall": false
46
+ },
47
+ "vadSettings": {
48
+ "turnTimeout": 3,
49
+ "silenceEndCallTimeout": 30,
50
+ "maxConversationDuration": 600
51
+ },
52
+ "dataStorageSettings": {
53
+ "storeAudioRecording": true,
54
+ "storeTranscripts": true,
55
+ "storeAgentTranscript": true
56
+ }
57
+ };
58
+ /**
59
+ * Field documentation for LLM consumption.
60
+ * Use this when generating prompts about config structure.
61
+ */
62
+ export const VOICE_TEMPLATE_FIELD_DOCS = `
63
+ ## Voice AI Persona Configuration Fields
64
+
65
+ ### Conversation Settings
66
+ - name: Name for the Voicebot Agent that will be spoken
67
+ - welcomeMessage: Message spoken when call connects
68
+ - speechCharacteristics: Instructions on how voicebot should speak
69
+ - identityAndPurpose: What the voicebot is for and tools it has access to
70
+ - hangupInstructions: When to end the call
71
+ - takeActionInstructions: Instructions on what actions takeAction can perform
72
+ - systemPrompt: Instructions on how to respond to user, handle tools
73
+ - waitMessage: Message when tool is taking time to execute
74
+ - formFillingInstructions: How to collect form data conversationally
75
+ - transferCallInstructions: When to transfer to human agent
76
+
77
+ ### Voice Settings
78
+ - languageHints: BCP47 language codes for speech recognition
79
+ - voiceModel: Voice model ID
80
+
81
+ ### Call Settings
82
+ - enableCallForwarding: Enable call forwarding
83
+ - callForwardingNumber: Number to forward calls to
84
+ - enableSpamCallPrevention: Enable spam call prevention
85
+ - enableDisconnectCall: Enable disconnect call
86
+
87
+ ### VAD Settings
88
+ - turnTimeout: Seconds to wait for caller to finish speaking
89
+ - silenceEndCallTimeout: Seconds of silence before ending call
90
+ - maxConversationDuration: Maximum call length in seconds
91
+
92
+ ### Data Storage Settings
93
+ - storeAudioRecording: Store audio recordings
94
+ - storeTranscripts: Store call transcripts
95
+ - storeAgentTranscript: Store agent transcript
96
+ `;
97
+ /**
98
+ * Get template fallback by type.
99
+ * Returns undefined if type is not supported.
100
+ */
101
+ export function getTemplateFallback(type) {
102
+ switch (type) {
103
+ case "voice":
104
+ return { ...VOICE_TEMPLATE_FALLBACK };
105
+ case "chat":
106
+ // TODO: Generate from chat proto definitions
107
+ return undefined;
108
+ case "dashboard":
109
+ // TODO: Generate from dashboard proto definitions
110
+ return undefined;
111
+ }
112
+ }
113
+ /**
114
+ * Get field documentation by type.
115
+ */
116
+ export function getTemplateFieldDocs(type) {
117
+ switch (type) {
118
+ case "voice":
119
+ return VOICE_TEMPLATE_FIELD_DOCS;
120
+ default:
121
+ return undefined;
122
+ }
123
+ }
@@ -82,8 +82,10 @@ await persona({ id: "abc", update: { workflow_spec: spec } }); // Then deploy`,
82
82
  do: "For content changes: 1) Get workflow with persona(include_workflow=true), " +
83
83
  "2) Edit the node's stringValue in the JSON, 3) Deploy with workflow(mode='deploy', workflow_def={...})",
84
84
  dont: "Use workflow(mode='modify') for content changes - it will silently return 0 changes.",
85
- example: `// STRUCTURAL change (supported)
86
- workflow({ mode: "modify", persona_id: "abc", input: "Add HITL before email" })
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
+ ]})
87
89
 
88
90
  // CONTENT change (requires manual edit)
89
91
  const p = await persona({ id: "abc", include_workflow: true });
@@ -92,8 +94,42 @@ const wf = p.workflow_def;
92
94
  wf.actions[2].inputs.data.stringValue = JSON.stringify(newData);
93
95
  await workflow({ mode: "deploy", persona_id: "abc", workflow_def: wf });`,
94
96
  antiExample: `// This will silently fail - content change via modify
95
- workflow({ mode: "modify", persona_id: "abc", input: "Change customer data to commission data" })`,
96
- related: ["workflow-spec-not-def"],
97
+ persona({ mode: "modify", id: "abc", operations: [...] }) // for content changes`,
98
+ related: ["workflow-spec-not-def", "llm-driven-modifications"],
99
+ },
100
+ {
101
+ id: "llm-driven-modifications",
102
+ level: "critical",
103
+ category: "workflow",
104
+ title: "LLM-Driven Workflow Modifications",
105
+ 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
115
+ const operations = [
116
+ {
117
+ type: "insert",
118
+ insert: {
119
+ action_type: "hitl",
120
+ display_name: "Email Approval",
121
+ insert_before: "send_email_node",
122
+ add_runif_to_downstream: true
123
+ }
124
+ }
125
+ ];
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`,
132
+ related: ["workflow-modification-scope", "analyze-before-modify"],
97
133
  },
98
134
  // ─────────────────────────────────────────────────────────────────────────
99
135
  // Important: Should Follow
@@ -162,7 +198,7 @@ const details = await persona({ id: list.personas[0].id });`,
162
198
  example: `// Useful resources:
163
199
  // ema://catalog/agents-summary - Action catalog
164
200
  // ema://rules/anti-patterns - Common mistakes
165
- // ema://templates/voice-ai/config - Voice AI template
201
+ // ema://catalog/persona-templates - Live API templates
166
202
  // ema://docs/usage-guide - Comprehensive guide`,
167
203
  },
168
204
  // ─────────────────────────────────────────────────────────────────────────
@@ -186,7 +222,7 @@ persona({ id: "abc", env: "prod" }) // production`,
186
222
  export const TOOL_GUIDANCE = {
187
223
  persona: {
188
224
  toolName: "persona",
189
- quickTip: "Use analyze=true before modifying. Use preview=true before deploying.",
225
+ quickTip: "Use analyze=true before modifying. Use preview=true before deploying. Build structured operations for modify.",
190
226
  operations: [
191
227
  {
192
228
  name: "List all",
@@ -204,9 +240,14 @@ export const TOOL_GUIDANCE = {
204
240
  example: 'persona(id="abc", analyze=true)',
205
241
  },
206
242
  {
207
- name: "Update",
208
- description: "Modify workflow using workflow_spec",
209
- example: 'persona(id="abc", update={workflow_spec: {...}})',
243
+ name: "Get modify context",
244
+ description: "Get current_nodes, available_actions for building operations",
245
+ example: 'persona(mode="modify", id="abc")',
246
+ },
247
+ {
248
+ name: "Execute modify",
249
+ description: "Apply structured operations (insert/remove/rewire)",
250
+ example: 'persona(mode="modify", id="abc", operations=[...])',
210
251
  },
211
252
  {
212
253
  name: "Create",
@@ -218,11 +259,13 @@ export const TOOL_GUIDANCE = {
218
259
  list: "Get details: persona(id='<id>')",
219
260
  get: "Analyze: persona(id='...', analyze=true)",
220
261
  analyze: "Review issues, build workflow_spec, then update",
221
- update: "Verify: persona(id='...') to confirm changes",
262
+ modify_context: "Build operations array, then execute with operations=[...]",
263
+ modify_execute: "Verify: persona(id='...') to confirm changes",
222
264
  create: "Configure: persona(id='<new_id>', update={...})",
223
265
  },
224
266
  commonMistakes: [
225
- "Modifying without analyzing first",
267
+ "Passing natural language to modify mode (MCP doesn't parse NL)",
268
+ "Modifying without getting context first",
226
269
  "Deploying without preview",
227
270
  "Using raw workflow_def instead of workflow_spec",
228
271
  "Forgetting Fallback in categorizers",
@@ -231,6 +274,7 @@ export const TOOL_GUIDANCE = {
231
274
  "analyze-before-modify",
232
275
  "preview-before-deploy",
233
276
  "workflow-spec-not-def",
277
+ "llm-driven-modifications",
234
278
  ],
235
279
  },
236
280
  catalog: {
@@ -361,6 +405,16 @@ ${critical.map((r) => `- **${r.title}**: ${r.do}`).join("\n")}
361
405
  ## Important
362
406
  ${important.map((r) => `- **${r.title}**: ${r.do}`).join("\n")}
363
407
 
408
+ ## LLM-Driven Architecture (CRITICAL)
409
+ The MCP does NOT parse natural language for workflow modifications.
410
+ **YOU (the Agent) must build structured operations:**
411
+
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=[...])\`
415
+
416
+ Example operations: insert, remove, rewire, update_config
417
+
364
418
  ## Resources
365
419
  - \`ema://docs/usage-guide\` - Complete guide
366
420
  - \`ema://catalog/agents-summary\` - Action catalog
package/dist/sdk/index.js CHANGED
@@ -22,7 +22,9 @@ export { StateStore } from "./state.js";
22
22
  // Auto Builder Knowledge Base
23
23
  export {
24
24
  // Catalogs & References
25
- AGENT_CATALOG, WIDGET_CATALOG, WORKFLOW_PATTERNS, QUALIFYING_QUESTIONS, PLATFORM_CONCEPTS, WORKFLOW_EXECUTION_MODEL, COMMON_MISTAKES, DEBUG_CHECKLIST, GUIDANCE_TOPICS, VOICE_PERSONA_TEMPLATE, PROJECT_TYPES,
25
+ AGENT_CATALOG, WIDGET_CATALOG, WORKFLOW_PATTERNS, QUALIFYING_QUESTIONS, PLATFORM_CONCEPTS, WORKFLOW_EXECUTION_MODEL, COMMON_MISTAKES, DEBUG_CHECKLIST, GUIDANCE_TOPICS,
26
+ // VOICE_PERSONA_TEMPLATE removed - use getTemplateFallback("voice") from generated/template-fallbacks.ts
27
+ PROJECT_TYPES,
26
28
  // Helper Functions
27
29
  getAgentsByCategory, getAgentByName, getWidgetsForPersonaType, checkTypeCompatibility, getQualifyingQuestionsByCategory, getRequiredQualifyingQuestions, getConceptByTerm, suggestAgentsForUseCase, validateWorkflowPrompt,
28
30
  // Workflow Analysis Functions