@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.

Files changed (67) hide show
  1. package/.context/public/guides/ema-user-guide.md +12 -16
  2. package/.context/public/guides/mcp-tools-guide.md +203 -334
  3. package/dist/cli/index.js +2 -2
  4. package/dist/mcp/domain/loop-detection.js +89 -0
  5. package/dist/mcp/domain/sanitizer.js +1 -1
  6. package/dist/mcp/domain/structural-rules.js +4 -5
  7. package/dist/mcp/domain/validation-rules.js +5 -5
  8. package/dist/mcp/domain/workflow-graph.js +3 -5
  9. package/dist/mcp/domain/workflow-path-enumerator.js +7 -4
  10. package/dist/mcp/guidance.js +62 -29
  11. package/dist/mcp/handlers/debug/adapter.js +15 -0
  12. package/dist/mcp/handlers/debug/formatters.js +282 -0
  13. package/dist/mcp/handlers/debug/index.js +133 -0
  14. package/dist/mcp/handlers/demo/adapter.js +180 -0
  15. package/dist/mcp/handlers/env/config.js +2 -2
  16. package/dist/mcp/handlers/feedback/index.js +1 -1
  17. package/dist/mcp/handlers/index.js +0 -1
  18. package/dist/mcp/handlers/persona/adapter.js +135 -0
  19. package/dist/mcp/handlers/persona/index.js +237 -8
  20. package/dist/mcp/handlers/persona/schema.js +27 -0
  21. package/dist/mcp/handlers/reference/index.js +6 -4
  22. package/dist/mcp/handlers/sync/adapter.js +200 -0
  23. package/dist/mcp/handlers/workflow/adapter.js +174 -0
  24. package/dist/mcp/handlers/workflow/fix.js +11 -12
  25. package/dist/mcp/handlers/workflow/index.js +12 -40
  26. package/dist/mcp/handlers/workflow/validation.js +1 -1
  27. package/dist/mcp/knowledge-guidance-topics.js +615 -0
  28. package/dist/mcp/knowledge-types.js +7 -0
  29. package/dist/mcp/knowledge.js +75 -1403
  30. package/dist/mcp/resources-dynamic.js +2395 -0
  31. package/dist/mcp/resources-validation.js +408 -0
  32. package/dist/mcp/resources.js +72 -2508
  33. package/dist/mcp/server.js +69 -2825
  34. package/dist/mcp/tools.js +106 -5
  35. package/dist/sdk/client-adapter.js +265 -24
  36. package/dist/sdk/ema-client.js +100 -9
  37. package/dist/sdk/generated/agent-catalog.js +615 -0
  38. package/dist/sdk/generated/api-client/client/client.gen.js +3 -3
  39. package/dist/sdk/generated/api-client/client/index.js +5 -5
  40. package/dist/sdk/generated/api-client/client/utils.gen.js +4 -4
  41. package/dist/sdk/generated/api-client/client.gen.js +1 -1
  42. package/dist/sdk/generated/api-client/core/utils.gen.js +1 -1
  43. package/dist/sdk/generated/api-client/index.js +1 -1
  44. package/dist/sdk/generated/api-client/sdk.gen.js +2 -2
  45. package/dist/sdk/generated/well-known-types.js +99 -0
  46. package/dist/sdk/generated/widget-catalog.js +60 -0
  47. package/dist/sdk/grpc-client.js +115 -1
  48. package/dist/sync/sdk.js +2 -2
  49. package/dist/sync.js +4 -3
  50. package/docs/README.md +17 -9
  51. package/package.json +4 -3
  52. package/.context/public/guides/dashboard-operations.md +0 -349
  53. package/.context/public/guides/email-patterns.md +0 -125
  54. package/.context/public/guides/workflow-builder-patterns.md +0 -708
  55. package/dist/mcp/domain/intent-architect.js +0 -914
  56. package/dist/mcp/domain/quality-gates.js +0 -110
  57. package/dist/mcp/domain/workflow-execution-analyzer.js +0 -412
  58. package/dist/mcp/domain/workflow-intent.js +0 -1806
  59. package/dist/mcp/domain/workflow-merge.js +0 -449
  60. package/dist/mcp/domain/workflow-tracer.js +0 -648
  61. package/dist/mcp/domain/workflow-transformer.js +0 -742
  62. package/dist/mcp/handlers/knowledge/index.js +0 -54
  63. package/dist/mcp/handlers/persona/intent.js +0 -141
  64. package/dist/mcp/handlers/workflow/analyze.js +0 -119
  65. package/dist/mcp/handlers/workflow/compare.js +0 -70
  66. package/dist/mcp/handlers/workflow/generate.js +0 -384
  67. 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 Pattern
2
+ * Persona Handler - Dispatch Table + Top-Level Router
3
3
  *
4
4
  * This module exports persona mode handlers using a dispatch table
5
- * instead of a giant switch statement. Each mode is in its own file
6
- * for better testability and maintainability.
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
- * - intent: Direct Intent Architect invocation for qualification
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 { handleIntent } from "./intent.js";
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
- intent: handleIntent,
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 { handleIntent } from "./intent.js";
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, validateWorkflowPrompt, } from "../../knowledge.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, } 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
- const result = validateWorkflowPrompt(args.validate_prompt);
290
- return result;
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
+ }