@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.
- package/.context/public/guides/ema-user-guide.md +7 -6
- package/.context/public/guides/mcp-tools-guide.md +46 -23
- package/dist/config/index.js +11 -0
- package/dist/config/workflow-patterns.js +361 -0
- package/dist/mcp/autobuilder.js +2 -2
- package/dist/mcp/domain/generation-schema.js +15 -9
- package/dist/mcp/domain/structural-rules.js +3 -3
- package/dist/mcp/domain/validation-rules.js +20 -27
- package/dist/mcp/domain/workflow-generator.js +3 -3
- package/dist/mcp/domain/workflow-graph.js +1 -1
- package/dist/mcp/guidance.js +60 -1
- package/dist/mcp/handlers/conversation/adapter.js +13 -0
- package/dist/mcp/handlers/conversation/create.js +19 -0
- package/dist/mcp/handlers/conversation/delete.js +18 -0
- package/dist/mcp/handlers/conversation/formatters.js +62 -0
- package/dist/mcp/handlers/conversation/history.js +15 -0
- package/dist/mcp/handlers/conversation/index.js +43 -0
- package/dist/mcp/handlers/conversation/list.js +40 -0
- package/dist/mcp/handlers/conversation/messages.js +13 -0
- package/dist/mcp/handlers/conversation/rename.js +16 -0
- package/dist/mcp/handlers/conversation/send.js +90 -0
- package/dist/mcp/handlers/data/index.js +169 -3
- package/dist/mcp/handlers/feedback/client-id.js +49 -0
- package/dist/mcp/handlers/feedback/coalesce.js +167 -0
- package/dist/mcp/handlers/feedback/index.js +42 -1
- package/dist/mcp/handlers/feedback/outbox.js +301 -0
- package/dist/mcp/handlers/feedback/probes.js +127 -0
- package/dist/mcp/handlers/feedback/remote-store.js +59 -0
- package/dist/mcp/handlers/feedback/store.js +13 -1
- package/dist/mcp/handlers/persona/delete.js +7 -28
- package/dist/mcp/handlers/persona/update.js +7 -26
- package/dist/mcp/handlers/persona/version.js +30 -15
- package/dist/mcp/handlers/template/adapter.js +23 -0
- package/dist/mcp/handlers/template/crud.js +174 -0
- package/dist/mcp/handlers/template/index.js +6 -7
- package/dist/mcp/handlers/workflow/adapter.js +30 -46
- package/dist/mcp/handlers/workflow/index.js +2 -2
- package/dist/mcp/handlers/workflow/validation.js +2 -2
- package/dist/mcp/knowledge-guidance-topics.js +90 -53
- package/dist/mcp/knowledge.js +7 -357
- package/dist/mcp/prompts.js +5 -5
- package/dist/mcp/resources-dynamic.js +46 -38
- package/dist/mcp/resources-validation.js +5 -5
- package/dist/mcp/server.js +38 -5
- package/dist/mcp/tools.js +340 -8
- package/dist/sdk/client-adapter.js +90 -2
- package/dist/sdk/client.js +7 -0
- package/dist/sdk/ema-client.js +242 -27
- package/dist/sdk/generated/agent-catalog.js +96 -39
- package/dist/sdk/generated/deprecated-actions.js +1 -1
- package/dist/sdk/grpc-client.js +67 -5
- package/dist/sync/central-factory.js +86 -0
- package/dist/sync/central-version-storage.js +387 -0
- package/dist/sync/dis-port.js +75 -0
- package/dist/sync/version-policy.js +29 -31
- package/dist/sync/version-storage-interface.js +11 -0
- package/dist/sync/version-storage.js +22 -22
- 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
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
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 +=
|
|
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
|
-
|
|
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: "
|
|
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,
|
|
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**:
|
|
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
|
|
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: "
|
|
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
|
|
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
|
|
457
|
-
pattern: "Chat workflow where chat_conversation is
|
|
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
|
|
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
|
|
464
|
-
|
|
465
|
-
"
|
|
466
|
-
|
|
467
|
-
"• For
|
|
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 →
|
|
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 →
|
|
476
|
-
" ❌ chat_trigger →
|
|
469
|
+
" ❌ chat_trigger → search → respond_for_external_actions (wrong — that's for tool results only)\n" +
|
|
470
|
+
" ❌ chat_trigger → entity_extraction → call_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: "
|
|
602
|
-
currentState: "Using call_llm for search-based responses",
|
|
603
|
-
recommendation: "
|
|
604
|
-
benefit: "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
29
|
+
"combine_and_rerank_search_results",
|
|
30
30
|
"text_categorizer",
|
|
31
31
|
"chat_categorizer",
|
|
32
32
|
"response_validator",
|
package/dist/mcp/guidance.js
CHANGED
|
@@ -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
|
+
}
|