@ema.co/mcp-toolkit 1.5.0 → 1.5.2
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.
- package/dist/mcp/handlers-consolidated.js +163 -8
- package/dist/mcp/prompts.js +80 -123
- package/dist/mcp/server.js +133 -6
- package/dist/mcp/tools-consolidated.js +89 -73
- package/dist/sdk/demo-generator.js +978 -0
- package/dist/sdk/workflow-generator.js +120 -25
- package/dist/sdk/workflow-intent.js +9 -0
- package/dist/sdk/workflow-transformer.js +3 -1
- package/docs/mcp-tools-guide.md +27 -15
- package/docs/test-persona-creation.md +196 -0
- package/package.json +1 -1
- package/resources/templates/demo-scenarios/README.md +63 -0
|
@@ -238,12 +238,13 @@ export async function handlePersona(args, client, getTemplateId, createClientFor
|
|
|
238
238
|
}
|
|
239
239
|
// IMPORTANT: The Ema API requires workflow to be sent along with proto_config
|
|
240
240
|
// for proto_config changes to persist. This matches what the UI does.
|
|
241
|
+
const workflowToSet = args.workflow ?? existingWorkflow;
|
|
241
242
|
await client.updateAiEmployee({
|
|
242
243
|
persona_id: persona.id,
|
|
243
244
|
name: args.name,
|
|
244
245
|
description: args.description,
|
|
245
246
|
proto_config: mergedProtoConfig,
|
|
246
|
-
workflow:
|
|
247
|
+
workflow: workflowToSet, // Use provided workflow or existing workflow for proto_config to persist
|
|
247
248
|
enabled_by_user: typeof args.enabled === "boolean" ? args.enabled : undefined,
|
|
248
249
|
});
|
|
249
250
|
return {
|
|
@@ -544,7 +545,7 @@ export async function handlePersona(args, client, getTemplateId, createClientFor
|
|
|
544
545
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
545
546
|
// WORKFLOW Handler - Unified greenfield/brownfield operations
|
|
546
547
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
547
|
-
export async function handleWorkflow(args, client) {
|
|
548
|
+
export async function handleWorkflow(args, client, getTemplateId) {
|
|
548
549
|
const personaId = args.persona_id;
|
|
549
550
|
const workflowDef = args.workflow_def;
|
|
550
551
|
const input = args.input;
|
|
@@ -570,8 +571,12 @@ export async function handleWorkflow(args, client) {
|
|
|
570
571
|
// Auto-fix issues
|
|
571
572
|
effectiveMode = "optimize";
|
|
572
573
|
}
|
|
574
|
+
else if (personaId && workflowDef) {
|
|
575
|
+
// DEPLOY: Persona exists + workflow_def provided → deploy directly (no input needed)
|
|
576
|
+
effectiveMode = "deploy";
|
|
577
|
+
}
|
|
573
578
|
else if (personaId && input) {
|
|
574
|
-
//
|
|
579
|
+
// MODIFY: Persona exists + input provided → generate new workflow from input
|
|
575
580
|
effectiveMode = "modify"; // New unified mode for all brownfield changes
|
|
576
581
|
}
|
|
577
582
|
else if (input && !personaId) {
|
|
@@ -589,8 +594,10 @@ export async function handleWorkflow(args, client) {
|
|
|
589
594
|
const legacyMode = args.mode;
|
|
590
595
|
if (legacyMode === "extend")
|
|
591
596
|
effectiveMode = "modify";
|
|
592
|
-
if (legacyMode === "deploy"
|
|
593
|
-
|
|
597
|
+
if (legacyMode === "deploy") {
|
|
598
|
+
// If explicit deploy mode, use it (works with or without input)
|
|
599
|
+
effectiveMode = "deploy";
|
|
600
|
+
}
|
|
594
601
|
switch (effectiveMode) {
|
|
595
602
|
case "extraction_schema": {
|
|
596
603
|
// Generate extraction schema from JSON metadata (fully dynamic, no hardcoding)
|
|
@@ -783,7 +790,95 @@ export async function handleWorkflow(args, client) {
|
|
|
783
790
|
result.deployed_to = { persona_id: personaId, persona_name: persona.name };
|
|
784
791
|
}
|
|
785
792
|
else if (!preview && !personaId) {
|
|
786
|
-
|
|
793
|
+
// GREENFIELD: Create new persona from template, then update proto_config only
|
|
794
|
+
// The template provides a valid workflow structure - we don't replace it,
|
|
795
|
+
// we just configure the persona settings (voice config, welcome message, etc.)
|
|
796
|
+
const personaName = args.name;
|
|
797
|
+
if (personaName) {
|
|
798
|
+
const personaType = args.type || "chat";
|
|
799
|
+
const DEFAULT_TEMPLATES = {
|
|
800
|
+
voice: "00000000-0000-0000-0000-00000000001e", // Voice AI template
|
|
801
|
+
chat: "00000000-0000-0000-0000-000000000004", // Chat AI template
|
|
802
|
+
dashboard: "00000000-0000-0000-0000-000000000002", // Dashboard AI template
|
|
803
|
+
};
|
|
804
|
+
const templateId = getTemplateId?.(personaType) || DEFAULT_TEMPLATES[personaType];
|
|
805
|
+
if (!templateId) {
|
|
806
|
+
return {
|
|
807
|
+
error: `No template found for type "${personaType}". Provide template_id or use type: voice, chat, or dashboard.`
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
// Step 1: Create the persona from template
|
|
811
|
+
const createResult = await client.createAiEmployee({
|
|
812
|
+
name: personaName,
|
|
813
|
+
description: args.description,
|
|
814
|
+
template_id: templateId,
|
|
815
|
+
});
|
|
816
|
+
const newPersonaId = createResult.persona_id ?? createResult.id;
|
|
817
|
+
if (!newPersonaId) {
|
|
818
|
+
return { error: "Failed to create persona: no ID returned" };
|
|
819
|
+
}
|
|
820
|
+
// Step 2: Fetch the newly created persona to get template's valid structure
|
|
821
|
+
const newPersona = await client.getPersonaById(newPersonaId);
|
|
822
|
+
if (!newPersona) {
|
|
823
|
+
return { error: `Failed to fetch newly created persona: ${newPersonaId}` };
|
|
824
|
+
}
|
|
825
|
+
// Step 3: Merge proto_config - keep template structure, update widget values
|
|
826
|
+
const existingProtoConfig = (newPersona.proto_config ?? {});
|
|
827
|
+
const generatedProtoConfig = args.proto_config || compiled.proto_config || {};
|
|
828
|
+
// Merge widgets by name - template widgets provide structure, generated values override
|
|
829
|
+
const existingWidgets = (existingProtoConfig.widgets ?? []);
|
|
830
|
+
const generatedWidgets = (generatedProtoConfig.widgets ?? []);
|
|
831
|
+
const widgetMap = new Map();
|
|
832
|
+
// Start with template widgets (valid structure)
|
|
833
|
+
for (const w of existingWidgets) {
|
|
834
|
+
if (typeof w.name === "string" && w.name.trim().length > 0) {
|
|
835
|
+
widgetMap.set(w.name, w);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
// Merge generated widget VALUES into template structure
|
|
839
|
+
for (const genWidget of generatedWidgets) {
|
|
840
|
+
const widgetName = genWidget.name;
|
|
841
|
+
if (typeof widgetName === "string" && widgetName.trim().length > 0) {
|
|
842
|
+
const existing = widgetMap.get(widgetName);
|
|
843
|
+
if (existing) {
|
|
844
|
+
// Merge: keep template structure, update config values
|
|
845
|
+
const merged = { ...existing };
|
|
846
|
+
// The widget config is stored under the widget name (e.g., conversationSettings)
|
|
847
|
+
if (genWidget[widgetName]) {
|
|
848
|
+
merged[widgetName] = { ...(existing[widgetName] || {}), ...genWidget[widgetName] };
|
|
849
|
+
}
|
|
850
|
+
widgetMap.set(widgetName, merged);
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
// New widget not in template - add as-is
|
|
854
|
+
widgetMap.set(widgetName, genWidget);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
const mergedProtoConfig = {
|
|
859
|
+
...existingProtoConfig,
|
|
860
|
+
widgets: Array.from(widgetMap.values()),
|
|
861
|
+
};
|
|
862
|
+
// Step 4: Update persona with merged proto_config only
|
|
863
|
+
// DO NOT send workflow - keep the template's valid workflow structure
|
|
864
|
+
await client.updateAiEmployee({
|
|
865
|
+
persona_id: newPersonaId,
|
|
866
|
+
proto_config: mergedProtoConfig,
|
|
867
|
+
});
|
|
868
|
+
result.deployed_to = {
|
|
869
|
+
persona_id: newPersonaId,
|
|
870
|
+
persona_name: personaName,
|
|
871
|
+
created: true
|
|
872
|
+
};
|
|
873
|
+
result.status = "deployed";
|
|
874
|
+
result.next_steps = [
|
|
875
|
+
`Persona "${personaName}" created with template workflow.`,
|
|
876
|
+
`To customize the workflow, use: workflow(persona_id="${newPersonaId}", input="add search node", preview=false)`,
|
|
877
|
+
];
|
|
878
|
+
}
|
|
879
|
+
else {
|
|
880
|
+
result.hint = "Provide name to create new persona, or persona_id to deploy to existing persona";
|
|
881
|
+
}
|
|
787
882
|
}
|
|
788
883
|
if (preview) {
|
|
789
884
|
result.next_steps = [
|
|
@@ -959,8 +1054,68 @@ export async function handleWorkflow(args, client) {
|
|
|
959
1054
|
}
|
|
960
1055
|
return result;
|
|
961
1056
|
}
|
|
962
|
-
|
|
963
|
-
|
|
1057
|
+
case "deploy": {
|
|
1058
|
+
// DEPLOY: Direct workflow deployment to existing persona (no input required)
|
|
1059
|
+
if (!personaId) {
|
|
1060
|
+
return { error: "persona_id required for deploy mode" };
|
|
1061
|
+
}
|
|
1062
|
+
if (!workflowDef) {
|
|
1063
|
+
return { error: "workflow_def required for deploy mode" };
|
|
1064
|
+
}
|
|
1065
|
+
const persona = await client.getPersonaById(personaId);
|
|
1066
|
+
if (!persona) {
|
|
1067
|
+
return { error: `Persona not found: ${personaId}` };
|
|
1068
|
+
}
|
|
1069
|
+
// Sanitize workflow before deployment
|
|
1070
|
+
const sanitizedWorkflow = sanitizeWorkflowForDeploy(workflowDef);
|
|
1071
|
+
// Determine proto_config to use: provided > existing
|
|
1072
|
+
const existingProtoConfig = persona.proto_config;
|
|
1073
|
+
const providedProtoConfig = args.proto_config;
|
|
1074
|
+
const protoConfigToUse = providedProtoConfig || existingProtoConfig || {};
|
|
1075
|
+
// Check if this is a voice persona and warn if critical settings are missing
|
|
1076
|
+
const projectSettings = protoConfigToUse.projectSettings;
|
|
1077
|
+
const isVoice = projectSettings?.projectType === 5;
|
|
1078
|
+
const warnings = [];
|
|
1079
|
+
if (isVoice && !providedProtoConfig) {
|
|
1080
|
+
// Check if existing proto_config has conversationSettings populated
|
|
1081
|
+
const widgets = (protoConfigToUse.widgets ?? []);
|
|
1082
|
+
const convSettings = widgets.find(w => w.name === "conversationSettings");
|
|
1083
|
+
const convConfig = convSettings?.conversationSettings;
|
|
1084
|
+
if (!convConfig?.welcomeMessage || !convConfig?.identityAndPurpose) {
|
|
1085
|
+
warnings.push("Voice persona detected but proto_config not provided. Voice settings (welcomeMessage, identityAndPurpose, etc.) may be empty or generic. " +
|
|
1086
|
+
"If you generated the workflow with workflow(input=...), pass the generated proto_config along with workflow_def to preserve voice settings.");
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
// Deploy workflow (always deploy, preview is not applicable for explicit deploy mode)
|
|
1090
|
+
try {
|
|
1091
|
+
await client.updateAiEmployee({
|
|
1092
|
+
persona_id: personaId,
|
|
1093
|
+
workflow: sanitizedWorkflow,
|
|
1094
|
+
proto_config: protoConfigToUse,
|
|
1095
|
+
});
|
|
1096
|
+
const result = {
|
|
1097
|
+
mode: "deploy",
|
|
1098
|
+
status: "deployed",
|
|
1099
|
+
persona_id: personaId,
|
|
1100
|
+
persona_name: persona.name,
|
|
1101
|
+
workflow_deployed: true,
|
|
1102
|
+
};
|
|
1103
|
+
if (warnings.length > 0) {
|
|
1104
|
+
result.warnings = warnings;
|
|
1105
|
+
}
|
|
1106
|
+
return result;
|
|
1107
|
+
}
|
|
1108
|
+
catch (err) {
|
|
1109
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1110
|
+
return {
|
|
1111
|
+
mode: "deploy",
|
|
1112
|
+
status: "failed",
|
|
1113
|
+
persona_id: personaId,
|
|
1114
|
+
persona_name: persona.name,
|
|
1115
|
+
error: errorMessage,
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
964
1119
|
case "compare": {
|
|
965
1120
|
if (!personaId || !args.compare_to) {
|
|
966
1121
|
return { error: "persona_id and compare_to required for compare mode" };
|
package/dist/mcp/prompts.js
CHANGED
|
@@ -1147,12 +1147,12 @@ Now begin by reading the resources, then proceed with generation.`,
|
|
|
1147
1147
|
],
|
|
1148
1148
|
},
|
|
1149
1149
|
// ─────────────────────────────────────────────────────────────────────────
|
|
1150
|
-
// Direct Workflow Generation
|
|
1150
|
+
// Direct Workflow Generation
|
|
1151
1151
|
// ─────────────────────────────────────────────────────────────────────────
|
|
1152
1152
|
workflow_generate: {
|
|
1153
1153
|
definition: {
|
|
1154
1154
|
name: "workflow_generate",
|
|
1155
|
-
description: "Generate
|
|
1155
|
+
description: "Generate an AI Employee workflow. Preview first, then deploy when ready.",
|
|
1156
1156
|
arguments: [
|
|
1157
1157
|
{
|
|
1158
1158
|
name: "name",
|
|
@@ -1161,29 +1161,14 @@ Now begin by reading the resources, then proceed with generation.`,
|
|
|
1161
1161
|
},
|
|
1162
1162
|
{
|
|
1163
1163
|
name: "description",
|
|
1164
|
-
description: "What the AI Employee does",
|
|
1164
|
+
description: "What the AI Employee does (be detailed about capabilities, intents, data sources)",
|
|
1165
1165
|
required: true,
|
|
1166
1166
|
},
|
|
1167
1167
|
{
|
|
1168
|
-
name: "
|
|
1168
|
+
name: "type",
|
|
1169
1169
|
description: "Type: 'voice', 'chat', or 'dashboard'",
|
|
1170
1170
|
required: true,
|
|
1171
1171
|
},
|
|
1172
|
-
{
|
|
1173
|
-
name: "pattern",
|
|
1174
|
-
description: "Workflow pattern: 'kb_search' (FAQ/docs), 'intent_routing' (multi-intent), 'tool_calling' (external actions)",
|
|
1175
|
-
required: true,
|
|
1176
|
-
},
|
|
1177
|
-
{
|
|
1178
|
-
name: "intents",
|
|
1179
|
-
description: "For intent_routing: comma-separated intent names with handler type, e.g., 'Search:search,Help:llm,Transfer:fixed'",
|
|
1180
|
-
required: false,
|
|
1181
|
-
},
|
|
1182
|
-
{
|
|
1183
|
-
name: "tools",
|
|
1184
|
-
description: "For tool_calling: comma-separated tool names with namespace, e.g., 'Create_Ticket:service_now,Send_Email:outlook'",
|
|
1185
|
-
required: false,
|
|
1186
|
-
},
|
|
1187
1172
|
],
|
|
1188
1173
|
},
|
|
1189
1174
|
render: (args) => [
|
|
@@ -1191,59 +1176,45 @@ Now begin by reading the resources, then proceed with generation.`,
|
|
|
1191
1176
|
role: "user",
|
|
1192
1177
|
content: {
|
|
1193
1178
|
type: "text",
|
|
1194
|
-
text: `Generate
|
|
1179
|
+
text: `Generate AI Employee: ${args.name}
|
|
1195
1180
|
|
|
1181
|
+
**Type**: ${args.type}
|
|
1196
1182
|
**Description**: ${args.description}
|
|
1197
|
-
**Persona Type**: ${args.persona_type}
|
|
1198
|
-
**Pattern**: ${args.pattern}
|
|
1199
|
-
${args.intents ? `**Intents**: ${args.intents}` : ""}
|
|
1200
|
-
${args.tools ? `**Tools**: ${args.tools}` : ""}
|
|
1201
|
-
|
|
1202
|
-
## Instructions
|
|
1203
|
-
|
|
1204
|
-
Generate deployment-ready JSON using the consolidated \`workflow\` tool (local compiler):
|
|
1205
1183
|
|
|
1206
|
-
|
|
1207
|
-
Call:
|
|
1208
|
-
\`workflow(input=<requirements>, type="${args.persona_type}", use_autobuilder=false)\`
|
|
1184
|
+
## Step 1: Preview the workflow
|
|
1209
1185
|
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
**HITL Policy**: DEFAULT is no approval gates. Only add HITL if user explicitly requests approval/confirmation before external actions.
|
|
1218
|
-
|
|
1219
|
-
If the tool returns \`status="needs_input"\`, ask the missing questions, then call \`workflow(...)\` again with the additional details.
|
|
1220
|
-
|
|
1221
|
-
### Step 2: (Optional) Validate the generated workflow
|
|
1222
|
-
Call \`workflow(mode="analyze", workflow_def=<workflow_def>)\` and fix any critical issues before deploying.
|
|
1186
|
+
\`\`\`
|
|
1187
|
+
workflow(
|
|
1188
|
+
input="${args.description}",
|
|
1189
|
+
type="${args.type}"
|
|
1190
|
+
)
|
|
1191
|
+
\`\`\`
|
|
1223
1192
|
|
|
1224
|
-
|
|
1225
|
-
If you already have a persona, deploy directly:
|
|
1226
|
-
\`workflow(mode="deploy", persona_id="<persona_id>", workflow_def=<workflow_def>, proto_config=<proto_config>)\`
|
|
1193
|
+
This returns workflow_def and proto_config. Review them before deploying.
|
|
1227
1194
|
|
|
1228
|
-
|
|
1229
|
-
\`persona(mode="create", name="${args.name}", type="${args.persona_type}")\`
|
|
1230
|
-
then deploy as above.
|
|
1195
|
+
## Step 2: Deploy when ready
|
|
1231
1196
|
|
|
1232
|
-
|
|
1197
|
+
\`\`\`
|
|
1198
|
+
workflow(
|
|
1199
|
+
input="${args.description}",
|
|
1200
|
+
name="${args.name}",
|
|
1201
|
+
type="${args.type}",
|
|
1202
|
+
preview=false
|
|
1203
|
+
)
|
|
1204
|
+
\`\`\`
|
|
1233
1205
|
|
|
1234
|
-
|
|
1206
|
+
This creates the persona and deploys the workflow in one step.
|
|
1235
1207
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
{workflow_def}
|
|
1239
|
-
\`\`\`
|
|
1208
|
+
${args.type === "voice" ? `
|
|
1209
|
+
## Voice AI Notes
|
|
1240
1210
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1211
|
+
The generated proto_config includes voice settings:
|
|
1212
|
+
- welcomeMessage: Generated greeting
|
|
1213
|
+
- identityAndPurpose: Generated from description
|
|
1214
|
+
- takeActionInstructions, hangupInstructions: Sensible defaults
|
|
1245
1215
|
|
|
1246
|
-
|
|
1216
|
+
These are set automatically but can be customized via proto_config override.
|
|
1217
|
+
` : ""}`,
|
|
1247
1218
|
},
|
|
1248
1219
|
},
|
|
1249
1220
|
],
|
|
@@ -1251,7 +1222,7 @@ Now generate by calling \`workflow(input=<requirements>, type="${args.persona_ty
|
|
|
1251
1222
|
workflow_deploy: {
|
|
1252
1223
|
definition: {
|
|
1253
1224
|
name: "workflow_deploy",
|
|
1254
|
-
description: "
|
|
1225
|
+
description: "Create a new AI Employee from template with configured settings, or modify an existing one.",
|
|
1255
1226
|
arguments: [
|
|
1256
1227
|
{
|
|
1257
1228
|
name: "name",
|
|
@@ -1259,13 +1230,18 @@ Now generate by calling \`workflow(input=<requirements>, type="${args.persona_ty
|
|
|
1259
1230
|
required: true,
|
|
1260
1231
|
},
|
|
1261
1232
|
{
|
|
1262
|
-
name: "
|
|
1263
|
-
description: "
|
|
1264
|
-
required:
|
|
1233
|
+
name: "type",
|
|
1234
|
+
description: "Type: voice, chat, or dashboard",
|
|
1235
|
+
required: true,
|
|
1236
|
+
},
|
|
1237
|
+
{
|
|
1238
|
+
name: "description",
|
|
1239
|
+
description: "What this AI Employee does",
|
|
1240
|
+
required: true,
|
|
1265
1241
|
},
|
|
1266
1242
|
{
|
|
1267
|
-
name: "
|
|
1268
|
-
description: "
|
|
1243
|
+
name: "env",
|
|
1244
|
+
description: "Target environment (default: demo)",
|
|
1269
1245
|
required: false,
|
|
1270
1246
|
},
|
|
1271
1247
|
],
|
|
@@ -1275,70 +1251,51 @@ Now generate by calling \`workflow(input=<requirements>, type="${args.persona_ty
|
|
|
1275
1251
|
role: "user",
|
|
1276
1252
|
content: {
|
|
1277
1253
|
type: "text",
|
|
1278
|
-
text: `
|
|
1279
|
-
|
|
1280
|
-
**Target Environment**: ${args.env || "demo"}
|
|
1281
|
-
**Update Existing**: ${args.update_existing === "true" ? "Yes" : "No (create new)"}
|
|
1282
|
-
|
|
1283
|
-
## Instructions
|
|
1284
|
-
|
|
1285
|
-
### Pre-Deploy Checklist
|
|
1286
|
-
|
|
1287
|
-
Before deploying, ensure you have:
|
|
1288
|
-
1. ✅ Generated workflow_def using workflow_generate prompt
|
|
1289
|
-
2. ✅ Generated proto_config/persona settings
|
|
1290
|
-
3. ✅ Validated the workflow (no critical issues)
|
|
1291
|
-
|
|
1292
|
-
### Step 1: Check for Existing
|
|
1254
|
+
text: `Create AI Employee: ${args.name}
|
|
1293
1255
|
|
|
1294
|
-
${args.
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
` : `
|
|
1298
|
-
Call \`persona(query="${args.name}", env="${args.env || "demo"}")\`
|
|
1299
|
-
to check whether an AI Employee with this name already exists (and capture the persona ID if present).
|
|
1300
|
-
`}
|
|
1301
|
-
|
|
1302
|
-
### Step 2: Create or Update
|
|
1303
|
-
|
|
1304
|
-
${args.update_existing === "true" ? `
|
|
1305
|
-
If not found, create it:
|
|
1306
|
-
\`persona(mode="create", name="${args.name}", type="chat", env="${args.env || "demo"}")\`
|
|
1307
|
-
` : `
|
|
1308
|
-
Create a new AI Employee:
|
|
1309
|
-
\`persona(mode="create", name="${args.name}", type="chat", env="${args.env || "demo"}")\`
|
|
1310
|
-
`}
|
|
1311
|
-
|
|
1312
|
-
### Step 3: Configure Workflow
|
|
1313
|
-
|
|
1314
|
-
Deploy the workflow via MCP:
|
|
1315
|
-
\`workflow(mode="deploy", persona_id="<persona_id>", workflow_def=<workflow_def>, proto_config=<proto_config>, env="${args.env || "demo"}")\`
|
|
1256
|
+
**Type**: ${args.type || "chat"}
|
|
1257
|
+
**Environment**: ${args.env || "demo"}
|
|
1258
|
+
**Description**: ${args.description}
|
|
1316
1259
|
|
|
1317
|
-
|
|
1260
|
+
## How It Works
|
|
1318
1261
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
4. Check HITL flows work (if applicable)
|
|
1262
|
+
**Greenfield** (new personas): Creates from template, configures settings.
|
|
1263
|
+
- Template provides valid workflow structure
|
|
1264
|
+
- We configure proto_config (voice settings, welcome message, etc.)
|
|
1265
|
+
- Customize workflow AFTER creation via modify mode
|
|
1324
1266
|
|
|
1325
|
-
|
|
1267
|
+
**Brownfield** (existing personas): Uses LLM-native transformation.
|
|
1268
|
+
- Fetches existing workflow
|
|
1269
|
+
- Decompiles to WorkflowSpec
|
|
1270
|
+
- Transforms based on your input
|
|
1271
|
+
- Compiles back and deploys
|
|
1326
1272
|
|
|
1327
|
-
|
|
1328
|
-
1. Call \`persona(id="<persona_id>", mode="update", enabled=true, env="${args.env || "demo"}")\`
|
|
1329
|
-
2. Monitor initial conversations
|
|
1330
|
-
3. Iterate based on feedback
|
|
1273
|
+
## Create New Persona
|
|
1331
1274
|
|
|
1332
|
-
|
|
1275
|
+
\`\`\`
|
|
1276
|
+
workflow(
|
|
1277
|
+
input="${args.description}",
|
|
1278
|
+
name="${args.name}",
|
|
1279
|
+
type="${args.type || "chat"}",
|
|
1280
|
+
preview=false,
|
|
1281
|
+
env="${args.env || "demo"}"
|
|
1282
|
+
)
|
|
1283
|
+
\`\`\`
|
|
1333
1284
|
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
-
|
|
1338
|
-
-
|
|
1339
|
-
-
|
|
1285
|
+
This creates the persona from template and configures settings.
|
|
1286
|
+
${args.type === "voice" ? `
|
|
1287
|
+
For Voice AI, this sets:
|
|
1288
|
+
- welcomeMessage (generated greeting)
|
|
1289
|
+
- identityAndPurpose (from your description)
|
|
1290
|
+
- hangupInstructions, speechCharacteristics (defaults)
|
|
1291
|
+
` : ""}
|
|
1292
|
+
## After Creation
|
|
1340
1293
|
|
|
1341
|
-
|
|
1294
|
+
1. Get persona_id from response
|
|
1295
|
+
2. Verify: \`persona(id="<persona_id>", include_workflow=true)\`
|
|
1296
|
+
3. Customize workflow: \`workflow(persona_id="<id>", input="add search node")\`
|
|
1297
|
+
4. Test in Ema simulator
|
|
1298
|
+
5. Enable: \`persona(id="<id>", mode="update", enabled=true)\``,
|
|
1342
1299
|
},
|
|
1343
1300
|
},
|
|
1344
1301
|
],
|
package/dist/mcp/server.js
CHANGED
|
@@ -4843,7 +4843,12 @@ const toolHandlers = {
|
|
|
4843
4843
|
// Consolidated workflow handler (replaces legacy inline handler)
|
|
4844
4844
|
workflow: async (args) => {
|
|
4845
4845
|
const client = createClient(args.env);
|
|
4846
|
-
|
|
4846
|
+
const DEFAULT_TEMPLATES = {
|
|
4847
|
+
voice: "00000000-0000-0000-0000-00000000001e", // Voice AI template
|
|
4848
|
+
chat: "00000000-0000-0000-0000-000000000004", // Chat AI template
|
|
4849
|
+
dashboard: "00000000-0000-0000-0000-000000000002", // Dashboard AI template
|
|
4850
|
+
};
|
|
4851
|
+
return handleWorkflow(args, client, (type) => DEFAULT_TEMPLATES[type]);
|
|
4847
4852
|
},
|
|
4848
4853
|
action: async (args) => {
|
|
4849
4854
|
const client = createClient(args.env);
|
|
@@ -4901,14 +4906,16 @@ toolHandlers.workflow = async (args) => {
|
|
|
4901
4906
|
const rawMode = normalizedArgs.mode ? String(normalizedArgs.mode) : undefined;
|
|
4902
4907
|
const mode = rawMode === "improve" ? "optimize" : rawMode;
|
|
4903
4908
|
// Default mode selection:
|
|
4904
|
-
// - persona_id +
|
|
4909
|
+
// - persona_id + workflow_def → deploy (direct deployment, no input needed)
|
|
4910
|
+
// - persona_id + input → modify (BROWNFIELD: generate new workflow from input)
|
|
4905
4911
|
// - persona_id only or workflow_def → analyze (inspect)
|
|
4906
4912
|
// - input only → generate (GREENFIELD: create from scratch)
|
|
4907
4913
|
// - Otherwise → generate (and prompt for missing info)
|
|
4908
4914
|
const effectiveMode = mode ??
|
|
4909
|
-
(personaId &&
|
|
4910
|
-
|
|
4911
|
-
"
|
|
4915
|
+
(personaId && workflowDef ? "deploy" : // Direct deployment (no input required)
|
|
4916
|
+
personaId && inputProvided ? "modify" : // BROWNFIELD: existing persona + new requirements
|
|
4917
|
+
workflowDef || personaId ? "analyze" : // Inspect existing
|
|
4918
|
+
"generate"); // GREENFIELD: new workflow
|
|
4912
4919
|
switch (effectiveMode) {
|
|
4913
4920
|
case "generate": {
|
|
4914
4921
|
const result = await legacyWorkflowTool({ ...normalizedArgs, mode: "generate" });
|
|
@@ -5137,11 +5144,131 @@ toolHandlers.sync = async (args) => {
|
|
|
5137
5144
|
include_status: normalizedArgs.include_status,
|
|
5138
5145
|
});
|
|
5139
5146
|
};
|
|
5140
|
-
// Consolidated demo tool: consolidate | generate | validate | template
|
|
5147
|
+
// Consolidated demo tool: kit | validate_kit | scenarios | consolidate | generate | validate | template
|
|
5141
5148
|
toolHandlers.demo = async (args) => {
|
|
5142
5149
|
const normalizedArgs = { ...(args ?? {}) };
|
|
5143
5150
|
const mode = normalizedArgs.mode ? String(normalizedArgs.mode) : "template";
|
|
5144
5151
|
switch (mode) {
|
|
5152
|
+
case "kit": {
|
|
5153
|
+
// Generate complete demo kit for a persona
|
|
5154
|
+
const personaId = String(normalizedArgs.persona_id ?? "");
|
|
5155
|
+
const scenarioId = String(normalizedArgs.scenario ?? "sales-sdr");
|
|
5156
|
+
if (!personaId) {
|
|
5157
|
+
throw new Error('demo(mode="kit") requires: persona_id');
|
|
5158
|
+
}
|
|
5159
|
+
// Import demo generator
|
|
5160
|
+
const { generateDemoKit, DEMO_SCENARIOS, generateDemoScriptMarkdown, validateDemoKit } = await import("../sdk/demo-generator.js");
|
|
5161
|
+
// Get scenario
|
|
5162
|
+
const scenario = DEMO_SCENARIOS[scenarioId];
|
|
5163
|
+
if (!scenario) {
|
|
5164
|
+
throw new Error(`Unknown scenario: ${scenarioId}. Available: ${Object.keys(DEMO_SCENARIOS).join(", ")}`);
|
|
5165
|
+
}
|
|
5166
|
+
// Get persona and workflow
|
|
5167
|
+
const client = await createClient(normalizedArgs.env);
|
|
5168
|
+
const persona = await client.getPersonaById(personaId);
|
|
5169
|
+
if (!persona) {
|
|
5170
|
+
throw new Error(`Persona not found: ${personaId}`);
|
|
5171
|
+
}
|
|
5172
|
+
const workflowDef = persona.workflow_def || {};
|
|
5173
|
+
const customQA = normalizedArgs.custom_qa;
|
|
5174
|
+
// Generate kit
|
|
5175
|
+
const kit = generateDemoKit(personaId, persona.name || personaId, workflowDef, scenario, customQA);
|
|
5176
|
+
// Generate markdown script
|
|
5177
|
+
const demoScript = generateDemoScriptMarkdown(kit);
|
|
5178
|
+
// Validate
|
|
5179
|
+
const validation = validateDemoKit(kit);
|
|
5180
|
+
return {
|
|
5181
|
+
success: true,
|
|
5182
|
+
persona_id: personaId,
|
|
5183
|
+
persona_name: persona.name,
|
|
5184
|
+
scenario: scenarioId,
|
|
5185
|
+
kit_summary: {
|
|
5186
|
+
kb_documents: kit.kb_documents.length,
|
|
5187
|
+
demo_questions: kit.demo_script.length,
|
|
5188
|
+
fixed_responses: kit.fixed_responses.length,
|
|
5189
|
+
validation_queries: kit.validation_queries.length,
|
|
5190
|
+
},
|
|
5191
|
+
validation,
|
|
5192
|
+
demo_script_preview: demoScript.slice(0, 2000) + (demoScript.length > 2000 ? "\n\n... (truncated)" : ""),
|
|
5193
|
+
kit,
|
|
5194
|
+
instructions: [
|
|
5195
|
+
"1. Upload KB documents to the persona's knowledge base",
|
|
5196
|
+
"2. Review the demo script and practice the questions",
|
|
5197
|
+
"3. Optionally apply fixed_responses for guaranteed fallbacks",
|
|
5198
|
+
"4. Run validation queries to verify demo readiness",
|
|
5199
|
+
"5. Conduct the demo with confidence!",
|
|
5200
|
+
],
|
|
5201
|
+
};
|
|
5202
|
+
}
|
|
5203
|
+
case "validate_kit": {
|
|
5204
|
+
// Validate a persona's demo readiness
|
|
5205
|
+
const personaId = String(normalizedArgs.persona_id ?? "");
|
|
5206
|
+
if (!personaId) {
|
|
5207
|
+
throw new Error('demo(mode="validate_kit") requires: persona_id');
|
|
5208
|
+
}
|
|
5209
|
+
const { analyzeWorkflowForDemo, DEMO_SCENARIOS } = await import("../sdk/demo-generator.js");
|
|
5210
|
+
const client = await createClient(normalizedArgs.env);
|
|
5211
|
+
const persona = await client.getPersonaById(personaId);
|
|
5212
|
+
if (!persona) {
|
|
5213
|
+
throw new Error(`Persona not found: ${personaId}`);
|
|
5214
|
+
}
|
|
5215
|
+
const analysis = analyzeWorkflowForDemo(persona.workflow_def || {});
|
|
5216
|
+
// Check data sources
|
|
5217
|
+
const dataSourcesResult = await client.listDataSourceFiles(personaId);
|
|
5218
|
+
const dataSources = dataSourcesResult.files || [];
|
|
5219
|
+
const hasKnowledgeBase = dataSources.length > 0;
|
|
5220
|
+
const issues = [];
|
|
5221
|
+
if (!hasKnowledgeBase) {
|
|
5222
|
+
issues.push("No knowledge base documents uploaded - RAG search will fail");
|
|
5223
|
+
}
|
|
5224
|
+
if (analysis.intents.length === 0) {
|
|
5225
|
+
issues.push("No categorizer intents detected - workflow may not route correctly");
|
|
5226
|
+
}
|
|
5227
|
+
if (!analysis.has_search) {
|
|
5228
|
+
issues.push("No search nodes detected - cannot retrieve KB data");
|
|
5229
|
+
}
|
|
5230
|
+
// Suggest best scenario
|
|
5231
|
+
let suggestedScenario = "sales-sdr";
|
|
5232
|
+
for (const [id, scenario] of Object.entries(DEMO_SCENARIOS)) {
|
|
5233
|
+
const intentOverlap = scenario.intents.filter(i => analysis.intents.some(ai => ai.toLowerCase().includes(i.name.toLowerCase()))).length;
|
|
5234
|
+
if (intentOverlap > 0) {
|
|
5235
|
+
suggestedScenario = id;
|
|
5236
|
+
break;
|
|
5237
|
+
}
|
|
5238
|
+
}
|
|
5239
|
+
return {
|
|
5240
|
+
persona_id: personaId,
|
|
5241
|
+
persona_name: persona.name,
|
|
5242
|
+
ready: issues.length === 0,
|
|
5243
|
+
issues,
|
|
5244
|
+
workflow_analysis: analysis,
|
|
5245
|
+
knowledge_base: {
|
|
5246
|
+
has_documents: hasKnowledgeBase,
|
|
5247
|
+
document_count: dataSources.length,
|
|
5248
|
+
},
|
|
5249
|
+
suggested_scenario: suggestedScenario,
|
|
5250
|
+
next_steps: issues.length > 0
|
|
5251
|
+
? issues.map((issue, i) => `${i + 1}. Fix: ${issue}`)
|
|
5252
|
+
: [`Generate demo kit: demo(mode="kit", persona_id="${personaId}", scenario="${suggestedScenario}")`],
|
|
5253
|
+
};
|
|
5254
|
+
}
|
|
5255
|
+
case "scenarios": {
|
|
5256
|
+
// List available demo scenarios
|
|
5257
|
+
const { DEMO_SCENARIOS } = await import("../sdk/demo-generator.js");
|
|
5258
|
+
return {
|
|
5259
|
+
scenarios: Object.entries(DEMO_SCENARIOS).map(([id, scenario]) => ({
|
|
5260
|
+
id,
|
|
5261
|
+
name: scenario.name,
|
|
5262
|
+
description: scenario.description,
|
|
5263
|
+
persona_types: scenario.persona_types,
|
|
5264
|
+
tags: scenario.tags,
|
|
5265
|
+
intent_count: scenario.intents.length,
|
|
5266
|
+
qa_count: scenario.qa_pairs.length,
|
|
5267
|
+
entity_types: scenario.entities.map(e => e.type),
|
|
5268
|
+
})),
|
|
5269
|
+
usage: 'demo(mode="kit", persona_id="...", scenario="<scenario_id>")',
|
|
5270
|
+
};
|
|
5271
|
+
}
|
|
5145
5272
|
case "consolidate": {
|
|
5146
5273
|
const source = String(normalizedArgs.source ?? "");
|
|
5147
5274
|
const output = String(normalizedArgs.output ?? "");
|