@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,282 @@
1
+ /**
2
+ * Debug Handler Formatters
3
+ *
4
+ * Transforms raw proto→JSON debug responses into concise, actionable summaries.
5
+ * Applies value truncation for large strings and adds _next_step hints
6
+ * to guide the agent through the drill-down flow.
7
+ */
8
+ const MAX_VALUE_LENGTH = 2000;
9
+ /** Action run status enum values from ShowWorkLog.ActionRunStatus */
10
+ const STATUS_LABELS = {
11
+ 0: "UNSPECIFIED",
12
+ 1: "SUCCESS",
13
+ 2: "ERRORED",
14
+ 3: "WARNING",
15
+ 4: "NOT_RUN",
16
+ 5: "PAUSED",
17
+ ACTION_RUN_STATUS_UNSPECIFIED: "UNSPECIFIED",
18
+ ACTION_RUN_STATUS_SUCCESS: "SUCCESS",
19
+ ACTION_RUN_STATUS_ERRORED: "ERRORED",
20
+ ACTION_RUN_STATUS_WARNING: "WARNING",
21
+ ACTION_RUN_STATUS_NOT_RUN: "NOT_RUN",
22
+ ACTION_RUN_STATUS_PAUSED: "PAUSED",
23
+ };
24
+ function truncateValue(value) {
25
+ if (typeof value === "string" && value.length > MAX_VALUE_LENGTH) {
26
+ return value.slice(0, MAX_VALUE_LENGTH) + `... [truncated, ${value.length} total chars]`;
27
+ }
28
+ if (Array.isArray(value)) {
29
+ return value.map(truncateValue);
30
+ }
31
+ if (value !== null && typeof value === "object") {
32
+ return truncateObject(value);
33
+ }
34
+ return value;
35
+ }
36
+ function truncateObject(obj) {
37
+ const result = {};
38
+ for (const [k, v] of Object.entries(obj)) {
39
+ result[k] = truncateValue(v);
40
+ }
41
+ return result;
42
+ }
43
+ function resolveStatus(raw) {
44
+ if (typeof raw === "number" || typeof raw === "string") {
45
+ return STATUS_LABELS[raw] ?? String(raw);
46
+ }
47
+ return "UNKNOWN";
48
+ }
49
+ // ─────────────────────────────────────────────────────────────────────────────
50
+ // conversations
51
+ // ─────────────────────────────────────────────────────────────────────────────
52
+ export function formatConversations(data, personaId) {
53
+ // toJson() serializes the response field as "items" (proto field name)
54
+ const reviews = data.items ?? data.conversationReviews ?? data.conversation_reviews;
55
+ const items = Array.isArray(reviews) ? reviews : [];
56
+ return {
57
+ persona_id: personaId,
58
+ total: items.length,
59
+ conversations: items.map((c) => ({
60
+ conversation_id: c.conversationId ?? c.conversation_id,
61
+ channel: c.channel,
62
+ resolution_status: c.resolutionStatus ?? c.resolution_status,
63
+ user_rating: c.userRating ?? c.user_rating,
64
+ message_count: c.messageCount ?? c.message_count,
65
+ created_at: c.createdAt ?? c.created_at,
66
+ last_message_at: c.lastMessageAt ?? c.last_message_at,
67
+ user_identifier: c.userIdentifier ?? c.user_identifier,
68
+ })),
69
+ _tip: "Use conversation_id with debug(method='conversation_detail') to see messages and workflow_run_ids.",
70
+ _next_step: items.length > 0
71
+ ? `debug(method="conversation_detail", conversation_id="${items[0].conversationId ?? items[0].conversation_id}")`
72
+ : "No conversations found. Try adjusting filters or date range.",
73
+ };
74
+ }
75
+ // ─────────────────────────────────────────────────────────────────────────────
76
+ // conversation_detail
77
+ // ─────────────────────────────────────────────────────────────────────────────
78
+ export function formatConversationDetail(data, conversationId, personaId) {
79
+ const messages = data.messages ?? data.messageDetails ?? data.message_details;
80
+ const items = Array.isArray(messages) ? messages : [];
81
+ const formatted = items.map((m) => {
82
+ const workflowRunId = m.workflowRunId ?? m.workflow_run_id;
83
+ const chatMsg = (m.chatbotMessage ?? m.chatbot_message ?? {});
84
+ // Derive role from ChatbotMessage.type + isUserMessage
85
+ const msgType = chatMsg.type ?? chatMsg.message_type;
86
+ const isUser = chatMsg.isUserMessage ?? chatMsg.is_user_message;
87
+ const role = isUser ? "user" : (msgType ? "assistant" : undefined);
88
+ // Extract content from the message oneof (textMessage, buttonsMessage, etc.)
89
+ const textMsg = (chatMsg.textMessage ?? chatMsg.text_message);
90
+ const buttonsMsg = (chatMsg.buttonsMessage ?? chatMsg.buttons_message);
91
+ const content = textMsg?.contents ?? textMsg?.content ?? buttonsMsg?.content ?? chatMsg.content;
92
+ return {
93
+ workflow_run_id: workflowRunId,
94
+ role,
95
+ type: msgType,
96
+ content: truncateValue(content),
97
+ user_feedback: m.userFeedback ?? m.user_feedback,
98
+ work_log_steps_count: Array.isArray(m.workLogSteps ?? m.work_log_steps)
99
+ ? (m.workLogSteps ?? m.work_log_steps).length
100
+ : 0,
101
+ };
102
+ });
103
+ // Find the first message with a workflow_run_id for the next step hint
104
+ const withRunId = formatted.find((m) => m.workflow_run_id);
105
+ return {
106
+ conversation_id: conversationId,
107
+ message_count: formatted.length,
108
+ messages: formatted,
109
+ _tip: "Messages with workflow_run_id can be inspected with debug(method='show_work', persona_id='...', workflow_run_id='...').",
110
+ _next_step: withRunId
111
+ ? `debug(method="show_work", persona_id="${personaId ?? "<persona_id>"}", workflow_run_id="${withRunId.workflow_run_id}")`
112
+ : "No workflow_run_ids found in this conversation.",
113
+ };
114
+ }
115
+ // ─────────────────────────────────────────────────────────────────────────────
116
+ // show_work
117
+ // ─────────────────────────────────────────────────────────────────────────────
118
+ export function formatShowWork(data, workflowRunId, personaId) {
119
+ const showWorkLogs = (data.showWorkLogs ?? data.show_work_logs ?? {});
120
+ const actions = [];
121
+ const erroredActions = [];
122
+ for (const [actionName, logRaw] of Object.entries(showWorkLogs)) {
123
+ const log = logRaw;
124
+ const status = resolveStatus(log.actionRunStatus ?? log.action_run_status);
125
+ const inputs = log.inputs;
126
+ const outputs = log.outputs;
127
+ const inputEntries = Array.isArray(inputs?.entries) ? inputs.entries : [];
128
+ const outputEntries = Array.isArray(outputs?.entries) ? outputs.entries : [];
129
+ const actionSummary = {
130
+ action_name: actionName,
131
+ title: log.actionTitle ?? log.action_title,
132
+ status,
133
+ llm_call_count: log.llmCallCount ?? log.llm_call_count ?? 0,
134
+ llm_cost_usd: log.llmCostUsd ?? log.llm_cost_usd ?? 0,
135
+ llm_latency_ms: log.llmLatencyMs ?? log.llm_latency_ms ?? 0,
136
+ input_names: inputEntries.map((e) => e.name),
137
+ output_names: outputEntries.map((e) => e.name),
138
+ step_count: Array.isArray(log.workLogs ?? log.work_logs) ? (log.workLogs ?? log.work_logs).length : 0,
139
+ hitl_round_count: Array.isArray(log.hitlRounds ?? log.hitl_rounds) ? (log.hitlRounds ?? log.hitl_rounds).length : 0,
140
+ };
141
+ // Include error info if present
142
+ const messages = Array.isArray(log.messages) ? log.messages : [];
143
+ const errorMessages = messages.filter((m) => m.logLevel === "LOG_LEVEL_ERROR" || m.log_level === "LOG_LEVEL_ERROR" || m.logLevel === 1 || m.log_level === 1);
144
+ if (errorMessages.length > 0) {
145
+ actionSummary.errors = errorMessages.map((m) => {
146
+ const errorInfo = (m.errorInfo ?? m.error_info);
147
+ return {
148
+ message: m.messageTemplate ?? m.message_template,
149
+ error_name: errorInfo?.name,
150
+ external_message: errorInfo?.externalMessage ?? errorInfo?.external_message,
151
+ };
152
+ });
153
+ }
154
+ if (status === "ERRORED") {
155
+ erroredActions.push(actionName);
156
+ }
157
+ actions.push(actionSummary);
158
+ }
159
+ const result = {
160
+ workflow_run_id: workflowRunId,
161
+ action_count: actions.length,
162
+ actions,
163
+ _tip: "Use action_name with debug(method='action_detail', persona_id='...') to see full inputs, outputs, LLM calls, and steps for any action.",
164
+ };
165
+ // Smart _next_step: auto-suggest drilling into errored actions
166
+ if (erroredActions.length > 0) {
167
+ const pid = personaId ?? "<persona_id>";
168
+ result._next_step = `Action(s) ERRORED: ${erroredActions.join(", ")}. Investigate with: debug(method="action_detail", persona_id="${pid}", workflow_run_id="${workflowRunId}", action_name="${erroredActions[0]}")`;
169
+ }
170
+ else {
171
+ const pid = personaId ?? "<persona_id>";
172
+ result._next_step = actions.length > 0
173
+ ? `debug(method="action_detail", persona_id="${pid}", workflow_run_id="${workflowRunId}", action_name="${actions[0].action_name}") to see detailed execution trace.`
174
+ : "No actions found in this workflow run.";
175
+ }
176
+ return result;
177
+ }
178
+ // ─────────────────────────────────────────────────────────────────────────────
179
+ // action_detail
180
+ // ─────────────────────────────────────────────────────────────────────────────
181
+ export function formatActionDetail(data, workflowRunId, actionName) {
182
+ const log = (data.showWorkLog ?? data.show_work_log ?? {});
183
+ const status = resolveStatus(log.actionRunStatus ?? log.action_run_status);
184
+ // Inputs
185
+ const inputs = log.inputs;
186
+ const inputEntries = Array.isArray(inputs?.entries) ? inputs.entries : [];
187
+ const formattedInputs = inputEntries.map((e) => ({
188
+ name: e.name,
189
+ value: truncateValue(e.value),
190
+ type: e.type,
191
+ source_agent: e.sourceAgentName ?? e.source_agent_name,
192
+ }));
193
+ // Outputs
194
+ const outputs = log.outputs;
195
+ const outputEntries = Array.isArray(outputs?.entries) ? outputs.entries : [];
196
+ const formattedOutputs = outputEntries.map((e) => ({
197
+ name: e.name,
198
+ value: truncateValue(e.value),
199
+ type: e.type,
200
+ published_as_chat_response: e.publishedAsChatResponse ?? e.published_as_chat_response,
201
+ }));
202
+ // Steps (work logs)
203
+ const steps = Array.isArray(log.workLogs ?? log.work_logs) ? (log.workLogs ?? log.work_logs) : [];
204
+ const formattedSteps = steps.map((s) => {
205
+ const msgs = Array.isArray(s.messages) ? s.messages : [];
206
+ const stepLlmCalls = Array.isArray(s.llmCalls ?? s.llm_calls) ? (s.llmCalls ?? s.llm_calls) : [];
207
+ return {
208
+ name: s.name,
209
+ messages: msgs.map((m) => ({
210
+ heading: m.heading,
211
+ template: truncateValue(m.messageTemplate ?? m.message_template),
212
+ log_level: m.logLevel ?? m.log_level,
213
+ error_info: m.errorInfo ?? m.error_info,
214
+ })),
215
+ llm_calls: stepLlmCalls.map((c) => formatLlmCall(c)),
216
+ };
217
+ });
218
+ // Top-level LLM calls
219
+ const topLlmCalls = Array.isArray(log.llmCalls ?? log.llm_calls) ? (log.llmCalls ?? log.llm_calls) : [];
220
+ const formattedLlmCalls = topLlmCalls.map((c) => formatLlmCall(c));
221
+ // HITL rounds
222
+ const hitlRounds = Array.isArray(log.hitlRounds ?? log.hitl_rounds) ? (log.hitlRounds ?? log.hitl_rounds) : [];
223
+ const formattedHitl = hitlRounds.map((r) => truncateObject(r));
224
+ return {
225
+ workflow_run_id: workflowRunId,
226
+ action_name: actionName,
227
+ action_type: data.actionType ?? data.action_type,
228
+ title: log.actionTitle ?? log.action_title,
229
+ status,
230
+ llm_call_count: log.llmCallCount ?? log.llm_call_count ?? 0,
231
+ llm_cost_usd: log.llmCostUsd ?? log.llm_cost_usd ?? 0,
232
+ llm_latency_ms: log.llmLatencyMs ?? log.llm_latency_ms ?? 0,
233
+ inputs: formattedInputs,
234
+ outputs: formattedOutputs,
235
+ steps: formattedSteps,
236
+ llm_calls: formattedLlmCalls,
237
+ hitl_rounds: formattedHitl.length > 0 ? formattedHitl : undefined,
238
+ _tip: status === "ERRORED"
239
+ ? "Check the steps and error_info fields for failure details. LLM calls show the exact prompts and responses."
240
+ : "Review inputs/outputs for data flow and llm_calls for prompt details.",
241
+ };
242
+ }
243
+ function formatLlmCall(call) {
244
+ const prompt = (call.prompt ?? {});
245
+ const metrics = (call.metrics ?? {});
246
+ return {
247
+ heading: call.heading,
248
+ success: call.success,
249
+ error_message: call.errorMessage ?? call.error_message,
250
+ fusion_used: call.fusionUsed ?? call.fusion_used,
251
+ cost_final_choice: call.llmCostFinalChoice ?? call.llm_cost_final_choice,
252
+ timestamp_ms: call.timestampMs ?? call.timestamp_ms,
253
+ prompt: {
254
+ system_prompt: truncateValue(prompt.systemPrompt ?? prompt.system_prompt),
255
+ user_prompt: truncateValue(prompt.userPrompt ?? prompt.user_prompt),
256
+ response: truncateValue(prompt.response),
257
+ },
258
+ metrics: Object.keys(metrics).length > 0 ? truncateObject(metrics) : undefined,
259
+ };
260
+ }
261
+ // ─────────────────────────────────────────────────────────────────────────────
262
+ // search
263
+ // ─────────────────────────────────────────────────────────────────────────────
264
+ export function formatSearch(data, query, personaId) {
265
+ const results = Array.isArray(data.results) ? data.results : [];
266
+ return {
267
+ persona_id: personaId,
268
+ query,
269
+ total: results.length,
270
+ results: results.map((r) => ({
271
+ run_id: r.runId ?? r.run_id,
272
+ conversation_id: r.conversationId ?? r.conversation_id,
273
+ user_message_created_at: r.userMessageCreatedAt ?? r.user_message_created_at,
274
+ user_message: truncateValue(r.userMessage ?? r.user_message),
275
+ bot_message: truncateValue(r.botMessage ?? r.bot_message),
276
+ })),
277
+ _tip: "Use conversation_id with debug(method='conversation_detail') to see the full conversation, or run_id to identify the workflow execution.",
278
+ _next_step: results.length > 0
279
+ ? `debug(method="conversation_detail", conversation_id="${results[0].conversationId ?? results[0].conversation_id}")`
280
+ : "No messages matched the search query.",
281
+ };
282
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Debug Handler — Method Dispatch
3
+ *
4
+ * Routes debug tool methods to the appropriate SDK calls and formatters.
5
+ * Follows the drill-down pattern: conversations → conversation_detail → show_work → action_detail
6
+ */
7
+ import { formatConversations, formatConversationDetail, formatShowWork, formatActionDetail, formatSearch, } from "./formatters.js";
8
+ export async function handleDebug(args, client) {
9
+ const method = args.method;
10
+ if (!method) {
11
+ return {
12
+ error: "Missing required parameter: method",
13
+ available_methods: ["conversations", "conversation_detail", "show_work", "action_detail", "search"],
14
+ _tip: "Start with debug(method='conversations', persona_id='...') to list audit conversations.",
15
+ };
16
+ }
17
+ switch (method) {
18
+ case "conversations":
19
+ return handleConversations(args, client);
20
+ case "conversation_detail":
21
+ return handleConversationDetail(args, client);
22
+ case "show_work":
23
+ return handleShowWork(args, client);
24
+ case "action_detail":
25
+ return handleActionDetail(args, client);
26
+ case "search":
27
+ return handleSearch(args, client);
28
+ default:
29
+ return {
30
+ error: `Unknown method: ${method}`,
31
+ available_methods: ["conversations", "conversation_detail", "show_work", "action_detail", "search"],
32
+ };
33
+ }
34
+ }
35
+ // ─────────────────────────────────────────────────────────────────────────────
36
+ // Method handlers
37
+ // ─────────────────────────────────────────────────────────────────────────────
38
+ async function handleConversations(args, client) {
39
+ const personaId = args.persona_id;
40
+ if (!personaId) {
41
+ return { error: "Missing required parameter: persona_id" };
42
+ }
43
+ // Build filters matching the gRPC ConversationReviewFilters proto shape
44
+ const filters = {};
45
+ if (args.start_time)
46
+ filters.createdFrom = new Date(String(args.start_time));
47
+ if (args.end_time)
48
+ filters.createdTo = new Date(String(args.end_time));
49
+ if (args.channel)
50
+ filters.channel = Number(args.channel);
51
+ if (args.status)
52
+ filters.status = Number(args.status);
53
+ if (args.search_query)
54
+ filters.searchQuery = String(args.search_query);
55
+ if (args.workflow_failure)
56
+ filters.workflowFailure = true;
57
+ if (args.has_user_frustration)
58
+ filters.hasUserFrustration = true;
59
+ if (args.no_results_found)
60
+ filters.noResultsFound = true;
61
+ const opts = {
62
+ ...(Object.keys(filters).length > 0 ? { filters } : {}),
63
+ ...(args.limit ? { limit: Number(args.limit) } : {}),
64
+ ...(args.pagination_token ? { paginationToken: String(args.pagination_token) } : {}),
65
+ };
66
+ const response = await client.getConversationReviews(personaId, opts);
67
+ return formatConversations(response, personaId);
68
+ }
69
+ async function handleConversationDetail(args, client) {
70
+ const conversationId = args.conversation_id;
71
+ if (!conversationId) {
72
+ return { error: "Missing required parameter: conversation_id" };
73
+ }
74
+ const personaId = args.persona_id;
75
+ const response = await client.getConversationReviewDetail(conversationId);
76
+ return formatConversationDetail(response, conversationId, personaId);
77
+ }
78
+ async function handleShowWork(args, client) {
79
+ const workflowRunId = args.workflow_run_id;
80
+ const personaId = args.persona_id;
81
+ if (!workflowRunId) {
82
+ return { error: "Missing required parameter: workflow_run_id" };
83
+ }
84
+ if (!personaId) {
85
+ return {
86
+ error: "Missing required parameter: persona_id",
87
+ _tip: "DebugLogService requires persona context. Pass persona_id alongside workflow_run_id.",
88
+ };
89
+ }
90
+ const response = await client.getWorkflowLevelDebugLog(workflowRunId, personaId);
91
+ return formatShowWork(response, workflowRunId, personaId);
92
+ }
93
+ async function handleActionDetail(args, client) {
94
+ const workflowRunId = args.workflow_run_id;
95
+ const actionName = args.action_name;
96
+ const personaId = args.persona_id;
97
+ if (!workflowRunId) {
98
+ return { error: "Missing required parameter: workflow_run_id" };
99
+ }
100
+ if (!actionName) {
101
+ return {
102
+ error: "Missing required parameter: action_name",
103
+ _tip: "Use debug(method='show_work') first to see available action names.",
104
+ };
105
+ }
106
+ if (!personaId) {
107
+ return {
108
+ error: "Missing required parameter: persona_id",
109
+ _tip: "DebugLogService requires persona context. Pass persona_id alongside workflow_run_id and action_name.",
110
+ };
111
+ }
112
+ const response = await client.getActionLevelShowWorkLog(actionName, workflowRunId, personaId);
113
+ return formatActionDetail(response, workflowRunId, actionName);
114
+ }
115
+ async function handleSearch(args, client) {
116
+ const personaId = args.persona_id;
117
+ const query = args.query;
118
+ if (!personaId) {
119
+ return { error: "Missing required parameter: persona_id" };
120
+ }
121
+ if (!query) {
122
+ return { error: "Missing required parameter: query" };
123
+ }
124
+ const opts = { personaId, query };
125
+ if (args.conversation_id)
126
+ opts.conversationId = String(args.conversation_id);
127
+ if (args.start_time)
128
+ opts.startTime = new Date(String(args.start_time));
129
+ if (args.end_time)
130
+ opts.endTime = new Date(String(args.end_time));
131
+ const response = await client.searchMessages(opts);
132
+ return formatSearch(response, query, personaId);
133
+ }
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Demo V2 Adapter
3
+ *
4
+ * Routes the consolidated demo tool modes (kit, validate_kit, scenarios,
5
+ * consolidate, generate, validate, template) to the appropriate handlers.
6
+ *
7
+ * Extracted from server.ts to keep the dispatch table thin.
8
+ */
9
+ import { handleConsolidateDemoData, handleGenerateDemoDocument, handleValidateDemoDocument, handleGetDemoDataTemplate, } from "./index.js";
10
+ export async function handleDemoAdapter(args, createClient) {
11
+ const normalizedArgs = { ...(args ?? {}) };
12
+ const mode = normalizedArgs.mode ? String(normalizedArgs.mode) : "template";
13
+ const deprecationWarning = {
14
+ _deprecation: {
15
+ message: "The 'demo' tool is deprecated. Please use 'data' and 'persona' tools instead.",
16
+ migration: {
17
+ "demo(mode='kit')": "persona(from='demo-sales-sdr', include_data=true)",
18
+ "demo(mode='generate')": "data(mode='generate', from='customer', count=5)",
19
+ "demo(mode='scenarios')": "data(mode='templates')",
20
+ "demo(mode='template')": "data(mode='templates', template='customer')",
21
+ },
22
+ },
23
+ };
24
+ switch (mode) {
25
+ case "kit": {
26
+ const personaId = String(normalizedArgs.persona_id ?? "");
27
+ const scenarioId = String(normalizedArgs.scenario ?? "sales-sdr");
28
+ if (!personaId) {
29
+ throw new Error('demo(mode="kit") requires: persona_id');
30
+ }
31
+ const { generateDemoKit, DEMO_SCENARIOS, generateDemoScriptMarkdown, validateDemoKit } = await import("../../demo-generator.js");
32
+ const scenario = DEMO_SCENARIOS[scenarioId];
33
+ if (!scenario) {
34
+ throw new Error(`Unknown scenario: ${scenarioId}. Available: ${Object.keys(DEMO_SCENARIOS).join(", ")}`);
35
+ }
36
+ const client = await createClient(normalizedArgs.env);
37
+ const persona = await client.getPersonaById(personaId);
38
+ if (!persona) {
39
+ throw new Error(`Persona not found: ${personaId}`);
40
+ }
41
+ const workflowDef = persona.workflow_def || {};
42
+ const customQA = normalizedArgs.custom_qa;
43
+ const kit = generateDemoKit(personaId, persona.name || personaId, workflowDef, scenario, customQA);
44
+ const demoScript = generateDemoScriptMarkdown(kit);
45
+ const validation = validateDemoKit(kit);
46
+ return {
47
+ success: true,
48
+ persona_id: personaId,
49
+ persona_name: persona.name,
50
+ scenario: scenarioId,
51
+ kit_summary: {
52
+ kb_documents: kit.kb_documents.length,
53
+ demo_questions: kit.demo_script.length,
54
+ fixed_responses: kit.fixed_responses.length,
55
+ validation_queries: kit.validation_queries.length,
56
+ },
57
+ validation,
58
+ demo_script_preview: demoScript.slice(0, 2000) + (demoScript.length > 2000 ? "\n\n... (truncated)" : ""),
59
+ kit,
60
+ instructions: [
61
+ "1. Upload KB documents to the persona's knowledge base",
62
+ "2. Review the demo script and practice the questions",
63
+ "3. Optionally apply fixed_responses for guaranteed fallbacks",
64
+ "4. Run validation queries to verify demo readiness",
65
+ "5. Conduct the demo with confidence!",
66
+ ],
67
+ };
68
+ }
69
+ case "validate_kit": {
70
+ const personaId = String(normalizedArgs.persona_id ?? "");
71
+ if (!personaId) {
72
+ throw new Error('demo(mode="validate_kit") requires: persona_id');
73
+ }
74
+ const { analyzeWorkflowForDemo, DEMO_SCENARIOS } = await import("../../demo-generator.js");
75
+ const client = await createClient(normalizedArgs.env);
76
+ const persona = await client.getPersonaById(personaId);
77
+ if (!persona) {
78
+ throw new Error(`Persona not found: ${personaId}`);
79
+ }
80
+ const analysis = analyzeWorkflowForDemo(persona.workflow_def || {});
81
+ const dataSourcesResult = await client.listDataSourceFiles(personaId);
82
+ const dataSources = dataSourcesResult.files || [];
83
+ const hasKnowledgeBase = dataSources.length > 0;
84
+ const issues = [];
85
+ if (!hasKnowledgeBase) {
86
+ issues.push("No knowledge base documents uploaded - RAG search will fail");
87
+ }
88
+ if (analysis.intents.length === 0) {
89
+ issues.push("No categorizer intents detected - workflow may not route correctly");
90
+ }
91
+ if (!analysis.has_search) {
92
+ issues.push("No search nodes detected - cannot retrieve KB data");
93
+ }
94
+ let suggestedScenario = "sales-sdr";
95
+ for (const [id, scenario] of Object.entries(DEMO_SCENARIOS)) {
96
+ const intentOverlap = scenario.intents.filter(i => analysis.intents.some(ai => ai.toLowerCase().includes(i.name.toLowerCase()))).length;
97
+ if (intentOverlap > 0) {
98
+ suggestedScenario = id;
99
+ break;
100
+ }
101
+ }
102
+ return {
103
+ persona_id: personaId,
104
+ persona_name: persona.name,
105
+ ready: issues.length === 0,
106
+ issues,
107
+ workflow_analysis: analysis,
108
+ knowledge_base: {
109
+ has_documents: hasKnowledgeBase,
110
+ document_count: dataSources.length,
111
+ },
112
+ suggested_scenario: suggestedScenario,
113
+ next_steps: issues.length > 0
114
+ ? issues.map((issue, i) => `${i + 1}. Fix: ${issue}`)
115
+ : [`Generate demo kit: demo(mode="kit", persona_id="${personaId}", scenario="${suggestedScenario}")`],
116
+ };
117
+ }
118
+ case "scenarios": {
119
+ const { DEMO_SCENARIOS } = await import("../../demo-generator.js");
120
+ return {
121
+ scenarios: Object.entries(DEMO_SCENARIOS).map(([id, scenario]) => ({
122
+ id,
123
+ name: scenario.name,
124
+ description: scenario.description,
125
+ persona_types: scenario.persona_types,
126
+ tags: scenario.tags,
127
+ intent_count: scenario.intents.length,
128
+ qa_count: scenario.qa_pairs.length,
129
+ entity_types: scenario.entities.map(e => e.type),
130
+ })),
131
+ usage: 'demo(mode="kit", persona_id="...", scenario="<scenario_id>")',
132
+ };
133
+ }
134
+ case "consolidate": {
135
+ const source = String(normalizedArgs.source ?? "");
136
+ const output = String(normalizedArgs.output ?? "");
137
+ const entity = String(normalizedArgs.entity ?? "");
138
+ if (!source || !output || !entity) {
139
+ throw new Error('demo(mode="consolidate") requires: source, output, entity');
140
+ }
141
+ return handleConsolidateDemoData({
142
+ source_dir: source,
143
+ output_dir: output,
144
+ entity_type: entity,
145
+ primary_file: normalizedArgs.primary ?? `${entity}s.json`,
146
+ joins: normalizedArgs.joins ?? [],
147
+ tags: normalizedArgs.tags,
148
+ });
149
+ }
150
+ case "generate": {
151
+ const entity = String(normalizedArgs.entity ?? "");
152
+ if (!entity)
153
+ throw new Error('demo(mode="generate") requires: entity');
154
+ return handleGenerateDemoDocument({
155
+ entity_type: entity,
156
+ data: normalizedArgs.data ?? {},
157
+ related_data: normalizedArgs.related ?? {},
158
+ output_path: normalizedArgs.output,
159
+ tags: normalizedArgs.tags,
160
+ });
161
+ }
162
+ case "validate": {
163
+ return handleValidateDemoDocument({
164
+ file_path: normalizedArgs.file,
165
+ content: normalizedArgs.content,
166
+ });
167
+ }
168
+ case "template": {
169
+ const entity = String(normalizedArgs.entity ?? "");
170
+ if (!entity)
171
+ throw new Error('demo(mode="template") requires: entity');
172
+ return handleGetDemoDataTemplate({
173
+ entity_type: entity,
174
+ include_example: normalizedArgs.include_example,
175
+ });
176
+ }
177
+ default:
178
+ throw new Error(`Unknown demo mode: ${mode}`);
179
+ }
180
+ }
@@ -9,7 +9,7 @@ import { readFileSync } from "node:fs";
9
9
  import { execSync } from "node:child_process";
10
10
  import { fileURLToPath } from "node:url";
11
11
  import { dirname, join } from "node:path";
12
- import { EmaClient } from "../../../sdk/client.js";
12
+ import { EmaClientAdapter } from "../../../sdk/client-adapter.js";
13
13
  import { loadConfigOptional } from "../../../sdk/config.js";
14
14
  import { SyncSDK } from "../../../sync/sdk.js";
15
15
  // ─────────────────────────────────────────────────────────────────────────────
@@ -282,7 +282,7 @@ export function createClient(envName) {
282
282
  },
283
283
  }
284
284
  : undefined;
285
- return new EmaClient({
285
+ return new EmaClientAdapter({
286
286
  name: envInfo.name,
287
287
  baseUrl: envInfo.baseUrl,
288
288
  bearerToken,
@@ -125,7 +125,7 @@ async function handleAnalyze() {
125
125
  ? `Found ${analysis.actionable_items.length} actionable item(s). Review and address these to improve agent experience.`
126
126
  : "No actionable items found. Feedback data may be limited - encourage agents to submit feedback.",
127
127
  _next_step: analysis.actionable_items.length > 0
128
- ? "Review actionable_items and update guidance.ts, resources.ts, or workflow-builder-patterns.md accordingly."
128
+ ? "Review actionable_items and update guidance.ts or resources.ts accordingly."
129
129
  : "Continue collecting feedback. Use toolkit_feedback(method='list') to review individual entries.",
130
130
  };
131
131
  }
@@ -27,6 +27,5 @@ export { handleEnv } from "./env/index.js";
27
27
  export { handleTemplate } from "./template/index.js";
28
28
  export { handleAction } from "./action/index.js";
29
29
  export { handleReference } from "./reference/index.js";
30
- export { handleKnowledge } from "./knowledge/index.js";
31
30
  export { handleSync } from "./sync/index.js";
32
31
  // Note: handleDashboardGenerate removed - LLM generates content, MCP uploads via data.method="upload"