@ema.co/mcp-toolkit 2026.2.27 → 2026.2.28

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 (58) hide show
  1. package/.context/public/guides/ema-user-guide.md +7 -6
  2. package/.context/public/guides/mcp-tools-guide.md +46 -23
  3. package/dist/config/index.js +11 -0
  4. package/dist/config/workflow-patterns.js +361 -0
  5. package/dist/mcp/autobuilder.js +2 -2
  6. package/dist/mcp/domain/generation-schema.js +15 -9
  7. package/dist/mcp/domain/structural-rules.js +3 -3
  8. package/dist/mcp/domain/validation-rules.js +20 -27
  9. package/dist/mcp/domain/workflow-generator.js +3 -3
  10. package/dist/mcp/domain/workflow-graph.js +1 -1
  11. package/dist/mcp/guidance.js +60 -1
  12. package/dist/mcp/handlers/conversation/adapter.js +13 -0
  13. package/dist/mcp/handlers/conversation/create.js +19 -0
  14. package/dist/mcp/handlers/conversation/delete.js +18 -0
  15. package/dist/mcp/handlers/conversation/formatters.js +62 -0
  16. package/dist/mcp/handlers/conversation/history.js +15 -0
  17. package/dist/mcp/handlers/conversation/index.js +43 -0
  18. package/dist/mcp/handlers/conversation/list.js +40 -0
  19. package/dist/mcp/handlers/conversation/messages.js +13 -0
  20. package/dist/mcp/handlers/conversation/rename.js +16 -0
  21. package/dist/mcp/handlers/conversation/send.js +90 -0
  22. package/dist/mcp/handlers/data/index.js +169 -3
  23. package/dist/mcp/handlers/feedback/client-id.js +49 -0
  24. package/dist/mcp/handlers/feedback/coalesce.js +167 -0
  25. package/dist/mcp/handlers/feedback/index.js +42 -1
  26. package/dist/mcp/handlers/feedback/outbox.js +301 -0
  27. package/dist/mcp/handlers/feedback/probes.js +127 -0
  28. package/dist/mcp/handlers/feedback/remote-store.js +59 -0
  29. package/dist/mcp/handlers/feedback/store.js +13 -1
  30. package/dist/mcp/handlers/persona/delete.js +7 -28
  31. package/dist/mcp/handlers/persona/update.js +7 -26
  32. package/dist/mcp/handlers/persona/version.js +30 -15
  33. package/dist/mcp/handlers/template/adapter.js +23 -0
  34. package/dist/mcp/handlers/template/crud.js +174 -0
  35. package/dist/mcp/handlers/template/index.js +6 -7
  36. package/dist/mcp/handlers/workflow/adapter.js +30 -46
  37. package/dist/mcp/handlers/workflow/index.js +2 -2
  38. package/dist/mcp/handlers/workflow/validation.js +2 -2
  39. package/dist/mcp/knowledge-guidance-topics.js +90 -53
  40. package/dist/mcp/knowledge.js +7 -357
  41. package/dist/mcp/prompts.js +5 -5
  42. package/dist/mcp/resources-dynamic.js +46 -38
  43. package/dist/mcp/resources-validation.js +5 -5
  44. package/dist/mcp/server.js +38 -5
  45. package/dist/mcp/tools.js +340 -8
  46. package/dist/sdk/client-adapter.js +90 -2
  47. package/dist/sdk/client.js +7 -0
  48. package/dist/sdk/ema-client.js +242 -27
  49. package/dist/sdk/generated/agent-catalog.js +96 -39
  50. package/dist/sdk/generated/deprecated-actions.js +1 -1
  51. package/dist/sdk/grpc-client.js +67 -5
  52. package/dist/sync/central-factory.js +86 -0
  53. package/dist/sync/central-version-storage.js +387 -0
  54. package/dist/sync/dis-port.js +75 -0
  55. package/dist/sync/version-policy.js +29 -31
  56. package/dist/sync/version-storage-interface.js +11 -0
  57. package/dist/sync/version-storage.js +22 -22
  58. package/package.json +2 -1
@@ -29,6 +29,10 @@ export function buildCompactAgents() {
29
29
  agents[agent.actionName] = {
30
30
  name: agent.displayName,
31
31
  category: agent.category,
32
+ description: agent.description,
33
+ whenToUse: agent.whenToUse,
34
+ aliases: agent.aliases,
35
+ tier: agent.tier,
32
36
  inputs,
33
37
  outputs,
34
38
  constraints: agent.criticalRules,
@@ -81,11 +85,11 @@ export function buildConstraints() {
81
85
  "Category routing is via runIf conditions, not edges",
82
86
  ],
83
87
  hitl: [
84
- "MUST have both success and failure paths",
85
- "Success path: source_output 'hitl_status_HITL Success' (note: space, not underscore)",
86
- "Failure path: source_output 'hitl_status_HITL Failure' (note: space, not underscore)",
87
- "Both paths must eventually reach WORKFLOW_OUTPUT",
88
- "Unlike regular categorizers, NO Fallback category - only Success/Failure",
88
+ "general_hitl (Human Collaboration Agent): standalone node with Conversational, Standard Form, and Custom Form modes",
89
+ "Outputs: human_collaboration_status (HITL Success / HITL Failure enum) and summarized_conversation",
90
+ "Branch downstream nodes with runIf on human_collaboration_status",
91
+ "MUST handle both HITL Success and HITL Failure paths — both must reach WORKFLOW_OUTPUT",
92
+ "send_email_agent and entity_extraction_with_documents also support HITL via disable_human_interaction: false flag",
89
93
  ],
90
94
  outputs: [
91
95
  "All user-visible outputs MUST connect to WORKFLOW_OUTPUT",
@@ -117,16 +121,18 @@ export function generateSchema() {
117
121
  export function generateSchemaMarkdown() {
118
122
  const schema = generateSchema();
119
123
  let md = `# Workflow Generation Schema\n\n`;
120
- // Agent summary table
124
+ // Agent summary table with tier and description
121
125
  md += `## Available Agents (${Object.keys(schema.agents).length})\n\n`;
122
- md += `| Action | Category | Inputs | Outputs |\n`;
123
- md += `|--------|----------|--------|--------|\n`;
126
+ md += `| Action | Tier | Category | Description | Inputs | Outputs |\n`;
127
+ md += `|--------|------|----------|-------------|--------|--------|\n`;
124
128
  for (const [actionName, agent] of Object.entries(schema.agents)) {
125
129
  const inputs = Object.entries(agent.inputs)
126
130
  .map(([name, { type, required }]) => `${name}${required ? "*" : ""}`)
127
131
  .join(", ") || "-";
128
132
  const outputs = Object.keys(agent.outputs).join(", ") || "-";
129
- md += `| \`${actionName}\` | ${agent.category} | ${inputs} | ${outputs} |\n`;
133
+ const tier = agent.tier ?? "-";
134
+ const desc = agent.description ? agent.description.slice(0, 80) : "-";
135
+ md += `| \`${actionName}\` | ${tier} | ${agent.category} | ${desc} | ${inputs} | ${outputs} |\n`;
130
136
  }
131
137
  // Type rules
132
138
  md += `\n## Type Compatibility\n\n`;
@@ -104,7 +104,7 @@ export const STRUCTURAL_INVARIANTS = [
104
104
  {
105
105
  id: "hitl_has_both_paths",
106
106
  name: "HITL Must Have Success AND Failure Paths",
107
- rule: "Legacy general_hitl nodes (if present) have two outcomes: approval and rejection. Both MUST have downstream handlers. Note: general_hitl is NOT deployable for new workflows HITL is a flag on entity_extraction_with_documents and send_email_agent only.",
107
+ rule: "general_hitl (Human Collaboration Agent) nodes have two outcomes: HITL Success and HITL Failure. Both MUST have downstream handlers via runIf conditions. Additionally, send_email_agent and entity_extraction_with_documents support HITL via their disable_human_interaction flag.",
108
108
  violation: "HITL 'approval' only has success path - rejections hang",
109
109
  fix: "For legacy workflows: add handler for hitl.approval_decision = 'reject'. For new workflows: use HITL flag on send_email_agent or entity_extraction_with_documents instead.",
110
110
  severity: "critical",
@@ -332,12 +332,12 @@ BEFORE finalizing any workflow modification, verify these rules:
332
332
  |---------------|------------------|
333
333
  | TEXT_WITH_SOURCES | trigger.user_query, conversation_to_search_query.summarized_conversation |
334
334
  | CHAT_CONVERSATION | trigger.chat_conversation |
335
- | SEARCH_RESULT | search.search_results, combine_search_results.combined_results |
335
+ | SEARCH_RESULT | search.search_results, combine_and_rerank_search_results.reranked_results |
336
336
  | EMAIL_ADDRESS | entity_extraction.email_address (NOT text outputs) |
337
337
 
338
338
  ### HITL Rules
339
339
 
340
- 10. **Both Paths Required**: Legacy general_hitl nodes need handlers for both approval AND rejection. Note: general_hitl is NOT deployable HITL is a flag on entity_extraction_with_documents and send_email_agent only.
340
+ 10. **Both Paths Required**: general_hitl (Human Collaboration Agent) nodes need handlers for both HITL Success AND HITL Failure paths. Additionally, send_email_agent and entity_extraction_with_documents support HITL via disable_human_interaction flag.
341
341
 
342
342
  ### Raw workflow_def Format Rules (CRITICAL)
343
343
 
@@ -148,7 +148,7 @@ export const ANTI_PATTERNS = [
148
148
  pattern: "send_email_agent without entity_extraction to validate recipient data",
149
149
  problem: "Sending emails without extracting and validating recipient data risks sending to wrong people or with wrong content. Emails are high-impact actions with external side effects.",
150
150
  solution: "Always: 1) Extract required fields (email_address, subject) via entity_extraction, 2) Validate completeness via categorizer, 3) Ask user if missing. " +
151
- "For approval: enable the HITL flag on send_email_agent (disable_human_interaction: false). general_hitl is NOT deployable.",
151
+ "For simple approval: enable HITL flag on send_email_agent (disable_human_interaction: false). For rich dialogue: use general_hitl (Human Collaboration Agent).",
152
152
  detection: {
153
153
  issueType: "incomplete_email_validation",
154
154
  condition: "send_email_agent without preceding entity_extraction node to extract/validate recipient data",
@@ -208,15 +208,12 @@ export const ANTI_PATTERNS = [
208
208
  name: "Incomplete HITL Paths",
209
209
  pattern: "HITL with only success path",
210
210
  problem: "Rejected requests have no handling, leaving users without response.",
211
- solution: "If workflow already has general_hitl node: ALWAYS implement both success AND failure paths. For NEW workflows: use HITL flag on the agent (only entity_extraction_with_documents and send_email_agent support HITL). general_hitl is NOT deployable.",
211
+ solution: "general_hitl (Human Collaboration Agent) MUST have both HITL Success AND HITL Failure downstream handlers via runIf. For agent-level HITL (send_email_agent, entity_extraction_with_documents), the platform handles approval UI automatically.",
212
212
  detection: {
213
213
  issueType: "incomplete_hitl",
214
214
  condition: "HITL node missing 'hitl_status_HITL Success' or 'hitl_status_HITL Failure' edge (note: space, not underscore)",
215
215
  },
216
216
  severity: "critical",
217
- // general_hitl is NOT deployable — it appears in catalogs but cannot be deployed.
218
- // HITL is a flag on entity_extraction_with_documents and send_email_agent only.
219
- // external_action_caller does NOT support HITL. This rule still fires for legacy workflows.
220
217
  },
221
218
  {
222
219
  id: "orphan-nodes",
@@ -234,7 +231,7 @@ export const ANTI_PATTERNS = [
234
231
  id: "unused-output",
235
232
  name: "Unused Output",
236
233
  pattern: "Node produces output that is not consumed by any downstream node",
237
- problem: "Node is doing work that goes nowhere. Common with combine_search_results where combined_results isn't wired. Wastes compute and indicates incomplete wiring.",
234
+ problem: "Node is doing work that goes nowhere. Common with combine_and_rerank_search_results where reranked_results isn't wired. Wastes compute and indicates incomplete wiring.",
238
235
  solution: "Connect the output to a downstream node that needs it, or remove the node if not needed.",
239
236
  detection: {
240
237
  issueType: "unused_output",
@@ -453,31 +450,27 @@ export const ANTI_PATTERNS = [
453
450
  },
454
451
  {
455
452
  id: "chat-conversation-without-chat-response",
456
- name: "Chat Conversation Not Wired to Chat Response Node",
457
- pattern: "Chat workflow where chat_conversation is consumed by processing nodes but never wired to a chat-aware response node (respond_for_external_actions)",
453
+ name: "Chat Conversation Not Wired to Response Node",
454
+ pattern: "Chat workflow where chat_conversation is not wired to the response node's named_inputs",
458
455
  problem: "When chat_conversation is processed by nodes like entity_extraction, call_llm, or search, " +
459
- "but the final response is generated by a generic call_llm (not a chat-aware response node), " +
460
- "the response node lacks conversation history context. This causes:\n" +
456
+ "but the final response node lacks conversation history context, it causes:\n" +
461
457
  "1. Duplicate/repetitive responses — the LLM re-asks questions already answered in conversation\n" +
462
458
  "2. Lost context — follow-up messages lose prior context\n" +
463
- "3. Hallucinated greetings — the LLM generates its own greeting instead of continuing the conversation\n\n" +
464
- "The respond_for_external_actions node is specifically designed to handle conversation history " +
465
- "and produce contextually appropriate responses (replacing the deprecated respond_with_sources).",
466
- solution: "Wire chat_conversation through to a chat-aware response node:\n" +
467
- "• For search results or tool/action results: use respond_for_external_actions (takes query + conversation + external_action_result)\n" +
468
- "• If using call_llm as responder: wire chat_conversation into named_inputs so the LLM sees full history\n\n" +
459
+ "3. Hallucinated greetings — the LLM generates its own greeting instead of continuing the conversation",
460
+ solution: "Wire chat_conversation into the response node's named_inputs:\n" +
461
+ " For KB Q&A: call_llm with named_inputs_Search_Results + named_inputs_Conversation (89% of production)\n" +
462
+ " For tool results: respond_for_external_actions (ONLY after external_action_caller)\n" +
463
+ "• For citations: respond_with_sources\n\n" +
469
464
  "Correct patterns:\n" +
470
- " chat_trigger → search → respond_for_external_actions → WORKFLOW_OUTPUT\n" +
471
- " chat_trigger → external_action_caller → respond_for_external_actions → WORKFLOW_OUTPUT\n" +
472
- " chat_trigger → call_llm(named_inputs includes chat_conversation) → WORKFLOW_OUTPUT\n\n" +
465
+ " chat_trigger → search → call_llm(named_inputs_Search_Results + named_inputs_Conversation) → WORKFLOW_OUTPUT\n" +
466
+ " chat_trigger → external_action_caller → respond_for_external_actions → WORKFLOW_OUTPUT\n\n" +
473
467
  "Anti-patterns:\n" +
474
468
  " ❌ chat_trigger → search → call_llm (no conversation context) → WORKFLOW_OUTPUT\n" +
475
- " ❌ chat_trigger → entity_extractioncall_llm (stateless) WORKFLOW_OUTPUT\n" +
476
- " ❌ chat_trigger → searchrespond_with_sources (DEPRECATED) → WORKFLOW_OUTPUT",
469
+ " ❌ chat_trigger → searchrespond_for_external_actions (wrong that's for tool results only)\n" +
470
+ " ❌ chat_trigger → entity_extractioncall_llm (stateless) → WORKFLOW_OUTPUT",
477
471
  detection: {
478
472
  issueType: "chat_conversation_not_wired_to_response",
479
- condition: "Chat workflow (chat_trigger) where terminal response node is call_llm without chat_conversation in named_inputs, " +
480
- "and respond_for_external_actions is not used",
473
+ condition: "Chat workflow (chat_trigger) where terminal response node is call_llm without chat_conversation in named_inputs",
481
474
  },
482
475
  severity: "critical",
483
476
  },
@@ -598,10 +591,10 @@ export const OPTIMIZATION_RULES = [
598
591
  },
599
592
  {
600
593
  id: "use-purpose-built",
601
- name: "Use Purpose-Built Response Nodes",
602
- currentState: "Using call_llm for search-based responses",
603
- recommendation: "Use respond_for_external_actions instead - it has built-in citation handling and conversation awareness (respond_with_sources is deprecated)",
604
- benefit: "Better citations, grounded responses, conversation context, less configuration",
594
+ name: "Wire Conversation Context into Response Nodes",
595
+ currentState: "Using call_llm for search-based responses without conversation context",
596
+ recommendation: "Wire chat_conversation via named_inputs_Conversation so the LLM sees multi-turn history. For citation-grounded responses, use respond_with_sources. respond_for_external_actions is ONLY for external_action_caller results.",
597
+ benefit: "Conversation-aware responses, no repetitive questions, proper multi-turn context",
605
598
  priority: "medium",
606
599
  },
607
600
  {
@@ -32,7 +32,7 @@ const FALLBACK_NAMESPACES = {
32
32
  send_email_agent: ["actions", "emainternal"],
33
33
  conversation_to_search_query: ["actions", "emainternal"],
34
34
  entity_extraction: ["actions", "emainternal"],
35
- combine_search_results: ["actions", "emainternal"],
35
+ combine_and_rerank_search_results: ["actions", "emainternal"],
36
36
  response_validator: ["actions", "emainternal"],
37
37
  };
38
38
  /**
@@ -57,7 +57,7 @@ const FALLBACK_VERSIONS = {
57
57
  send_email_agent: "v0", // Fixed: was v1
58
58
  conversation_to_search_query: "v0", // Fixed: was v1
59
59
  entity_extraction: "v0", // entity_extraction_with_documents is v0
60
- combine_search_results: "v0", // combine_and_rerank_search_results is v0
60
+ combine_and_rerank_search_results: "v0",
61
61
  response_validator: "v0", // Fixed: was v1
62
62
  };
63
63
  /**
@@ -66,7 +66,7 @@ const FALLBACK_VERSIONS = {
66
66
  */
67
67
  const ACTION_TYPE_TO_API_NAME = {
68
68
  entity_extraction: "entity_extraction_with_documents",
69
- combine_search_results: "combine_and_rerank_search_results",
69
+ // combine_and_rerank_search_results maps to itself (identity) — no override needed
70
70
  general_hitl: "hitl",
71
71
  // CRITICAL: respond_with_sources doesn't exist in API - map to call_llm
72
72
  respond_with_sources: "call_llm",
@@ -26,7 +26,7 @@ const SIDE_EFFECT_FREE = new Set([
26
26
  "respond_with_sources",
27
27
  "respond_for_external_actions",
28
28
  "conversation_to_search_query",
29
- "combine_search_results",
29
+ "combine_and_rerank_search_results",
30
30
  "text_categorizer",
31
31
  "chat_categorizer",
32
32
  "response_validator",
@@ -337,13 +337,18 @@ export const TOOL_GUIDANCE = {
337
337
  },
338
338
  toolkit_feedback: {
339
339
  toolName: "toolkit_feedback",
340
- quickTip: "Submit feedback to help improve the MCP toolkit. Report gaps, confusion, successes, or suggestions.",
340
+ quickTip: "Submit feedback to help improve the MCP toolkit. Report gaps, confusion, successes, or suggestions. Respond to _probe questions when they appear in tool responses.",
341
341
  operations: [
342
342
  {
343
343
  name: "Submit feedback",
344
344
  description: "Report an issue or positive experience",
345
345
  example: 'toolkit_feedback(method="submit", category="gap", message="Missing docs on X")',
346
346
  },
347
+ {
348
+ name: "Respond to probe",
349
+ description: "Answer a targeted question from a _probe field in a tool response",
350
+ example: 'toolkit_feedback(method="submit", category="probe_response", message="<your answer>", context="<probe.id>")',
351
+ },
347
352
  {
348
353
  name: "List feedback",
349
354
  description: "View recent feedback entries",
@@ -363,6 +368,45 @@ export const TOOL_GUIDANCE = {
363
368
  commonMistakes: [
364
369
  "Forgetting to include which tool/operation the feedback is about",
365
370
  "Not describing what you were trying to accomplish (use context parameter)",
371
+ "Ignoring _probe questions in tool responses — answering them helps improve the toolkit",
372
+ ],
373
+ applicableRules: [],
374
+ },
375
+ template: {
376
+ toolName: "template",
377
+ quickTip: "Manage persona templates (blueprints for AI Employees). List, create, update, delete templates.",
378
+ operations: [
379
+ {
380
+ name: "List templates",
381
+ description: "Browse available persona templates",
382
+ example: 'template(method="list")',
383
+ },
384
+ {
385
+ name: "Get template",
386
+ description: "Get full details for a template",
387
+ example: 'template(method="get", id="...")',
388
+ },
389
+ {
390
+ name: "Create template",
391
+ description: "Create a new persona template",
392
+ example: 'template(method="create", name="My Template", type="voice")',
393
+ },
394
+ {
395
+ name: "Delete template",
396
+ description: "Delete a persona template",
397
+ example: 'template(method="delete", id="...", confirm=true)',
398
+ },
399
+ ],
400
+ nextSteps: {
401
+ list: "Get details: template(method='get', id='<id>')",
402
+ get: "Create persona from template: persona(method='create', from='<template_id>', name='...')",
403
+ create: "View template: template(method='get', id='<template_id>')",
404
+ update: "Verify changes: template(method='get', id='<template_id>')",
405
+ delete: "Template removed. List remaining: template(method='list')",
406
+ },
407
+ commonMistakes: [
408
+ "Deleting a template without confirm=true",
409
+ "Using template tool for read-only browsing when catalog tool would suffice",
366
410
  ],
367
411
  applicableRules: [],
368
412
  },
@@ -486,6 +530,15 @@ Use the \`debug\` tool to inspect workflow runs and troubleshoot issues:
486
530
  3. \`debug(method="action_detail", ...)\` → deep trace with LLM calls, inputs/outputs
487
531
  Also available as persona sub-resource: \`persona(id="abc", debug={method:"conversations"})\`
488
532
 
533
+ ## Template Management
534
+ Use the \`template\` tool to create, browse, update, and delete persona templates (blueprints for AI Employees):
535
+ - \`template(method="list")\` → browse available templates
536
+ - \`template(method="get", id="...")\` → get full template details
537
+ - \`template(method="create", name="...", type="chat", proto_config={...})\` → create new template
538
+ - \`template(method="update", id="...", name="...")\` → update template
539
+ - \`template(method="delete", id="...", confirm=true)\` → delete template
540
+ For read-only browsing, \`catalog(type="templates")\` also works.
541
+
489
542
  ## Resources
490
543
  - \`ema://docs/usage-guide\` - Complete guide
491
544
  - \`ema://docs/debugging-guide\` - Debugging workflow executions
@@ -501,6 +554,12 @@ Help us improve! Use \`toolkit_feedback\` to report issues or successes:
501
554
 
502
555
  Your feedback is automatically collected and analyzed to improve the toolkit for all agents.
503
556
 
557
+ ## Probes
558
+ Tool responses may occasionally include a \`_probe\` field with a targeted question.
559
+ When you see one, please respond using the \`how_to_respond\` instructions in the probe.
560
+ This provides valuable contextual feedback that helps us improve specific areas of the toolkit.
561
+ Probes are optional — you can skip them if they are not relevant to your current task.
562
+
504
563
  ## Need Help?
505
564
  Use the \`toolkit_onboard\` prompt for comprehensive guidance.
506
565
  `;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Conversation Adapter
3
+ *
4
+ * Thin adapter that resolves client from env and delegates to handleConversation.
5
+ * Same pattern as debug/adapter.ts.
6
+ */
7
+ import { handleConversation } from "./index.js";
8
+ export async function handleConversationAdapter(args, createClient, getDefaultEnvName) {
9
+ const targetEnv = args.env ?? getDefaultEnvName();
10
+ // createClient() returns EmaClientAdapter at runtime (see env/config.ts)
11
+ const client = createClient(targetEnv);
12
+ return handleConversation(args, client);
13
+ }
@@ -0,0 +1,19 @@
1
+ import { formatCreateResponse } from "./formatters.js";
2
+ /**
3
+ * Create a new chat conversation for a persona.
4
+ * Returns the conversation ID and welcome message.
5
+ */
6
+ export async function handleCreate(args, client) {
7
+ const personaId = args.persona_id;
8
+ if (!personaId) {
9
+ return {
10
+ error: "persona_id is required",
11
+ hint: 'conversation(method="create", persona_id="<uuid>")',
12
+ };
13
+ }
14
+ const result = await client.createConversation(personaId, {
15
+ userContext: args.user_context,
16
+ caller: args.caller ?? "mcp-toolkit",
17
+ });
18
+ return formatCreateResponse(result);
19
+ }
@@ -0,0 +1,18 @@
1
+ export async function handleDelete(args, client) {
2
+ const conversationId = args.conversation_id;
3
+ if (!conversationId) {
4
+ return { error: "Missing required parameter: conversation_id" };
5
+ }
6
+ const confirm = args.confirm;
7
+ if (confirm !== true) {
8
+ return {
9
+ error: "Deletion requires confirm=true",
10
+ _tip: `To delete conversation ${conversationId}, call conversation(method='delete', conversation_id='${conversationId}', confirm=true)`,
11
+ };
12
+ }
13
+ await client.deleteConversation(conversationId);
14
+ return {
15
+ success: true,
16
+ deleted_conversation_id: conversationId,
17
+ };
18
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Conversation response formatters.
3
+ *
4
+ * Shapes raw API responses into agent-friendly output with _tip / _next_step hints.
5
+ */
6
+ const MAX_MESSAGE_PREVIEW = 2000;
7
+ /**
8
+ * Truncate a string to max length with ellipsis.
9
+ */
10
+ function truncate(value, max = MAX_MESSAGE_PREVIEW) {
11
+ const str = typeof value === "string" ? value : JSON.stringify(value) ?? "";
12
+ return str.length > max ? str.slice(0, max) + "..." : str;
13
+ }
14
+ export function formatCreateResponse(result) {
15
+ return {
16
+ conversation_id: result.conversationId,
17
+ welcome_message: result.message,
18
+ _next_step: `conversation(method="send", conversation_id="${result.conversationId}", text="your message here")`,
19
+ _tip: "Send a text message to start the conversation. The AI Employee will respond with its workflow output.",
20
+ };
21
+ }
22
+ export function formatSendResponse(result) {
23
+ const msg = result.message;
24
+ const msgType = msg?.type ?? "unknown";
25
+ const response = {
26
+ conversation_id: result.conversationId,
27
+ message: result.message,
28
+ snippets: result.snippets,
29
+ };
30
+ // Provide contextual next-step based on response message type
31
+ if (msgType.includes("BUTTONS")) {
32
+ response._tip = "The AI responded with buttons. Select one using the buttons parameter.";
33
+ response._next_step = `conversation(method="send", conversation_id="${result.conversationId}", buttons={selected_button:{label:"<button label>"}})`;
34
+ }
35
+ else if (msgType.includes("FORM")) {
36
+ response._tip = "The AI responded with a form. Echo back the entire formMessage with values filled in using raw_message. The form param only works for cancellation.";
37
+ response._next_step = `conversation(method="send", conversation_id="${result.conversationId}", original_message_id="<bot_msg_id>", raw_message={type:"MESSAGE_TYPE_FORM", formMessage:{...formMessage from response with values filled...}, isUserMessage:true})`;
38
+ }
39
+ else {
40
+ response._tip = "Check message for the AI's reply. Use method='history' to review the full thread.";
41
+ response._next_step = `conversation(method="history", conversation_id="${result.conversationId}")`;
42
+ }
43
+ return response;
44
+ }
45
+ export function formatHistoryResponse(conversationId, result) {
46
+ const messages = result.messages.map((m) => {
47
+ const text = m.textMessage;
48
+ const contents = text?.contents;
49
+ return {
50
+ ...m,
51
+ // Add a preview field for quick scanning
52
+ ...(contents?.[0] && { _preview: truncate(contents[0], 200) }),
53
+ };
54
+ });
55
+ return {
56
+ conversation_id: conversationId,
57
+ message_count: messages.length,
58
+ messages,
59
+ _tip: "Messages are in chronological order. Use method='send' to continue the conversation.",
60
+ _next_step: `conversation(method="send", conversation_id="${conversationId}", text="your reply")`,
61
+ };
62
+ }
@@ -0,0 +1,15 @@
1
+ import { formatHistoryResponse } from "./formatters.js";
2
+ /**
3
+ * Retrieve the message history for a conversation.
4
+ */
5
+ export async function handleHistory(args, client) {
6
+ const conversationId = args.conversation_id;
7
+ if (!conversationId) {
8
+ return {
9
+ error: "conversation_id is required",
10
+ hint: 'conversation(method="history", conversation_id="<uuid>")',
11
+ };
12
+ }
13
+ const result = await client.getConversationHistory(conversationId);
14
+ return formatHistoryResponse(conversationId, result);
15
+ }
@@ -0,0 +1,43 @@
1
+ import { handleCreate } from "./create.js";
2
+ import { handleSend } from "./send.js";
3
+ import { handleHistory } from "./history.js";
4
+ import { handleList } from "./list.js";
5
+ import { handleMessages } from "./messages.js";
6
+ import { handleDelete } from "./delete.js";
7
+ import { handleRename } from "./rename.js";
8
+ const METHOD_HANDLERS = {
9
+ create: handleCreate,
10
+ send: handleSend,
11
+ history: handleHistory,
12
+ list: handleList,
13
+ messages: handleMessages,
14
+ delete: handleDelete,
15
+ rename: handleRename,
16
+ };
17
+ /**
18
+ * Top-level conversation handler.
19
+ * Routes to method-specific handlers based on the `method` parameter.
20
+ */
21
+ export async function handleConversation(args, client) {
22
+ const method = args.method;
23
+ if (!method) {
24
+ return {
25
+ error: "method is required",
26
+ valid_methods: Object.keys(METHOD_HANDLERS),
27
+ examples: [
28
+ 'conversation(method="create", persona_id="...") - start a live chat session',
29
+ 'conversation(method="send", conversation_id="...", text="hello") - send a message',
30
+ 'conversation(method="history", conversation_id="...") - view messages',
31
+ ],
32
+ _tip: "Start with method='create' to open a new chat session with a persona.",
33
+ };
34
+ }
35
+ const handler = METHOD_HANDLERS[method];
36
+ if (!handler) {
37
+ return {
38
+ error: `Unknown method: ${method}`,
39
+ valid_methods: Object.keys(METHOD_HANDLERS),
40
+ };
41
+ }
42
+ return handler(args, client);
43
+ }
@@ -0,0 +1,40 @@
1
+ export async function handleList(args, client) {
2
+ const personaId = args.persona_id;
3
+ if (personaId) {
4
+ // List conversations for a specific persona
5
+ const limit = args.limit;
6
+ const paginationToken = args.pagination_token;
7
+ const result = await client.listConversationsForPersona(personaId, {
8
+ limit,
9
+ paginationToken,
10
+ });
11
+ const conversations = result.conversations ?? result;
12
+ const count = Array.isArray(conversations) ? conversations.length : 0;
13
+ return {
14
+ persona_id: personaId,
15
+ count,
16
+ conversations,
17
+ ...(typeof result.pagination_token === "string" && {
18
+ pagination_token: result.pagination_token,
19
+ }),
20
+ _tip: count > 0
21
+ ? "Use conversation(method='messages', conversation_id='<id>') to view messages."
22
+ : "No conversations found for this persona.",
23
+ };
24
+ }
25
+ // List all conversations for the current user
26
+ const conversations = await client.listConversations();
27
+ return {
28
+ count: conversations.length,
29
+ conversations: conversations.map((c) => ({
30
+ id: c.id,
31
+ display_name: c.display_name,
32
+ persona_id: c.persona_id,
33
+ persona_display_name: c.persona_display_name,
34
+ created_at: c.created_at,
35
+ updated_at: c.updated_at,
36
+ status: c.status,
37
+ })),
38
+ _tip: "Use conversation(method='messages', conversation_id='<id>') to view messages in a conversation.",
39
+ };
40
+ }
@@ -0,0 +1,13 @@
1
+ export async function handleMessages(args, client) {
2
+ const conversationId = args.conversation_id;
3
+ if (!conversationId) {
4
+ return { error: "Missing required parameter: conversation_id" };
5
+ }
6
+ const messages = await client.getConversationMessages(conversationId);
7
+ return {
8
+ conversation_id: conversationId,
9
+ message_count: messages.length,
10
+ messages,
11
+ _tip: "Use conversation(method='send', conversation_id='...', text='...') to continue the conversation.",
12
+ };
13
+ }
@@ -0,0 +1,16 @@
1
+ export async function handleRename(args, client) {
2
+ const conversationId = args.conversation_id;
3
+ if (!conversationId) {
4
+ return { error: "Missing required parameter: conversation_id" };
5
+ }
6
+ const displayName = args.display_name;
7
+ if (!displayName) {
8
+ return { error: "Missing required parameter: display_name" };
9
+ }
10
+ await client.updateConversationDisplayName(conversationId, displayName);
11
+ return {
12
+ success: true,
13
+ conversation_id: conversationId,
14
+ display_name: displayName,
15
+ };
16
+ }