@ema.co/mcp-toolkit 1.5.2 → 1.7.0

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.

@@ -42,142 +42,189 @@ export function generateConsolidatedTools(envNames, defaultEnv) {
42
42
  // ═══════════════════════════════════════════════════════════════════════
43
43
  {
44
44
  name: "env",
45
- description: "List available Ema environments. Shows which is default.",
45
+ description: "List available Ema environments and toolkit info. Returns environments (with default marker) and toolkit name/version.",
46
46
  inputSchema: { type: "object", properties: {}, required: [] },
47
47
  },
48
48
  // ═══════════════════════════════════════════════════════════════════════
49
- // 2. PERSONA - AI Employee management (CRUD + compare + versioning)
49
+ // 2. PERSONA - Unified AI Employee management (create, modify, analyze, list)
50
50
  // ═══════════════════════════════════════════════════════════════════════
51
51
  {
52
52
  name: "persona",
53
- description: `Unified AI Employee (persona) management.
53
+ description: `Create, modify, analyze, or list AI Employees. ONE tool for everything.
54
54
 
55
- **Get single** (default when id provided):
56
- persona(id="IT Support Bot")
55
+ ## ⚠️ ONE CALL CREATES EVERYTHING - THEN STOP
56
+
57
+ If requirements are unclear, first call \`template(questions=true)\` to get what to ask.
58
+ Then make ONE call with all gathered info:
59
+
60
+ \`\`\`
61
+ persona(
62
+ input="Voice AI SDR: qualifies leads, identifies use-case, sends follow-up email",
63
+ type="voice",
64
+ name="SP - SDR Test", // REQUIRED: The actual persona name shown in Ema
65
+ preview=false
66
+ )
67
+ \`\`\`
68
+
69
+ **After success, STOP. Do NOT make follow-up calls to "fix" or "enhance".**
70
+
71
+ **MCP handles internally:** template selection, config generation, widget formatting, welcome message, API calls.
72
+
73
+ ## CRITICAL: The \`name\` Parameter
74
+
75
+ The \`name\` parameter is the **actual persona name** in the Ema platform - NOT derived from input.
76
+
77
+ ❌ name omitted → MCP parses name from input (often wrong)
78
+ ✅ name="SP - SDR Test" → Exact name shown in platform
79
+
80
+ ## Create NEW AI Employee
81
+
82
+ persona(input="<what it should do>", type="voice", name="Actual Name", preview=false)
83
+
84
+ ## Modify EXISTING (workflow changes)
85
+
86
+ persona(id="abc-123", input="add HITL before email", preview=false)
87
+
88
+ ## Update Config Only (voice settings, welcome message)
89
+
90
+ persona(id="abc-123", input="Update welcome message to: Hello!", preview=false)
91
+
92
+ MCP auto-detects config vs workflow changes.
93
+
94
+ ## Analyze/Get
95
+
96
+ persona(id="abc-123")
57
97
  persona(id="abc-123", include_workflow=true)
58
98
 
59
- **List/Search** (when --all or filters used):
99
+ ## Optimize (auto-fix issues)
100
+
101
+ persona(id="abc-123", optimize=true, preview=false)
102
+
103
+ ## List/Search
104
+
60
105
  persona(all=true)
61
106
  persona(query="support", status="active")
62
- persona(trigger_type="voice")
63
107
 
64
- **Create**:
65
- persona(mode="create", name="New Bot", type="voice")
108
+ ## Simple vs Complex Workflows
66
109
 
67
- **Update**:
68
- persona(id="abc-123", mode="update", name="Renamed")
110
+ **Simple** (Q&A, search + respond): Deploys directly → \`status: "success"\`
69
111
 
70
- **Compare**:
71
- persona(id="abc-123", mode="compare", compare_to="def-456")
112
+ **Complex** (email, HITL, multi-intent): Returns \`status: "needs_llm_generation"\` with:
113
+ - \`llm_prompt\`: System + user prompts for workflow generation
114
+ - \`available_actions\`: Action catalog from API
115
+ - \`hint\`: How to complete deployment
72
116
 
73
- **Templates** (list available templates):
74
- persona(templates=true)
117
+ For complex workflows, send the prompt to an LLM and deploy:
118
+ \`persona(workflow_def=<llm_response>, ...)\`
75
119
 
76
- **Version Management** (track configuration history):
77
- persona(id="abc-123", mode="version_create", message="Before major update")
78
- persona(id="abc-123", mode="version_list")
79
- persona(id="abc-123", mode="version_get", version="v3")
80
- persona(id="abc-123", mode="version_compare", v1="v2", v2="v3")
81
- persona(id="abc-123", mode="version_restore", version="v2")
82
- persona(id="abc-123", mode="version_policy", auto_on_deploy=true)`,
120
+ ## Key Rules
121
+
122
+ 1. **ONE CALL** - Put everything in \`input\`, explicit \`name\`, MCP handles rest
123
+ 2. **STOP after success** - Don't make follow-up "fix" calls
124
+ 3. **preview=false** - Deploys to Ema platform`,
83
125
  inputSchema: withEnv({
84
- // ID (or exact name) (optional - if omitted with all=true, lists all)
126
+ // === IDENTITY ===
85
127
  id: {
86
128
  type: "string",
87
- description: "Persona ID (UUID) or exact name. Omit for list operations."
129
+ description: "Persona ID (UUID) or exact name. Omit when creating new."
88
130
  },
89
- // Deprecated alias (backwards compatibility)
90
131
  identifier: {
91
132
  type: "string",
92
133
  deprecated: true,
93
- description: "DEPRECATED: use id. Persona ID (UUID) or exact name.",
134
+ description: "DEPRECATED: use id.",
94
135
  },
95
- // Mode (defaults to "get" if id provided, "list" if all=true)
96
- mode: {
136
+ // === CREATE/MODIFY (the main way to use this tool) ===
137
+ input: {
97
138
  type: "string",
98
- enum: ["get", "list", "create", "update", "compare", "version_create", "version_list", "version_get", "version_compare", "version_restore", "version_policy"],
99
- description: "Operation mode. Default: 'get' with id, 'list' without."
139
+ description: "Natural language description. For new: 'Voice AI for sales...'. For modify: 'add HITL before email'.",
100
140
  },
101
- // List/Search flags
141
+ type: {
142
+ type: "string",
143
+ enum: ["voice", "chat", "dashboard"],
144
+ description: "AI Employee type. REQUIRED for creating new."
145
+ },
146
+ name: { type: "string", description: "REQUIRED for new: The actual persona name shown in Ema platform (e.g., 'SP - SDR Test'). Don't derive from input." },
147
+ description: { type: "string", description: "Description of what it does." },
148
+ preview: {
149
+ type: "boolean",
150
+ description: "Default: true (safe). Set false to deploy changes."
151
+ },
152
+ // === ANALYZE/OPTIMIZE ===
153
+ optimize: {
154
+ type: "boolean",
155
+ description: "Auto-fix detected issues. Use with id.",
156
+ },
157
+ include: {
158
+ type: "array",
159
+ items: { type: "string", enum: ["issues", "connections", "fixes", "metrics"] },
160
+ description: "What to include in analysis output.",
161
+ },
162
+ include_workflow: { type: "boolean", description: "Include full workflow_def in response" },
163
+ include_fingerprint: { type: "boolean", description: "Include config hash" },
164
+ // === LIST/SEARCH ===
102
165
  all: { type: "boolean", description: "List all personas" },
103
166
  query: { type: "string", description: "Search by name (partial match)" },
104
167
  status: { type: "string", description: "Filter: 'active', 'inactive', 'draft'" },
105
168
  trigger_type: { type: "string", description: "Filter: 'voice', 'chat', 'dashboard'" },
106
169
  limit: { type: "number", description: "Max results (default: 50)" },
107
- // Get flags
108
- include_workflow: { type: "boolean", description: "Include full workflow_def" },
109
- include_fingerprint: { type: "boolean", description: "Include config hash" },
110
- // Create flags
111
- name: { type: "string", description: "Name (for create/update)" },
112
- description: { type: "string", description: "Description (for create/update)" },
113
- type: {
114
- type: "string",
115
- enum: ["voice", "chat", "dashboard"],
116
- description: "Persona type (for create)"
117
- },
118
- template_id: { type: "string", description: "Template ID (for create)" },
119
- clone_from: { type: "string", description: "Clone from persona ID (for create)" },
120
- clone_data: { type: "boolean", description: "Also clone knowledge base files when using clone_from (default: false)" },
121
- // Update flags
122
- enabled: { type: "boolean", description: "Enable/disable (for update)" },
170
+ // === COMPARE ===
171
+ compare_to: { type: "string", description: "Second persona ID for comparison" },
172
+ compare_env: { type: "string", description: "Environment of compare_to persona" },
173
+ // === ADVANCED/OVERRIDE ===
123
174
  proto_config: {
124
175
  type: "object",
125
- description: "Voice/chat settings (welcomeMessage, identityAndPurpose, etc.) for update"
176
+ description: "Override voice/chat settings. Usually auto-generated from input."
126
177
  },
127
178
  workflow: {
128
179
  type: "object",
129
- description: "Workflow definition to set (for update)"
180
+ description: "Direct workflow JSON (advanced). Usually auto-generated."
130
181
  },
131
- // Compare flags
132
- compare_to: { type: "string", description: "Second persona ID (for compare)" },
133
- compare_env: { type: "string", description: "Environment of compare_to persona" },
134
- // Templates flag
182
+ workflow_def: {
183
+ type: "object",
184
+ description: "Alias for workflow (backwards compatibility)."
185
+ },
186
+ template_id: { type: "string", description: "Specific template ID (usually auto-selected)" },
187
+ clone_from: { type: "string", description: "Clone from existing persona ID" },
188
+ clone_data: { type: "boolean", description: "Also clone knowledge base files and dashboard rows (auto-enables persona for dashboard cloning)" },
189
+ sanitize: { type: "boolean", description: "Sanitize/obfuscate PII and sensitive data (for demo environments)" },
190
+ sanitize_examples: { type: "array", items: { type: "string" }, description: "Additional items to treat as sensitive (e.g., company names)" },
191
+ enabled: { type: "boolean", description: "Enable/disable persona" },
192
+ // === TEMPLATES ===
135
193
  templates: { type: "boolean", description: "List available templates" },
136
- // Version management flags
137
- version: { type: "string", description: "Version identifier (e.g., 'v3', 'latest', or UUID)" },
138
- v1: { type: "string", description: "First version for comparison (version_compare mode)" },
139
- v2: { type: "string", description: "Second version for comparison (version_compare mode)" },
140
- message: { type: "string", description: "Version message/description (version_create mode)" },
141
- auto_on_deploy: { type: "boolean", description: "Auto-create version on deploy (version_policy mode)" },
142
- auto_on_sync: { type: "boolean", description: "Auto-create version on sync (version_policy mode)" },
143
- max_versions: { type: "number", description: "Max versions to keep (version_policy mode)" },
194
+ // === VERSION MANAGEMENT ===
195
+ mode: {
196
+ type: "string",
197
+ enum: ["version_create", "version_list", "version_get", "version_compare", "version_restore", "version_policy"],
198
+ description: "Version management mode. Only needed for version operations."
199
+ },
200
+ version: { type: "string", description: "Version identifier (e.g., 'v3', 'latest')" },
201
+ v1: { type: "string", description: "First version for comparison" },
202
+ v2: { type: "string", description: "Second version for comparison" },
203
+ message: { type: "string", description: "Version message/description" },
204
+ auto_on_deploy: { type: "boolean", description: "Auto-create version on deploy" },
205
+ auto_on_sync: { type: "boolean", description: "Auto-create version on sync" },
206
+ max_versions: { type: "number", description: "Max versions to keep" },
144
207
  }),
145
208
  },
146
209
  // ═══════════════════════════════════════════════════════════════════════
147
- // 3. WORKFLOW - Unified workflow operations (greenfield & brownfield)
210
+ // 3. WORKFLOW - DEPRECATED: Use persona() instead
148
211
  // ═══════════════════════════════════════════════════════════════════════
149
212
  {
150
213
  name: "workflow",
151
- description: `Create, modify, analyze, or deploy AI Employee workflows.
152
-
153
- ## Creating a NEW AI Employee (Greenfield)
154
-
155
- Creates persona from template, configures settings. Template provides valid workflow structure.
156
-
157
- workflow(input="Voice AI for sales development", name="My Sales SDR", type="voice", preview=false)
158
-
159
- Returns: { deployed_to: { persona_id, created: true }, next_steps: [...] }
160
-
161
- To customize the workflow AFTER creation, use modify mode with the persona_id.
214
+ description: `⚠️ DEPRECATED: Use \`persona()\` instead. This tool routes to persona.
162
215
 
163
- ## Modifying an EXISTING AI Employee (Brownfield)
216
+ ## Migration Guide
164
217
 
165
- Uses LLM-native workflow transformation. Fetches existing workflow, transforms it, deploys.
218
+ OLD (deprecated):
219
+ workflow(input="...", type="voice", name="Bot", preview=false)
220
+ workflow(persona_id="abc", input="add HITL")
166
221
 
167
- workflow(persona_id="abc-123", input="add HITL approval before sending emails")
168
- workflow(persona_id="abc-123", input="add a new Sales intent category", preview=false)
222
+ NEW (use this):
223
+ persona(input="...", type="voice", name="Bot", preview=false)
224
+ persona(id="abc", input="add HITL")
169
225
 
170
- ## Analyzing a Workflow
171
-
172
- workflow(persona_id="abc-123") # Returns issues, connections, metrics
173
- workflow(persona_id="abc-123", optimize=true, preview=false) # Auto-fix and deploy
174
-
175
- ## Key Rules
176
-
177
- 1. **preview=true (default)**: Safe - returns result without deploying
178
- 2. **preview=false**: Deploys changes to Ema platform
179
- 3. **Greenfield creates from template** - then modify workflow separately if needed
180
- 4. **Brownfield transforms existing** - uses decompile → transform → compile`,
226
+ The \`persona\` tool now handles everything: create, modify, analyze, list.
227
+ This \`workflow\` tool still works but shows a deprecation warning.`,
181
228
  inputSchema: withEnv({
182
229
  // === REQUIRED for creating/modifying ===
183
230
  input: {
@@ -296,23 +343,24 @@ Uses LLM-native workflow transformation. Fetches existing workflow, transforms i
296
343
  // ═══════════════════════════════════════════════════════════════════════
297
344
  {
298
345
  name: "template",
299
- description: `Get workflow patterns, widget references, and configuration templates.
346
+ description: `Get qualifying questions and reference patterns.
300
347
 
301
- **Workflow patterns**:
302
- template(pattern="intent-routing")
303
- template(patterns=true)
304
- template(patterns=true, type="voice")
348
+ ## 🎯 PRIMARY USE: Get Questions to Ask User
305
349
 
306
- **Widget reference**:
307
- template(widgets="voice")
308
- template(widgets="chat")
350
+ **Before creating an AI Employee, call this to get what to ask:**
351
+ template(questions=true) // All qualifying questions
352
+ template(questions=true, category="Voice") // Voice-specific questions
309
353
 
310
- **Persona config template**:
311
- template(config="voice") # Voice AI settings template
354
+ Returns structured questions about: type, intents, data sources, actions, approvals, etc.
355
+ Ask the user these questions, then put answers into ONE workflow() call.
312
356
 
313
- **Qualifying questions** (for requirements gathering):
314
- template(questions=true)
315
- template(questions=true, category="Voice")`,
357
+ ## Reference (understand options, not for manual building)
358
+
359
+ template(pattern="intent-routing") // See pattern structure
360
+ template(patterns=true) // List available patterns
361
+ template(widgets="voice") // Widget reference
362
+
363
+ ⚠️ Do NOT copy/paste configs from these - MCP generates them internally.`,
316
364
  inputSchema: {
317
365
  type: "object",
318
366
  properties: {
@@ -381,12 +429,20 @@ Uses LLM-native workflow transformation. Fetches existing workflow, transforms i
381
429
 
382
430
  **Attach data source to workflow node**:
383
431
  knowledge(persona_id="abc", mode="attach", node_name="knowledge_search_1")
384
- knowledge(persona_id="abc", mode="attach", node_name="knowledge_search_1", widget_name="fileUpload")`,
432
+ knowledge(persona_id="abc", mode="attach", node_name="knowledge_search_1", widget_name="fileUpload")
433
+
434
+ **Dashboard rows** (for Dashboard personas):
435
+ knowledge(persona_id="abc", mode="dashboard_rows")
436
+ knowledge(persona_id="abc", mode="dashboard_rows", limit=10)
437
+
438
+ **Clone dashboard data** (copy rows from source to target):
439
+ knowledge(persona_id="target", mode="dashboard_clone", source_persona_id="source")
440
+ knowledge(persona_id="target", mode="dashboard_clone", source_persona_id="source", sanitize=true)`,
385
441
  inputSchema: withEnv({
386
442
  persona_id: { type: "string", description: "AI Employee ID (required)" },
387
443
  mode: {
388
444
  type: "string",
389
- enum: ["list", "aggregates", "upload", "delete", "status", "toggle", "attach"],
445
+ enum: ["list", "aggregates", "upload", "delete", "status", "toggle", "attach", "dashboard_rows", "dashboard_clone"],
390
446
  description: "Operation. Default: 'list'"
391
447
  },
392
448
  // List flags
@@ -402,6 +458,10 @@ Uses LLM-native workflow transformation. Fetches existing workflow, transforms i
402
458
  enabled: { type: "boolean", description: "Enable/disable embedding" },
403
459
  // Attach flags
404
460
  node_name: { type: "string", description: "Workflow node name to attach data source to (e.g., 'knowledge_search_1')" },
461
+ // Dashboard clone flags
462
+ source_persona_id: { type: "string", description: "Source persona ID for dashboard clone" },
463
+ sanitize: { type: "boolean", description: "Sanitize/obfuscate data during clone" },
464
+ sanitize_examples: { type: "array", items: { type: "string" }, description: "Additional sensitive items to sanitize" },
405
465
  }, ["persona_id"]),
406
466
  },
407
467
  // ═══════════════════════════════════════════════════════════════════════
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Action Registry - Helpers for extracting action metadata from API
3
+ *
4
+ * Uses existing client.listActions() - no duplicate caching needed.
5
+ * Just provides helpers to extract version/namespace from raw API response.
6
+ */
7
+ // ─────────────────────────────────────────────────────────────────────────────
8
+ // Helpers to extract version/namespace from raw API response
9
+ // ─────────────────────────────────────────────────────────────────────────────
10
+ /**
11
+ * Extract action name, version, and namespaces from raw API response.
12
+ */
13
+ export function parseActionDefinition(action) {
14
+ // API returns typeName.name.namespaces and typeName.version
15
+ const raw = action;
16
+ const typeName = raw.typeName;
17
+ if (!typeName?.name?.name)
18
+ return null;
19
+ return {
20
+ name: typeName.name.name,
21
+ version: typeName.version ?? "v0",
22
+ namespaces: typeName.name.namespaces ?? ["actions", "emainternal"],
23
+ };
24
+ }
25
+ /**
26
+ * Extract template info from raw API response.
27
+ */
28
+ export function parseTemplateDefinition(template) {
29
+ if (!template.id || !template.name)
30
+ return null;
31
+ const raw = template;
32
+ return {
33
+ id: template.id,
34
+ name: template.name,
35
+ triggerType: raw.trigger_type ?? 1,
36
+ };
37
+ }
38
+ // ─────────────────────────────────────────────────────────────────────────────
39
+ // Registry Class (lightweight, uses client directly)
40
+ // ─────────────────────────────────────────────────────────────────────────────
41
+ export class ActionRegistry {
42
+ actions = new Map();
43
+ templates = new Map();
44
+ templatesByType = new Map();
45
+ loaded = false;
46
+ /**
47
+ * Load from raw API data.
48
+ * Call client.listActions() and client.getPersonaTemplates() externally
49
+ * and pass the results here.
50
+ */
51
+ loadFromData(actions, templates) {
52
+ this.actions.clear();
53
+ this.templates.clear();
54
+ this.templatesByType.clear();
55
+ for (const action of actions) {
56
+ const def = parseActionDefinition(action);
57
+ if (def) {
58
+ this.actions.set(def.name, def);
59
+ }
60
+ }
61
+ for (const template of templates) {
62
+ const def = parseTemplateDefinition(template);
63
+ if (def) {
64
+ this.templates.set(def.id, def);
65
+ // First template of each trigger type wins
66
+ if (!this.templatesByType.has(def.triggerType)) {
67
+ this.templatesByType.set(def.triggerType, def);
68
+ }
69
+ }
70
+ }
71
+ this.loaded = true;
72
+ }
73
+ /**
74
+ * Get action version. Falls back to "v0".
75
+ */
76
+ getVersion(actionName) {
77
+ return this.actions.get(actionName)?.version ?? "v0";
78
+ }
79
+ /**
80
+ * Get action namespaces. Falls back to ["actions", "emainternal"].
81
+ */
82
+ getNamespaces(actionName) {
83
+ return this.actions.get(actionName)?.namespaces ?? ["actions", "emainternal"];
84
+ }
85
+ /**
86
+ * Get action definition.
87
+ */
88
+ getAction(actionName) {
89
+ return this.actions.get(actionName);
90
+ }
91
+ /**
92
+ * Get template by ID.
93
+ */
94
+ getTemplate(id) {
95
+ return this.templates.get(id);
96
+ }
97
+ /**
98
+ * Get template for persona type.
99
+ * Trigger types: 1=CHAT, 2=DASHBOARD, 4=VOICE
100
+ */
101
+ getTemplateForType(type) {
102
+ const triggerTypes = {
103
+ voice: 4,
104
+ chat: 1,
105
+ dashboard: 2,
106
+ };
107
+ return this.templatesByType.get(triggerTypes[type]);
108
+ }
109
+ isLoaded() {
110
+ return this.loaded;
111
+ }
112
+ }
113
+ // ─────────────────────────────────────────────────────────────────────────────
114
+ // Factory function (loads from API)
115
+ // ─────────────────────────────────────────────────────────────────────────────
116
+ /**
117
+ * Create and load action registry from API.
118
+ * Uses client methods directly - no caching here (resources.ts handles that for MCP).
119
+ */
120
+ export async function ensureActionRegistry(client) {
121
+ const registry = new ActionRegistry();
122
+ const [actions, templates] = await Promise.all([
123
+ client.listActions().catch(() => []),
124
+ client.getPersonaTemplates().catch(() => []),
125
+ ]);
126
+ registry.loadFromData(actions, templates);
127
+ return registry;
128
+ }