@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.
- package/README.md +2 -2
- package/dist/mcp/handlers-consolidated.js +773 -25
- package/dist/mcp/resources.js +124 -0
- package/dist/mcp/server.js +13 -205
- package/dist/mcp/tools-consolidated.js +163 -103
- package/dist/sdk/action-registry.js +128 -0
- package/dist/sdk/action-schema-parser.js +379 -0
- package/dist/sdk/client.js +757 -90
- package/dist/sdk/generated/api-types.js +11 -0
- package/dist/sdk/index.js +59 -2
- package/dist/sdk/intent-architect.js +883 -0
- package/dist/sdk/knowledge.js +38 -8
- package/dist/sdk/quality-gates.js +386 -0
- package/dist/sdk/sanitizer.js +1121 -0
- package/dist/sdk/structural-rules.js +290 -0
- package/dist/sdk/workflow-generator.js +88 -34
- package/dist/sdk/workflow-intent.js +237 -24
- package/dist/sdk/workflow-optimizer.js +665 -0
- package/dist/sdk/workflow-tracer.js +648 -0
- package/dist/sdk/workflow-transformer.js +10 -0
- package/dist/sdk/workflow-validator.js +609 -0
- package/docs/local-generation.md +508 -0
- package/docs/mcp-flow-diagram.md +135 -0
- package/docs/mcp-tools-guide.md +196 -204
- package/docs/release-process.md +153 -0
- package/docs/tool-consolidation-proposal.md +166 -378
- package/package.json +8 -2
- package/resources/action-schema.json +5678 -0
- package/resources/config/gates.json +88 -0
- package/resources/config/gates.schema.json +77 -0
- package/resources/templates/auto-builder-rules.md +222 -0
- package/resources/templates/demo-scenarios/test-published-package.md +116 -0
|
@@ -42,142 +42,189 @@ export function generateConsolidatedTools(envNames, defaultEnv) {
|
|
|
42
42
|
// ═══════════════════════════════════════════════════════════════════════
|
|
43
43
|
{
|
|
44
44
|
name: "env",
|
|
45
|
-
description: "List available Ema environments.
|
|
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 (
|
|
49
|
+
// 2. PERSONA - Unified AI Employee management (create, modify, analyze, list)
|
|
50
50
|
// ═══════════════════════════════════════════════════════════════════════
|
|
51
51
|
{
|
|
52
52
|
name: "persona",
|
|
53
|
-
description: `
|
|
53
|
+
description: `Create, modify, analyze, or list AI Employees. ONE tool for everything.
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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
|
-
|
|
65
|
-
persona(mode="create", name="New Bot", type="voice")
|
|
108
|
+
## Simple vs Complex Workflows
|
|
66
109
|
|
|
67
|
-
**
|
|
68
|
-
persona(id="abc-123", mode="update", name="Renamed")
|
|
110
|
+
**Simple** (Q&A, search + respond): Deploys directly → \`status: "success"\`
|
|
69
111
|
|
|
70
|
-
**
|
|
71
|
-
|
|
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
|
-
|
|
74
|
-
persona(
|
|
117
|
+
For complex workflows, send the prompt to an LLM and deploy:
|
|
118
|
+
\`persona(workflow_def=<llm_response>, ...)\`
|
|
75
119
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
//
|
|
126
|
+
// === IDENTITY ===
|
|
85
127
|
id: {
|
|
86
128
|
type: "string",
|
|
87
|
-
description: "Persona ID (UUID) or exact name. Omit
|
|
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.
|
|
134
|
+
description: "DEPRECATED: use id.",
|
|
94
135
|
},
|
|
95
|
-
//
|
|
96
|
-
|
|
136
|
+
// === CREATE/MODIFY (the main way to use this tool) ===
|
|
137
|
+
input: {
|
|
97
138
|
type: "string",
|
|
98
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
//
|
|
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: "
|
|
176
|
+
description: "Override voice/chat settings. Usually auto-generated from input."
|
|
126
177
|
},
|
|
127
178
|
workflow: {
|
|
128
179
|
type: "object",
|
|
129
|
-
description: "
|
|
180
|
+
description: "Direct workflow JSON (advanced). Usually auto-generated."
|
|
130
181
|
},
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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 -
|
|
210
|
+
// 3. WORKFLOW - DEPRECATED: Use persona() instead
|
|
148
211
|
// ═══════════════════════════════════════════════════════════════════════
|
|
149
212
|
{
|
|
150
213
|
name: "workflow",
|
|
151
|
-
description:
|
|
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
|
-
##
|
|
216
|
+
## Migration Guide
|
|
164
217
|
|
|
165
|
-
|
|
218
|
+
OLD (deprecated):
|
|
219
|
+
workflow(input="...", type="voice", name="Bot", preview=false)
|
|
220
|
+
workflow(persona_id="abc", input="add HITL")
|
|
166
221
|
|
|
167
|
-
|
|
168
|
-
|
|
222
|
+
NEW (use this):
|
|
223
|
+
persona(input="...", type="voice", name="Bot", preview=false)
|
|
224
|
+
persona(id="abc", input="add HITL")
|
|
169
225
|
|
|
170
|
-
|
|
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
|
|
346
|
+
description: `Get qualifying questions and reference patterns.
|
|
300
347
|
|
|
301
|
-
|
|
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
|
-
**
|
|
307
|
-
template(
|
|
308
|
-
template(
|
|
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
|
-
|
|
311
|
-
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
template(
|
|
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
|
+
}
|