@ema.co/mcp-toolkit 2026.2.13 → 2026.2.23-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.
- package/.context/public/guides/ema-user-guide.md +12 -16
- package/.context/public/guides/mcp-tools-guide.md +203 -334
- package/dist/cli/index.js +2 -2
- package/dist/mcp/domain/loop-detection.js +89 -0
- package/dist/mcp/domain/sanitizer.js +1 -1
- package/dist/mcp/domain/structural-rules.js +4 -5
- package/dist/mcp/domain/validation-rules.js +5 -5
- package/dist/mcp/domain/workflow-graph.js +3 -5
- package/dist/mcp/domain/workflow-path-enumerator.js +7 -4
- package/dist/mcp/guidance.js +62 -29
- package/dist/mcp/handlers/debug/adapter.js +15 -0
- package/dist/mcp/handlers/debug/formatters.js +282 -0
- package/dist/mcp/handlers/debug/index.js +133 -0
- package/dist/mcp/handlers/demo/adapter.js +180 -0
- package/dist/mcp/handlers/env/config.js +2 -2
- package/dist/mcp/handlers/feedback/index.js +1 -1
- package/dist/mcp/handlers/index.js +0 -1
- package/dist/mcp/handlers/persona/adapter.js +135 -0
- package/dist/mcp/handlers/persona/index.js +237 -8
- package/dist/mcp/handlers/persona/schema.js +27 -0
- package/dist/mcp/handlers/reference/index.js +6 -4
- package/dist/mcp/handlers/sync/adapter.js +200 -0
- package/dist/mcp/handlers/workflow/adapter.js +174 -0
- package/dist/mcp/handlers/workflow/fix.js +11 -12
- package/dist/mcp/handlers/workflow/index.js +12 -40
- package/dist/mcp/handlers/workflow/validation.js +1 -1
- package/dist/mcp/knowledge-guidance-topics.js +615 -0
- package/dist/mcp/knowledge-types.js +7 -0
- package/dist/mcp/knowledge.js +75 -1403
- package/dist/mcp/resources-dynamic.js +2395 -0
- package/dist/mcp/resources-validation.js +408 -0
- package/dist/mcp/resources.js +72 -2508
- package/dist/mcp/server.js +69 -2825
- package/dist/mcp/tools.js +106 -5
- package/dist/sdk/client-adapter.js +265 -24
- package/dist/sdk/ema-client.js +100 -9
- package/dist/sdk/generated/agent-catalog.js +615 -0
- package/dist/sdk/generated/api-client/client/client.gen.js +3 -3
- package/dist/sdk/generated/api-client/client/index.js +5 -5
- package/dist/sdk/generated/api-client/client/utils.gen.js +4 -4
- package/dist/sdk/generated/api-client/client.gen.js +1 -1
- package/dist/sdk/generated/api-client/core/utils.gen.js +1 -1
- package/dist/sdk/generated/api-client/index.js +1 -1
- package/dist/sdk/generated/api-client/sdk.gen.js +2 -2
- package/dist/sdk/generated/well-known-types.js +99 -0
- package/dist/sdk/generated/widget-catalog.js +60 -0
- package/dist/sdk/grpc-client.js +115 -1
- package/dist/sync/sdk.js +2 -2
- package/dist/sync.js +4 -3
- package/docs/README.md +17 -9
- package/package.json +4 -3
- package/.context/public/guides/dashboard-operations.md +0 -349
- package/.context/public/guides/email-patterns.md +0 -125
- package/.context/public/guides/workflow-builder-patterns.md +0 -708
- package/dist/mcp/domain/intent-architect.js +0 -914
- package/dist/mcp/domain/quality-gates.js +0 -110
- package/dist/mcp/domain/workflow-execution-analyzer.js +0 -412
- package/dist/mcp/domain/workflow-intent.js +0 -1806
- package/dist/mcp/domain/workflow-merge.js +0 -449
- package/dist/mcp/domain/workflow-tracer.js +0 -648
- package/dist/mcp/domain/workflow-transformer.js +0 -742
- package/dist/mcp/handlers/knowledge/index.js +0 -54
- package/dist/mcp/handlers/persona/intent.js +0 -141
- package/dist/mcp/handlers/workflow/analyze.js +0 -119
- package/dist/mcp/handlers/workflow/compare.js +0 -70
- package/dist/mcp/handlers/workflow/generate.js +0 -384
- package/dist/mcp/handlers-consolidated.js +0 -333
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persona V2 Adapter
|
|
3
|
+
*
|
|
4
|
+
* Converts the V2 method-based persona tool args into the internal
|
|
5
|
+
* mode-based format expected by handlePersona, routes data sub-resource
|
|
6
|
+
* operations directly to handleData, and executes action composition
|
|
7
|
+
* post-processing.
|
|
8
|
+
*
|
|
9
|
+
* Extracted from server.ts to keep the dispatch table thin.
|
|
10
|
+
*/
|
|
11
|
+
import { handlePersona } from "./index.js";
|
|
12
|
+
export async function handlePersonaAdapter(args, createClient, getDefaultEnvName) {
|
|
13
|
+
const targetEnv = args.env ?? getDefaultEnvName();
|
|
14
|
+
const client = createClient(targetEnv);
|
|
15
|
+
const versionContext = {
|
|
16
|
+
workspaceRoot: process.cwd(),
|
|
17
|
+
environment: targetEnv,
|
|
18
|
+
tenant_id: targetEnv,
|
|
19
|
+
};
|
|
20
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
21
|
+
// Debug Sub-Resource (intercepted before data)
|
|
22
|
+
//
|
|
23
|
+
// persona(id="abc", debug={method:"conversations"}) routes to handleDebug.
|
|
24
|
+
// Injects persona_id for methods that need it (conversations, search).
|
|
25
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
26
|
+
if (args.debug && typeof args.debug === "object") {
|
|
27
|
+
const debug = args.debug;
|
|
28
|
+
const personaId = args.id;
|
|
29
|
+
if (typeof debug.method === "string") {
|
|
30
|
+
const { handleDebugAdapter } = await import("../debug/adapter.js");
|
|
31
|
+
return handleDebugAdapter({ ...debug, persona_id: debug.persona_id ?? personaId, env: args.env }, createClient, getDefaultEnvName);
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
error: "Missing debug.method",
|
|
35
|
+
hint: "Use debug={method:'conversations'} format",
|
|
36
|
+
available_methods: ["conversations", "conversation_detail", "show_work", "action_detail", "search"],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
40
|
+
// Data Sub-Resource (intercepted after debug — bypasses persona routing entirely)
|
|
41
|
+
//
|
|
42
|
+
// persona(id="abc", data={method:"list"}) routes directly to handleData.
|
|
43
|
+
// This avoids data operations flowing through handlePersona's routing.
|
|
44
|
+
// Direct callers of handlePersona (e.g. tests) still use its own data routing.
|
|
45
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
46
|
+
if (args.data && typeof args.data === "object") {
|
|
47
|
+
const data = args.data;
|
|
48
|
+
const personaId = args.id;
|
|
49
|
+
if (typeof data.method === "string") {
|
|
50
|
+
const { handleData: handleDataDirect } = await import("../data/index.js");
|
|
51
|
+
return handleDataDirect({ persona_id: personaId, env: args.env, data }, client);
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
error: "Missing data.method",
|
|
55
|
+
hint: "Use data={method:'list'} format",
|
|
56
|
+
available_methods: ["list", "stats", "upload", "copy", "replicate", "delete", "search", "refresh", "regenerate", "replace"],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
60
|
+
// V2 Parameter Transformation
|
|
61
|
+
// Convert v2 structure to v1 mode-based structure for handler compatibility
|
|
62
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
63
|
+
const transformedArgs = { ...args };
|
|
64
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
65
|
+
// Method → Mode mapping
|
|
66
|
+
// persona(method="create|get|list|update|delete|sanitize|snapshot|history|restore")
|
|
67
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
68
|
+
if (args.method) {
|
|
69
|
+
const method = String(args.method);
|
|
70
|
+
if (method === "analyze" || method === "compare") {
|
|
71
|
+
return {
|
|
72
|
+
error: `method="${method}" is removed. Use persona(method="get", id="...") and analyze/compare the results yourself.`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const methodToMode = {
|
|
76
|
+
create: "create",
|
|
77
|
+
get: "get",
|
|
78
|
+
list: "list",
|
|
79
|
+
update: "update",
|
|
80
|
+
delete: "delete",
|
|
81
|
+
sanitize: "sanitize",
|
|
82
|
+
schema: "schema",
|
|
83
|
+
clone: "create",
|
|
84
|
+
snapshot: "version_create",
|
|
85
|
+
history: "version_list",
|
|
86
|
+
restore: "version_restore",
|
|
87
|
+
};
|
|
88
|
+
const mode = methodToMode[method];
|
|
89
|
+
if (!mode) {
|
|
90
|
+
return { error: `Unknown method: ${method}`, valid_methods: Object.keys(methodToMode) };
|
|
91
|
+
}
|
|
92
|
+
transformedArgs.mode = mode;
|
|
93
|
+
delete transformedArgs.method;
|
|
94
|
+
if (args.actions && Array.isArray(args.actions)) {
|
|
95
|
+
transformedArgs._actions = args.actions;
|
|
96
|
+
delete transformedArgs.actions;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else if (args.id) {
|
|
100
|
+
transformedArgs.mode = "get";
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
transformedArgs.mode = "list";
|
|
104
|
+
}
|
|
105
|
+
const result = await handlePersona(transformedArgs, client, () => undefined, (env) => createClient(env), versionContext);
|
|
106
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
107
|
+
// Action Composition Post-Processing
|
|
108
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
109
|
+
const actions = transformedArgs._actions;
|
|
110
|
+
if (actions && actions.length > 0) {
|
|
111
|
+
const resultObj = result;
|
|
112
|
+
const targetId = resultObj.id ??
|
|
113
|
+
resultObj.persona_id;
|
|
114
|
+
const sourceId = args.from;
|
|
115
|
+
if (!targetId) {
|
|
116
|
+
return {
|
|
117
|
+
...(typeof result === "object" && result !== null ? result : {}),
|
|
118
|
+
_actions_error: "No persona ID available for action execution",
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const actionExecutor = await import("../action-executor.js");
|
|
122
|
+
const context = {
|
|
123
|
+
source: sourceId,
|
|
124
|
+
target: targetId,
|
|
125
|
+
env: targetEnv,
|
|
126
|
+
originalArgs: args,
|
|
127
|
+
};
|
|
128
|
+
const actionsResult = await actionExecutor.executeActions(actions, context, client);
|
|
129
|
+
return {
|
|
130
|
+
...(typeof result === "object" && result !== null ? result : {}),
|
|
131
|
+
_actions: actionsResult,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Persona Handler - Dispatch Table
|
|
2
|
+
* Persona Handler - Dispatch Table + Top-Level Router
|
|
3
3
|
*
|
|
4
4
|
* This module exports persona mode handlers using a dispatch table
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* and provides the top-level `handlePersona` function that was previously
|
|
6
|
+
* (previously in the now-deleted handlers-consolidated.ts).
|
|
7
7
|
*
|
|
8
8
|
* Persona modes:
|
|
9
9
|
* - get: Fetch single persona (LLM then analyzes the data)
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - update: Update persona config
|
|
14
14
|
* - delete: Delete persona (with confirmation)
|
|
15
15
|
* - create/clone: Create new persona from template or clone existing
|
|
16
|
-
* -
|
|
16
|
+
* - schema: Get persona input schema (dashboard columns, types)
|
|
17
17
|
*
|
|
18
18
|
* REMOVED (LLM does these):
|
|
19
19
|
* - analyze: LLM analyzes data from 'get'
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
* - snapshot/version_create, history/version_list, version_get
|
|
24
24
|
* - version_compare, restore/version_restore, version_policy
|
|
25
25
|
*/
|
|
26
|
+
import { resolvePersona } from "../utils.js";
|
|
27
|
+
import { checkDeprecatedParams, addDeprecationWarnings } from "../deprecation.js";
|
|
28
|
+
import { handleData } from "../data/index.js";
|
|
29
|
+
import { handleWorkflow } from "../workflow/index.js";
|
|
26
30
|
// Import mode handlers
|
|
27
31
|
import { handleGet } from "./get.js";
|
|
28
32
|
import { handleList } from "./list.js";
|
|
@@ -31,7 +35,7 @@ import { handleSanitize } from "./sanitize.js";
|
|
|
31
35
|
import { handleUpdate } from "./update.js";
|
|
32
36
|
import { handleDelete } from "./delete.js";
|
|
33
37
|
import { handleCreate } from "./create.js";
|
|
34
|
-
import {
|
|
38
|
+
import { handleSchema } from "./schema.js";
|
|
35
39
|
/**
|
|
36
40
|
* Dispatch table for persona modes
|
|
37
41
|
*
|
|
@@ -48,7 +52,7 @@ export const PERSONA_MODE_HANDLERS = {
|
|
|
48
52
|
delete: handleDelete,
|
|
49
53
|
create: handleCreate,
|
|
50
54
|
clone: handleCreate, // Clone uses same handler as create
|
|
51
|
-
|
|
55
|
+
schema: handleSchema,
|
|
52
56
|
};
|
|
53
57
|
/**
|
|
54
58
|
* Check if a mode has been extracted to this module
|
|
@@ -70,7 +74,232 @@ export { handleSanitize } from "./sanitize.js";
|
|
|
70
74
|
export { handleUpdate } from "./update.js";
|
|
71
75
|
export { handleDelete } from "./delete.js";
|
|
72
76
|
export { handleCreate } from "./create.js";
|
|
73
|
-
export {
|
|
74
|
-
// REMOVED: handleCompare, handleAnalyze - LLM does analysis/comparison
|
|
77
|
+
export { handleSchema } from "./schema.js";
|
|
75
78
|
// Version management
|
|
76
79
|
export { handleVersion, isVersionMode } from "./version.js";
|
|
80
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
81
|
+
// Top-level Persona Router
|
|
82
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
83
|
+
import { handleVersion } from "./version.js";
|
|
84
|
+
/**
|
|
85
|
+
* Top-level persona handler that routes to mode-specific handlers.
|
|
86
|
+
*
|
|
87
|
+
* Handles:
|
|
88
|
+
* - Data sub-resource routing (data={method:...})
|
|
89
|
+
* - Legacy workflow routing (optimize flag, workflow_def without update mode)
|
|
90
|
+
* - Mode resolution and dispatch
|
|
91
|
+
* - Version management modes
|
|
92
|
+
*/
|
|
93
|
+
export async function handlePersona(args, client, getTemplateId, createClientForEnv, versionContext) {
|
|
94
|
+
const deprecationWarnings = checkDeprecatedParams(args);
|
|
95
|
+
for (const warning of deprecationWarnings) {
|
|
96
|
+
console.warn(`[persona] Deprecation: ${warning}`);
|
|
97
|
+
}
|
|
98
|
+
const id = args.id;
|
|
99
|
+
const identifier = args.identifier;
|
|
100
|
+
const idOrName = id ?? identifier;
|
|
101
|
+
// Support both 'method' (new) and 'mode' (legacy) - method takes precedence
|
|
102
|
+
const method = args.method;
|
|
103
|
+
const mode = method ?? args.mode;
|
|
104
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
105
|
+
// DATA SUB-RESOURCE: Handle data={method:...} early
|
|
106
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
107
|
+
const dataArg = args.data;
|
|
108
|
+
if (dataArg && typeof dataArg === "object") {
|
|
109
|
+
if (!idOrName) {
|
|
110
|
+
return {
|
|
111
|
+
error: "Data operations require persona id",
|
|
112
|
+
hint: "Use persona(id=\"abc\", data={method:\"list\"})",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const dataMethod = dataArg.method;
|
|
116
|
+
if (!dataMethod) {
|
|
117
|
+
return {
|
|
118
|
+
error: "Data operations require explicit method",
|
|
119
|
+
hint: "Use data={method:\"list|schema|upload|copy|delete|embed|search\", ...}",
|
|
120
|
+
valid_methods: ["list", "schema", "upload", "copy", "delete", "embed", "search"],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const methodToMode = {
|
|
124
|
+
"list": "list",
|
|
125
|
+
"schema": "schema",
|
|
126
|
+
"upload": "upload",
|
|
127
|
+
"copy": "dashboard_clone",
|
|
128
|
+
"delete": "delete",
|
|
129
|
+
"embed": "embed",
|
|
130
|
+
"search": "search",
|
|
131
|
+
};
|
|
132
|
+
const mappedMode = methodToMode[dataMethod];
|
|
133
|
+
if (!mappedMode) {
|
|
134
|
+
return {
|
|
135
|
+
error: `Unknown data method: ${dataMethod}`,
|
|
136
|
+
valid_methods: Object.keys(methodToMode),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const dataArgs = {
|
|
140
|
+
persona_id: idOrName,
|
|
141
|
+
mode: mappedMode,
|
|
142
|
+
};
|
|
143
|
+
if (dataArg.from)
|
|
144
|
+
dataArgs.source_persona_id = dataArg.from;
|
|
145
|
+
if (dataArg.sanitize !== undefined)
|
|
146
|
+
dataArgs.sanitize = dataArg.sanitize;
|
|
147
|
+
if (dataArg.path)
|
|
148
|
+
dataArgs.file_path = dataArg.path;
|
|
149
|
+
if (dataArg.content)
|
|
150
|
+
dataArgs.content = dataArg.content;
|
|
151
|
+
if (dataArg.file_id)
|
|
152
|
+
dataArgs.file_id = dataArg.file_id;
|
|
153
|
+
else if (dataArg.id)
|
|
154
|
+
dataArgs.file_id = dataArg.id;
|
|
155
|
+
if (dataArg.enabled !== undefined)
|
|
156
|
+
dataArgs.embed = dataArg.enabled;
|
|
157
|
+
if (dataArg.query)
|
|
158
|
+
dataArgs.query = dataArg.query;
|
|
159
|
+
const readFile = async (path) => {
|
|
160
|
+
const fs = await import("fs/promises");
|
|
161
|
+
return fs.readFile(path);
|
|
162
|
+
};
|
|
163
|
+
return handleData(dataArgs, client, readFile);
|
|
164
|
+
}
|
|
165
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
166
|
+
// ROUTING: Persona operations
|
|
167
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
168
|
+
// LLM-DRIVEN ARCHITECTURE:
|
|
169
|
+
// - persona tool: config changes only (name, description, widgets)
|
|
170
|
+
// - workflow tool: ALL workflow changes (get → modify → deploy)
|
|
171
|
+
// mode="update" → modular handleUpdate (config changes, workflow_spec for rewire/remove only)
|
|
172
|
+
if (mode === "update" && idOrName) {
|
|
173
|
+
return getPersonaModeHandler("update")(args, client);
|
|
174
|
+
}
|
|
175
|
+
// Legacy: optimize flag routes to workflow handler
|
|
176
|
+
const optimize = args.optimize;
|
|
177
|
+
const workflowDef = args.workflow_def ?? args.workflow;
|
|
178
|
+
if (optimize || (idOrName && workflowDef && mode !== "update")) {
|
|
179
|
+
const workflowArgs = {
|
|
180
|
+
...args,
|
|
181
|
+
persona_id: idOrName,
|
|
182
|
+
};
|
|
183
|
+
delete workflowArgs.id;
|
|
184
|
+
delete workflowArgs.identifier;
|
|
185
|
+
return handleWorkflow(workflowArgs, client, getTemplateId);
|
|
186
|
+
}
|
|
187
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
188
|
+
// Standard persona operations (get, list, compare, version management)
|
|
189
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
190
|
+
const fromParam = args.from;
|
|
191
|
+
const cloneFromParam = args.clone_from;
|
|
192
|
+
const templateIdParam = args.template_id;
|
|
193
|
+
const hasBase = fromParam || cloneFromParam || templateIdParam;
|
|
194
|
+
let effectiveMode = mode;
|
|
195
|
+
if (!effectiveMode) {
|
|
196
|
+
const availableMethods = ["list", "get", "create", "update", "delete", "analyze", "sanitize", "snapshot", "history", "restore", "compare"];
|
|
197
|
+
if (idOrName && args.proto_config) {
|
|
198
|
+
return {
|
|
199
|
+
error: "Explicit method required",
|
|
200
|
+
message: "You provided id and proto_config. Did you want to update this persona?",
|
|
201
|
+
suggested_method: "update",
|
|
202
|
+
hint: "Use method='update' to apply changes",
|
|
203
|
+
example: `persona(method="update", id="${idOrName}", config={...})`,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
else if (idOrName) {
|
|
207
|
+
return {
|
|
208
|
+
error: "Explicit method required",
|
|
209
|
+
message: "You provided a persona id/name. What operation would you like to perform?",
|
|
210
|
+
valid_methods: ["get", "update", "delete", "sanitize", "snapshot", "history", "restore"],
|
|
211
|
+
hint: "LLM does analysis/comparison. Use method='get' to fetch data, then reason about it.",
|
|
212
|
+
example: `persona(method="get", id="${idOrName}")`,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
else if (hasBase || (args.name && args.type)) {
|
|
216
|
+
return {
|
|
217
|
+
error: "Explicit method required",
|
|
218
|
+
message: "You provided creation parameters. Did you want to create a new persona?",
|
|
219
|
+
suggested_method: "create",
|
|
220
|
+
hint: "Use method='create'",
|
|
221
|
+
example: `persona(method="create", name="...", type="voice")`,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
return {
|
|
226
|
+
error: "Explicit method required",
|
|
227
|
+
message: "All persona operations require explicit method parameter",
|
|
228
|
+
valid_methods: availableMethods,
|
|
229
|
+
examples: [
|
|
230
|
+
'persona(method="list") - list all personas',
|
|
231
|
+
'persona(method="get", id="...") - get specific persona',
|
|
232
|
+
'persona(method="create", name="...", type="voice") - create new',
|
|
233
|
+
'persona(method="update", id="...", config={...}) - update config',
|
|
234
|
+
'persona(method="delete", id="...", confirm=true) - delete',
|
|
235
|
+
],
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
240
|
+
// Dispatch to handlers via dispatch table
|
|
241
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
242
|
+
if (hasExtractedHandler(effectiveMode)) {
|
|
243
|
+
const handler = getPersonaModeHandler(effectiveMode);
|
|
244
|
+
if (handler) {
|
|
245
|
+
let extraContext = undefined;
|
|
246
|
+
if (effectiveMode === "create" || effectiveMode === "clone") {
|
|
247
|
+
extraContext = getTemplateId;
|
|
248
|
+
}
|
|
249
|
+
else if (effectiveMode === "compare") {
|
|
250
|
+
extraContext = createClientForEnv;
|
|
251
|
+
}
|
|
252
|
+
const result = await handler(args, client, extraContext);
|
|
253
|
+
return addDeprecationWarnings(result, deprecationWarnings);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
switch (effectiveMode) {
|
|
257
|
+
// Fallback cases for modes already in dispatch table (should not be reached)
|
|
258
|
+
case "get":
|
|
259
|
+
case "list":
|
|
260
|
+
case "templates":
|
|
261
|
+
case "sanitize":
|
|
262
|
+
case "update":
|
|
263
|
+
case "delete": {
|
|
264
|
+
return { error: `Mode "${effectiveMode}" should be handled by extracted handler` };
|
|
265
|
+
}
|
|
266
|
+
// Clone and create - handled by dispatch table
|
|
267
|
+
case "clone":
|
|
268
|
+
case "create": {
|
|
269
|
+
return { error: `Mode "${effectiveMode}" should be handled by extracted handler` };
|
|
270
|
+
}
|
|
271
|
+
// Analyze and compare - LLM does this, not MCP
|
|
272
|
+
case "analyze":
|
|
273
|
+
case "compare": {
|
|
274
|
+
return {
|
|
275
|
+
error: `Method "${effectiveMode}" removed - LLM does analysis/comparison`,
|
|
276
|
+
hint: "Use method='get' to fetch persona data, then do your own analysis/comparison.",
|
|
277
|
+
example: `persona(method="get", id="...", include_workflow=true)`,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
// ─────────────── Version Management Modes ───────────────
|
|
281
|
+
case "snapshot":
|
|
282
|
+
case "history":
|
|
283
|
+
case "restore":
|
|
284
|
+
case "version_create":
|
|
285
|
+
case "version_list":
|
|
286
|
+
case "version_get":
|
|
287
|
+
case "version_compare":
|
|
288
|
+
case "version_restore":
|
|
289
|
+
case "version_policy": {
|
|
290
|
+
if (!idOrName) {
|
|
291
|
+
return { error: `id required for ${effectiveMode} mode` };
|
|
292
|
+
}
|
|
293
|
+
if (!versionContext) {
|
|
294
|
+
return { error: "Version tracking not configured. Provide workspaceRoot in context." };
|
|
295
|
+
}
|
|
296
|
+
const persona = await resolvePersona(client, idOrName);
|
|
297
|
+
if (!persona) {
|
|
298
|
+
return { error: `Persona not found: ${idOrName}` };
|
|
299
|
+
}
|
|
300
|
+
return handleVersion(effectiveMode, args, client, persona, versionContext);
|
|
301
|
+
}
|
|
302
|
+
default:
|
|
303
|
+
return { error: `Unknown mode: ${effectiveMode}` };
|
|
304
|
+
}
|
|
305
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema Handler - Get persona input schema (dashboard columns, types)
|
|
3
|
+
*/
|
|
4
|
+
import { resolvePersona } from "../utils.js";
|
|
5
|
+
export async function handleSchema(args, client) {
|
|
6
|
+
const id = (args.id ?? args.identifier);
|
|
7
|
+
if (!id) {
|
|
8
|
+
return { error: "id required for schema mode" };
|
|
9
|
+
}
|
|
10
|
+
const persona = await resolvePersona(client, id);
|
|
11
|
+
if (!persona) {
|
|
12
|
+
return { error: `Persona not found: ${id}` };
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const schema = await client.getDashboardSchema(persona.id, persona.id);
|
|
16
|
+
return {
|
|
17
|
+
mode: "schema",
|
|
18
|
+
persona_id: persona.id,
|
|
19
|
+
persona_name: persona.name,
|
|
20
|
+
schema: schema,
|
|
21
|
+
column_count: schema.columns?.length ?? 0,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return { error: `Failed to get schema: ${error instanceof Error ? error.message : String(error)}` };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Unified reference tool for envs, actions, templates, patterns, concepts, guidance.
|
|
5
5
|
*/
|
|
6
6
|
import { errorResult } from "../types.js";
|
|
7
|
-
import { AGENT_CATALOG, WORKFLOW_PATTERNS, QUALIFYING_QUESTIONS, PLATFORM_CONCEPTS, WORKFLOW_EXECUTION_MODEL, COMMON_MISTAKES, DEBUG_CHECKLIST, GUIDANCE_TOPICS, getAgentByName, getWidgetsForPersonaType, checkTypeCompatibility, getQualifyingQuestionsByCategory, getRequiredQualifyingQuestions, getConceptByTerm, suggestAgentsForUseCase,
|
|
7
|
+
import { AGENT_CATALOG, WORKFLOW_PATTERNS, QUALIFYING_QUESTIONS, PLATFORM_CONCEPTS, WORKFLOW_EXECUTION_MODEL, COMMON_MISTAKES, DEBUG_CHECKLIST, GUIDANCE_TOPICS, getAgentByName, getWidgetsForPersonaType, checkTypeCompatibility, getQualifyingQuestionsByCategory, getRequiredQualifyingQuestions, getConceptByTerm, suggestAgentsForUseCase, } from "../../knowledge.js";
|
|
8
8
|
/**
|
|
9
9
|
* Handle reference tool requests - unified lookup for all reference data
|
|
10
10
|
*/
|
|
@@ -284,10 +284,12 @@ export async function handleReference(args, context) {
|
|
|
284
284
|
note: result?.note,
|
|
285
285
|
};
|
|
286
286
|
}
|
|
287
|
-
// Validate prompt
|
|
287
|
+
// Validate prompt (deprecated — LLM responsibility)
|
|
288
288
|
if (args.validate_prompt) {
|
|
289
|
-
|
|
290
|
-
|
|
289
|
+
return {
|
|
290
|
+
error: "Workflow prompt validation is the LLM's responsibility. Use workflow(mode='validate') for structural validation.",
|
|
291
|
+
_deprecated: true,
|
|
292
|
+
};
|
|
291
293
|
}
|
|
292
294
|
// ─────────────────────────────────────────────────────────────────────────
|
|
293
295
|
// type="demo_kits" or demo_kits=true - List demo kits
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync V2 Adapter
|
|
3
|
+
*
|
|
4
|
+
* Contains syncRunImpl / syncInfoImpl implementation functions and the
|
|
5
|
+
* top-level sync tool handler that routes method ↔ mode and normalises arg
|
|
6
|
+
* names between the V2 tool schema and the internal sync implementation.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from server.ts to keep the dispatch table thin.
|
|
9
|
+
*/
|
|
10
|
+
import { resolveSyncBehavior, loadSyncOptions } from "../../../sync/sync-options.js";
|
|
11
|
+
import { directSyncPersona, directSyncPersonaById, directSyncAll } from "./direct.js";
|
|
12
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
13
|
+
// Implementation helpers
|
|
14
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
15
|
+
async function syncRunImpl(args, getDefaultEnvName, getSyncSDK) {
|
|
16
|
+
const targetEnv = String(args.target_env);
|
|
17
|
+
const sourceEnv = args.source_env ? String(args.source_env) : getDefaultEnvName();
|
|
18
|
+
const dryRun = args.dry_run === true;
|
|
19
|
+
const includeStatus = args.include_status === true;
|
|
20
|
+
const scope = args.scope === "all" ? "all" : "one";
|
|
21
|
+
const identifier = args.identifier ? String(args.identifier) : undefined;
|
|
22
|
+
if (scope === "all" || !identifier) {
|
|
23
|
+
const sdk = getSyncSDK();
|
|
24
|
+
if (sdk) {
|
|
25
|
+
try {
|
|
26
|
+
const result = await sdk.runSync();
|
|
27
|
+
return { success: true, mode: "config", ...result };
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
sdk.close();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const result = await directSyncAll({ targetEnv, dryRun });
|
|
35
|
+
return { success: true, mode: "tags", ...result };
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
return { success: false, error: e instanceof Error ? e.message : String(e) };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(identifier);
|
|
42
|
+
const behavior = resolveSyncBehavior({
|
|
43
|
+
personaName: isUUID ? undefined : identifier,
|
|
44
|
+
targetEnv,
|
|
45
|
+
overrides: {
|
|
46
|
+
dry_run: dryRun ? true : undefined,
|
|
47
|
+
sync_status: includeStatus ? true : undefined,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
try {
|
|
51
|
+
const result = isUUID
|
|
52
|
+
? await directSyncPersonaById({
|
|
53
|
+
personaId: identifier,
|
|
54
|
+
sourceEnv,
|
|
55
|
+
targetEnv,
|
|
56
|
+
dryRun: behavior.dry_run,
|
|
57
|
+
syncStatus: behavior.sync_status,
|
|
58
|
+
})
|
|
59
|
+
: await directSyncPersona({
|
|
60
|
+
name: identifier,
|
|
61
|
+
sourceEnv,
|
|
62
|
+
targetEnv,
|
|
63
|
+
dryRun: behavior.dry_run,
|
|
64
|
+
syncStatus: behavior.sync_status,
|
|
65
|
+
});
|
|
66
|
+
return { ...result, resolved_behavior: behavior };
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
return { success: false, error: e instanceof Error ? e.message : String(e) };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function syncInfoImpl(args, createClient, getSyncSDK) {
|
|
73
|
+
const client = args.env ? createClient(args.env) : undefined;
|
|
74
|
+
if (args.persona_id) {
|
|
75
|
+
if (!client)
|
|
76
|
+
throw new Error("env required when checking persona sync status");
|
|
77
|
+
const personaId = String(args.persona_id);
|
|
78
|
+
const personas = await client.getPersonasForTenant();
|
|
79
|
+
const persona = personas.find((p) => p.id === personaId);
|
|
80
|
+
if (!persona)
|
|
81
|
+
throw new Error(`AI Employee not found: ${personaId}`);
|
|
82
|
+
const meta = client.getSyncMetadata(persona);
|
|
83
|
+
return {
|
|
84
|
+
environment: client["env"].name,
|
|
85
|
+
persona_id: personaId,
|
|
86
|
+
persona_name: persona.name,
|
|
87
|
+
is_synced: !!meta,
|
|
88
|
+
sync_metadata: meta,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (args.persona_name) {
|
|
92
|
+
const sdk = getSyncSDK();
|
|
93
|
+
if (!sdk)
|
|
94
|
+
return { error: "No sync config found. Set EMA_AGENT_SYNC_CONFIG." };
|
|
95
|
+
try {
|
|
96
|
+
const persona = await sdk.getMasterPersonaByName(String(args.persona_name));
|
|
97
|
+
if (!persona)
|
|
98
|
+
return { error: `Persona not found: ${args.persona_name}` };
|
|
99
|
+
return await sdk.getPersonaSyncStatus(persona.id);
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
sdk.close();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (args.list_synced === true) {
|
|
106
|
+
if (!client)
|
|
107
|
+
throw new Error("env required when listing synced personas");
|
|
108
|
+
const personas = await client.getPersonasForTenant();
|
|
109
|
+
const masterEnvFilter = args.master_env ? String(args.master_env).toLowerCase() : undefined;
|
|
110
|
+
const synced = [];
|
|
111
|
+
for (const p of personas) {
|
|
112
|
+
const meta = client.getSyncMetadata(p);
|
|
113
|
+
if (meta) {
|
|
114
|
+
if (masterEnvFilter && meta.master_env.toLowerCase() !== masterEnvFilter)
|
|
115
|
+
continue;
|
|
116
|
+
synced.push({ persona_id: p.id, persona_name: p.name, sync_metadata: meta });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return { environment: client["env"].name, count: synced.length, synced_personas: synced };
|
|
120
|
+
}
|
|
121
|
+
const sdk = getSyncSDK();
|
|
122
|
+
const options = args.include_options === true ? loadSyncOptions() : undefined;
|
|
123
|
+
if (!sdk) {
|
|
124
|
+
return {
|
|
125
|
+
configured: false,
|
|
126
|
+
error: "No sync config found. Set EMA_AGENT_SYNC_CONFIG.",
|
|
127
|
+
options,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
const master = sdk.getMasterEnvironment();
|
|
132
|
+
const envs = sdk.getEnvironments();
|
|
133
|
+
const personas = await sdk.listMasterPersonas();
|
|
134
|
+
return {
|
|
135
|
+
configured: true,
|
|
136
|
+
master_environment: { name: master.name, url: master.baseUrl },
|
|
137
|
+
target_environments: envs.filter((e) => !e.isMaster).map((e) => ({ name: e.name, url: e.baseUrl })),
|
|
138
|
+
total_personas: personas.length,
|
|
139
|
+
options,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
sdk.close();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
147
|
+
// Top-level sync tool handler
|
|
148
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
149
|
+
export async function handleSyncAdapter(args, createClient, getDefaultEnvName, getSyncSDK) {
|
|
150
|
+
const normalizedArgs = { ...(args ?? {}) };
|
|
151
|
+
const rawMethod = normalizedArgs.method ? String(normalizedArgs.method) : normalizedArgs.mode ? String(normalizedArgs.mode) : "run";
|
|
152
|
+
const mode = rawMethod === "preview" ? "run" : rawMethod === "execute" ? "run" : rawMethod;
|
|
153
|
+
if (rawMethod === "preview") {
|
|
154
|
+
normalizedArgs.dry_run = true;
|
|
155
|
+
}
|
|
156
|
+
const target = (normalizedArgs.target ?? normalizedArgs.target_env);
|
|
157
|
+
const source = (normalizedArgs.source ?? normalizedArgs.source_env);
|
|
158
|
+
const id = normalizedArgs.id;
|
|
159
|
+
const identifier = normalizedArgs.identifier;
|
|
160
|
+
const idOrIdentifier = id ?? identifier;
|
|
161
|
+
if (mode === "config") {
|
|
162
|
+
return syncInfoImpl({ include_options: true }, createClient, getSyncSDK);
|
|
163
|
+
}
|
|
164
|
+
if (mode === "status") {
|
|
165
|
+
const env = normalizedArgs.env;
|
|
166
|
+
if (normalizedArgs.list_synced === true) {
|
|
167
|
+
if (!env)
|
|
168
|
+
throw new Error('env is required for sync(mode="status", list_synced=true)');
|
|
169
|
+
return syncInfoImpl({ list_synced: true, master_env: normalizedArgs.master_env, env }, createClient, getSyncSDK);
|
|
170
|
+
}
|
|
171
|
+
if (idOrIdentifier) {
|
|
172
|
+
if (!env)
|
|
173
|
+
throw new Error('env is required for sync(mode="status", id="...")');
|
|
174
|
+
const identifierToResolve = String(idOrIdentifier);
|
|
175
|
+
const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(identifierToResolve);
|
|
176
|
+
if (isUUID) {
|
|
177
|
+
return syncInfoImpl({ persona_id: identifierToResolve, env }, createClient, getSyncSDK);
|
|
178
|
+
}
|
|
179
|
+
const client = createClient(env);
|
|
180
|
+
const personas = await client.getPersonasForTenant();
|
|
181
|
+
const match = personas.find((p) => p.name === identifierToResolve);
|
|
182
|
+
if (!match)
|
|
183
|
+
throw new Error(`AI Employee not found by name in ${env}: ${identifierToResolve}`);
|
|
184
|
+
return syncInfoImpl({ persona_id: match.id, env }, createClient, getSyncSDK);
|
|
185
|
+
}
|
|
186
|
+
return syncInfoImpl({ include_options: normalizedArgs.include_options === true }, createClient, getSyncSDK);
|
|
187
|
+
}
|
|
188
|
+
// mode === "run" (default)
|
|
189
|
+
if (!target) {
|
|
190
|
+
throw new Error('target (or target_env) is required for sync(mode="run")');
|
|
191
|
+
}
|
|
192
|
+
return syncRunImpl({
|
|
193
|
+
identifier: idOrIdentifier,
|
|
194
|
+
target_env: target,
|
|
195
|
+
source_env: source,
|
|
196
|
+
scope: normalizedArgs.scope,
|
|
197
|
+
dry_run: normalizedArgs.dry_run,
|
|
198
|
+
include_status: normalizedArgs.include_status,
|
|
199
|
+
}, getDefaultEnvName, getSyncSDK);
|
|
200
|
+
}
|