@ema.co/mcp-toolkit 2026.1.26 → 2026.1.27-10
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/dist/mcp/handlers/action/index.js +17 -20
- package/dist/mcp/handlers/data/index.js +75 -6
- package/dist/mcp/handlers/deprecation.js +50 -0
- package/dist/mcp/handlers/env/index.js +3 -3
- package/dist/mcp/handlers/knowledge/index.js +44 -237
- package/dist/mcp/handlers/persona/create.js +63 -18
- package/dist/mcp/handlers/persona/index.js +9 -10
- package/dist/mcp/handlers/persona/update.js +6 -3
- package/dist/mcp/handlers/reference/index.js +15 -2
- package/dist/mcp/handlers/sync/index.js +3 -18
- package/dist/mcp/handlers/workflow/analyze.js +53 -105
- package/dist/mcp/handlers/workflow/deploy.js +160 -1
- package/dist/mcp/handlers/workflow/generate.js +8 -28
- package/dist/mcp/handlers/workflow/index.js +258 -85
- package/dist/mcp/handlers/workflow/modify.js +69 -26
- package/dist/mcp/handlers/workflow/optimize.js +22 -108
- package/dist/mcp/handlers/workflow/utils.js +0 -102
- package/dist/mcp/handlers-consolidated.js +15 -38
- package/dist/mcp/prompts.js +82 -44
- package/dist/mcp/resources.js +335 -3
- package/dist/mcp/server.js +242 -457
- package/dist/mcp/tools.js +44 -61
- package/dist/sdk/action-schema-parser.js +11 -5
- package/dist/sdk/client.js +81 -25
- package/dist/sdk/ema-client.js +11 -0
- package/dist/sdk/generated/deprecated-actions.js +171 -0
- package/dist/sdk/guidance.js +58 -35
- package/dist/sdk/index.js +8 -7
- package/dist/sdk/knowledge.js +216 -1932
- package/dist/sdk/quality-gates.js +60 -336
- package/dist/sdk/validation-rules.js +33 -0
- package/dist/sdk/workflow-fixer.js +29 -360
- package/dist/sdk/workflow-generator.js +2 -1
- package/dist/sdk/workflow-intent.js +43 -3
- package/dist/sdk/workflow-transformer.js +0 -342
- package/docs/dashboard-operations.md +35 -0
- package/docs/ema-user-guide.md +66 -0
- package/docs/mcp-tools-guide.md +74 -45
- package/package.json +2 -2
- package/dist/mcp/handlers/persona/analyze.js +0 -275
- package/dist/mcp/handlers/persona/compare.js +0 -32
- package/dist/mcp/handlers/workflow/compile.js +0 -39
- package/docs/DEBUG-ANALYSIS-unused-category-type-mismatch.md +0 -481
- package/docs/TODO-fix-analyzer-and-modify.md +0 -182
- package/resources/action-schema.json +0 -5678
|
@@ -655,345 +655,3 @@ Return ONLY the modified WorkflowSpec JSON, no explanation needed:
|
|
|
655
655
|
\`\`\`json
|
|
656
656
|
`;
|
|
657
657
|
}
|
|
658
|
-
/**
|
|
659
|
-
* @deprecated MCP should not parse natural language - the Agent IS the LLM.
|
|
660
|
-
* Use agent reasoning instead. Will be removed in future version.
|
|
661
|
-
*
|
|
662
|
-
* Parse natural language intent into deterministic operations.
|
|
663
|
-
* Returns operations that can be executed directly, or indicates LLM is needed.
|
|
664
|
-
*/
|
|
665
|
-
export function parseIntent(input, spec) {
|
|
666
|
-
const ops = [];
|
|
667
|
-
const lowerInput = input.toLowerCase();
|
|
668
|
-
const nodeIds = spec.nodes.map(n => n.id);
|
|
669
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
670
|
-
// REMOVE NODE: "remove node X", "delete X", "remove X"
|
|
671
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
672
|
-
if (lowerInput.includes("remove") || lowerInput.includes("delete")) {
|
|
673
|
-
// Strategy 1: Exact node ID in input
|
|
674
|
-
for (const nodeId of nodeIds) {
|
|
675
|
-
if (input.includes(nodeId)) {
|
|
676
|
-
ops.push({ type: "remove", nodeId });
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
// Strategy 2: "remove the node X" pattern
|
|
680
|
-
if (ops.length === 0) {
|
|
681
|
-
const match = input.match(/(?:remove|delete)\s+(?:the\s+)?node\s+["']?([a-zA-Z0-9_]+)["']?/i);
|
|
682
|
-
if (match) {
|
|
683
|
-
const nodeId = nodeIds.find(id => id.toLowerCase() === match[1].toLowerCase());
|
|
684
|
-
if (nodeId) {
|
|
685
|
-
ops.push({ type: "remove", nodeId });
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
691
|
-
// RENAME NODE: "rename X to Y", "change displayName of X to Y"
|
|
692
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
693
|
-
if (lowerInput.includes("rename") || lowerInput.includes("displayname")) {
|
|
694
|
-
const match = input.match(/(?:rename|change\s+displayName\s+of)\s+["']?([a-zA-Z0-9_]+)["']?\s+to\s+["']?([^"'\n]+)["']?/i);
|
|
695
|
-
if (match) {
|
|
696
|
-
const nodeId = nodeIds.find(id => id.toLowerCase() === match[1].toLowerCase());
|
|
697
|
-
if (nodeId) {
|
|
698
|
-
ops.push({ type: "rename", nodeId, newDisplayName: match[2].trim() });
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
703
|
-
// ADD RUNIF: "add runIf to X when Y.Z equals V"
|
|
704
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
705
|
-
if (lowerInput.includes("runif") || lowerInput.includes("conditional") ||
|
|
706
|
-
(lowerInput.includes("only run") && lowerInput.includes("when"))) {
|
|
707
|
-
// Pattern: "add runIf to NODE when SOURCE.OUTPUT equals VALUE"
|
|
708
|
-
const match = input.match(/(?:add\s+)?runIf\s+to\s+["']?([a-zA-Z0-9_]+)["']?\s+when\s+["']?([a-zA-Z0-9_]+)["']?\.["']?([a-zA-Z0-9_]+)["']?\s+(?:equals?|==|eq)\s+["']?([^"'\n]+)["']?/i);
|
|
709
|
-
if (match) {
|
|
710
|
-
const nodeId = nodeIds.find(id => id.toLowerCase() === match[1].toLowerCase());
|
|
711
|
-
const sourceAction = nodeIds.find(id => id.toLowerCase() === match[2].toLowerCase());
|
|
712
|
-
if (nodeId && sourceAction) {
|
|
713
|
-
ops.push({
|
|
714
|
-
type: "add_runif",
|
|
715
|
-
nodeId,
|
|
716
|
-
condition: {
|
|
717
|
-
sourceAction,
|
|
718
|
-
sourceOutput: match[3],
|
|
719
|
-
operator: "eq",
|
|
720
|
-
value: match[4].trim(),
|
|
721
|
-
},
|
|
722
|
-
});
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
727
|
-
// ADD TO RESULTS: "add X to results", "include X in output"
|
|
728
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
729
|
-
if (lowerInput.includes("add") && (lowerInput.includes("result") || lowerInput.includes("output"))) {
|
|
730
|
-
for (const nodeId of nodeIds) {
|
|
731
|
-
if (input.includes(nodeId)) {
|
|
732
|
-
// Determine output name based on node type
|
|
733
|
-
const node = spec.nodes.find(n => n.id === nodeId);
|
|
734
|
-
const output = node?.actionType === "search" ? "search_results" : "response_with_sources";
|
|
735
|
-
ops.push({ type: "add_to_results", nodeId, output });
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
// If we found deterministic ops, return them
|
|
740
|
-
if (ops.length > 0) {
|
|
741
|
-
return { ops, needs_llm: false };
|
|
742
|
-
}
|
|
743
|
-
// Otherwise, this needs LLM for complex interpretation
|
|
744
|
-
return {
|
|
745
|
-
ops: [],
|
|
746
|
-
needs_llm: true,
|
|
747
|
-
reason: "Could not parse deterministic operations from input. LLM interpretation needed."
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
/**
|
|
751
|
-
* @deprecated MCP should not apply operations - the Agent builds workflow_spec.
|
|
752
|
-
* Will be removed in future version.
|
|
753
|
-
*
|
|
754
|
-
* Apply deterministic modifications to a WorkflowSpec.
|
|
755
|
-
*/
|
|
756
|
-
export function applyOperations(spec, ops) {
|
|
757
|
-
// Deep clone to avoid mutations
|
|
758
|
-
const newSpec = JSON.parse(JSON.stringify(spec));
|
|
759
|
-
const changes = [];
|
|
760
|
-
const errors = [];
|
|
761
|
-
for (const op of ops) {
|
|
762
|
-
switch (op.type) {
|
|
763
|
-
case "remove": {
|
|
764
|
-
const idx = newSpec.nodes.findIndex(n => n.id === op.nodeId);
|
|
765
|
-
if (idx >= 0) {
|
|
766
|
-
newSpec.nodes.splice(idx, 1);
|
|
767
|
-
// Also remove from resultMappings
|
|
768
|
-
newSpec.resultMappings = newSpec.resultMappings.filter(rm => rm.nodeId !== op.nodeId);
|
|
769
|
-
// Clean up any references in other nodes' inputs - REMOVE them, don't just warn
|
|
770
|
-
for (const node of newSpec.nodes) {
|
|
771
|
-
if (node.inputs) {
|
|
772
|
-
for (const [key, binding] of Object.entries(node.inputs)) {
|
|
773
|
-
if (binding.actionName === op.nodeId) {
|
|
774
|
-
// Remove the dangling reference to prevent API errors
|
|
775
|
-
delete node.inputs[key];
|
|
776
|
-
errors.push(`Warning: Removed dangling reference ${node.id}.${key} → ${op.nodeId}`);
|
|
777
|
-
}
|
|
778
|
-
// Also check namedInputs for dangling refs
|
|
779
|
-
if (binding.namedInputs) {
|
|
780
|
-
const originalLen = binding.namedInputs.length;
|
|
781
|
-
binding.namedInputs = binding.namedInputs.filter(ni => ni.binding?.actionName !== op.nodeId);
|
|
782
|
-
if (binding.namedInputs.length < originalLen) {
|
|
783
|
-
errors.push(`Warning: Removed dangling namedInput reference in ${node.id}.${key} → ${op.nodeId}`);
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
changes.push(`Removed node: ${op.nodeId}`);
|
|
790
|
-
}
|
|
791
|
-
else {
|
|
792
|
-
errors.push(`Node not found: ${op.nodeId}`);
|
|
793
|
-
}
|
|
794
|
-
break;
|
|
795
|
-
}
|
|
796
|
-
case "rename": {
|
|
797
|
-
const node = newSpec.nodes.find(n => n.id === op.nodeId);
|
|
798
|
-
if (node) {
|
|
799
|
-
const oldName = node.displayName;
|
|
800
|
-
node.displayName = op.newDisplayName;
|
|
801
|
-
changes.push(`Renamed ${op.nodeId}: "${oldName}" → "${op.newDisplayName}"`);
|
|
802
|
-
}
|
|
803
|
-
else {
|
|
804
|
-
errors.push(`Node not found: ${op.nodeId}`);
|
|
805
|
-
}
|
|
806
|
-
break;
|
|
807
|
-
}
|
|
808
|
-
case "add_runif": {
|
|
809
|
-
const node = newSpec.nodes.find(n => n.id === op.nodeId);
|
|
810
|
-
if (node) {
|
|
811
|
-
node.runIf = op.condition;
|
|
812
|
-
changes.push(`Added runIf to ${op.nodeId}: when ${op.condition.sourceAction}.${op.condition.sourceOutput} ${op.condition.operator} "${op.condition.value}"`);
|
|
813
|
-
}
|
|
814
|
-
else {
|
|
815
|
-
errors.push(`Node not found: ${op.nodeId}`);
|
|
816
|
-
}
|
|
817
|
-
break;
|
|
818
|
-
}
|
|
819
|
-
case "remove_runif": {
|
|
820
|
-
const node = newSpec.nodes.find(n => n.id === op.nodeId);
|
|
821
|
-
if (node) {
|
|
822
|
-
delete node.runIf;
|
|
823
|
-
changes.push(`Removed runIf from ${op.nodeId}`);
|
|
824
|
-
}
|
|
825
|
-
else {
|
|
826
|
-
errors.push(`Node not found: ${op.nodeId}`);
|
|
827
|
-
}
|
|
828
|
-
break;
|
|
829
|
-
}
|
|
830
|
-
case "add_to_results": {
|
|
831
|
-
const exists = newSpec.resultMappings.some(rm => rm.nodeId === op.nodeId);
|
|
832
|
-
if (!exists) {
|
|
833
|
-
newSpec.resultMappings.push({ nodeId: op.nodeId, output: op.output });
|
|
834
|
-
changes.push(`Added ${op.nodeId}.${op.output} to results`);
|
|
835
|
-
}
|
|
836
|
-
else {
|
|
837
|
-
changes.push(`${op.nodeId} already in results`);
|
|
838
|
-
}
|
|
839
|
-
break;
|
|
840
|
-
}
|
|
841
|
-
case "remove_from_results": {
|
|
842
|
-
const idx = newSpec.resultMappings.findIndex(rm => rm.nodeId === op.nodeId);
|
|
843
|
-
if (idx >= 0) {
|
|
844
|
-
newSpec.resultMappings.splice(idx, 1);
|
|
845
|
-
changes.push(`Removed ${op.nodeId} from results`);
|
|
846
|
-
}
|
|
847
|
-
break;
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
return { spec: newSpec, changes, errors };
|
|
852
|
-
}
|
|
853
|
-
/**
|
|
854
|
-
* Directly patch a workflow_def without recompilation.
|
|
855
|
-
* This preserves ALL original fields and only modifies what's necessary.
|
|
856
|
-
*
|
|
857
|
-
* Use this for simple operations like remove/rename that don't need full recompile.
|
|
858
|
-
*/
|
|
859
|
-
function patchWorkflowDef(workflowDef, ops) {
|
|
860
|
-
const patched = JSON.parse(JSON.stringify(workflowDef));
|
|
861
|
-
const actions = patched.actions;
|
|
862
|
-
const results = patched.results;
|
|
863
|
-
const changes = [];
|
|
864
|
-
const errors = [];
|
|
865
|
-
for (const op of ops) {
|
|
866
|
-
switch (op.type) {
|
|
867
|
-
case "remove": {
|
|
868
|
-
const idx = actions.findIndex((a) => a.name === op.nodeId);
|
|
869
|
-
if (idx >= 0) {
|
|
870
|
-
actions.splice(idx, 1);
|
|
871
|
-
changes.push(`Removed node: ${op.nodeId}`);
|
|
872
|
-
// Remove from results
|
|
873
|
-
for (const key of Object.keys(results)) {
|
|
874
|
-
if (results[key].actionName === op.nodeId) {
|
|
875
|
-
delete results[key];
|
|
876
|
-
changes.push(`Removed result mapping: ${key}`);
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
// Clean up dangling input references
|
|
880
|
-
for (const action of actions) {
|
|
881
|
-
const inputs = action.inputs;
|
|
882
|
-
if (inputs) {
|
|
883
|
-
for (const [inputKey, inputVal] of Object.entries(inputs)) {
|
|
884
|
-
const actionOutput = inputVal.actionOutput;
|
|
885
|
-
if (actionOutput?.actionName === op.nodeId) {
|
|
886
|
-
delete inputs[inputKey];
|
|
887
|
-
errors.push(`Warning: Removed dangling reference ${action.name}.${inputKey} → ${op.nodeId}`);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
else {
|
|
894
|
-
errors.push(`Node not found: ${op.nodeId}`);
|
|
895
|
-
}
|
|
896
|
-
break;
|
|
897
|
-
}
|
|
898
|
-
case "rename": {
|
|
899
|
-
const action = actions.find((a) => a.name === op.nodeId);
|
|
900
|
-
if (action) {
|
|
901
|
-
const displaySettings = action.displaySettings;
|
|
902
|
-
if (displaySettings) {
|
|
903
|
-
displaySettings.displayName = op.newDisplayName;
|
|
904
|
-
changes.push(`Renamed ${op.nodeId} to "${op.newDisplayName}"`);
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
else {
|
|
908
|
-
errors.push(`Node not found for rename: ${op.nodeId}`);
|
|
909
|
-
}
|
|
910
|
-
break;
|
|
911
|
-
}
|
|
912
|
-
// For other operations, we'd need full recompile
|
|
913
|
-
default:
|
|
914
|
-
errors.push(`Direct patch not supported for operation: ${op.type}`);
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
return { workflow_def: patched, changes, errors };
|
|
918
|
-
}
|
|
919
|
-
/**
|
|
920
|
-
* @deprecated MCP should not transform from intent - the Agent builds workflow_spec.
|
|
921
|
-
* The agent uses analyze to get current spec, reasons about changes, and calls
|
|
922
|
-
* update with the new workflow_spec it built.
|
|
923
|
-
* Will be removed in future version.
|
|
924
|
-
*
|
|
925
|
-
* Transform a workflow based on natural language intent.
|
|
926
|
-
*
|
|
927
|
-
* @param workflowDef - The existing workflow_def JSON
|
|
928
|
-
* @param input - Natural language modification request
|
|
929
|
-
* @param personaType - The persona type (voice, chat, dashboard)
|
|
930
|
-
* @returns IntentTransformResult with modified workflow or LLM prompt
|
|
931
|
-
*/
|
|
932
|
-
export function transformWorkflowFromIntent(workflowDef, input, personaType = "chat") {
|
|
933
|
-
// Step 1: Decompile to spec (for parsing)
|
|
934
|
-
const spec = decompileWorkflow(workflowDef, personaType);
|
|
935
|
-
// Step 2: Parse intent into operations
|
|
936
|
-
const { ops, needs_llm, reason } = parseIntent(input, spec);
|
|
937
|
-
if (needs_llm) {
|
|
938
|
-
// Complex modification - return LLM prompt
|
|
939
|
-
return {
|
|
940
|
-
success: false,
|
|
941
|
-
needs_llm: true,
|
|
942
|
-
llm_prompt: createModificationPrompt(spec, input),
|
|
943
|
-
changes: [],
|
|
944
|
-
errors: [reason || "LLM interpretation needed"],
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
|
-
// Step 3: Check if we can use direct patching (simpler, preserves all fields)
|
|
948
|
-
const simpleOps = ["remove", "rename"];
|
|
949
|
-
const allSimple = ops.every(op => simpleOps.includes(op.type));
|
|
950
|
-
if (allSimple && ops.length > 0) {
|
|
951
|
-
// Use direct patching - no recompilation needed
|
|
952
|
-
const { workflow_def, changes, errors } = patchWorkflowDef(workflowDef, ops);
|
|
953
|
-
if (errors.some(e => !e.startsWith("Warning"))) {
|
|
954
|
-
return {
|
|
955
|
-
success: false,
|
|
956
|
-
workflow_def,
|
|
957
|
-
changes,
|
|
958
|
-
errors,
|
|
959
|
-
};
|
|
960
|
-
}
|
|
961
|
-
return {
|
|
962
|
-
success: true,
|
|
963
|
-
workflow_def,
|
|
964
|
-
changes,
|
|
965
|
-
errors,
|
|
966
|
-
};
|
|
967
|
-
}
|
|
968
|
-
// Step 4: Complex operations - use full decompile → transform → compile
|
|
969
|
-
const { spec: modifiedSpec, changes, errors } = applyOperations(spec, ops);
|
|
970
|
-
if (errors.some(e => !e.startsWith("Warning"))) {
|
|
971
|
-
return {
|
|
972
|
-
success: false,
|
|
973
|
-
spec: modifiedSpec,
|
|
974
|
-
changes,
|
|
975
|
-
errors,
|
|
976
|
-
};
|
|
977
|
-
}
|
|
978
|
-
// Step 5: Compile back to workflow_def
|
|
979
|
-
try {
|
|
980
|
-
const compiled = compileWorkflow(modifiedSpec);
|
|
981
|
-
// Preserve original workflowName
|
|
982
|
-
compiled.workflow_def.workflowName = workflowDef.workflowName;
|
|
983
|
-
return {
|
|
984
|
-
success: true,
|
|
985
|
-
spec: modifiedSpec,
|
|
986
|
-
workflow_def: compiled.workflow_def,
|
|
987
|
-
changes,
|
|
988
|
-
errors,
|
|
989
|
-
};
|
|
990
|
-
}
|
|
991
|
-
catch (e) {
|
|
992
|
-
return {
|
|
993
|
-
success: false,
|
|
994
|
-
spec: modifiedSpec,
|
|
995
|
-
changes,
|
|
996
|
-
errors: [...errors, `Compilation failed: ${e instanceof Error ? e.message : String(e)}`],
|
|
997
|
-
};
|
|
998
|
-
}
|
|
999
|
-
}
|
|
@@ -93,6 +93,41 @@ persona(id="persona-uuid", data={method:"list"})
|
|
|
93
93
|
persona(id="persona-uuid", data={method:"schema"})
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
+
### Upload Dashboard Rows with Files
|
|
97
|
+
|
|
98
|
+
Create new dashboard rows with file attachments (triggers workflow execution):
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
persona(id="dashboard-uuid", data={
|
|
102
|
+
method: "upload",
|
|
103
|
+
items: [
|
|
104
|
+
{
|
|
105
|
+
"Input Document": { file: "/path/to/invoice.pdf" },
|
|
106
|
+
"Customer Name": "Acme Corp",
|
|
107
|
+
"Amount": 1500.00,
|
|
108
|
+
"Priority": true
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
})
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Supported value types in `items`:**
|
|
115
|
+
|
|
116
|
+
| Type | Format | Result |
|
|
117
|
+
|------|--------|--------|
|
|
118
|
+
| String | `"value"` | `string_value` |
|
|
119
|
+
| Number | `123.45` | `number_value` |
|
|
120
|
+
| Boolean | `true` | `boolean_value` |
|
|
121
|
+
| File | `{ file: "/path/to/doc.pdf" }` | `document_value` (auto base64) |
|
|
122
|
+
| Inline Doc | `{ contents: "...", mime_type: "text/plain" }` | `document_value` |
|
|
123
|
+
|
|
124
|
+
**Key differences from knowledge base upload:**
|
|
125
|
+
|
|
126
|
+
| Operation | Target | Use Case |
|
|
127
|
+
|-----------|--------|----------|
|
|
128
|
+
| `data={method:"upload", path:"..."}` | Knowledge Base | Chat personas, RAG search |
|
|
129
|
+
| `data={method:"upload", items:[...]}` | Dashboard Rows | Dashboard personas, per-row workflow |
|
|
130
|
+
|
|
96
131
|
### Legacy Syntax (still works)
|
|
97
132
|
|
|
98
133
|
```
|
package/docs/ema-user-guide.md
CHANGED
|
@@ -1003,6 +1003,72 @@ tools:
|
|
|
1003
1003
|
|
|
1004
1004
|
---
|
|
1005
1005
|
|
|
1006
|
+
## Workflow Analysis (MCP Users)
|
|
1007
|
+
|
|
1008
|
+
When using the MCP toolkit to analyze workflows, the analysis is **LLM-driven** using rule resources.
|
|
1009
|
+
|
|
1010
|
+
### Analysis Pattern
|
|
1011
|
+
|
|
1012
|
+
```
|
|
1013
|
+
1. Get workflow data: workflow(mode="get", persona_id="...")
|
|
1014
|
+
2. Fetch analysis rules: ema://rules/anti-patterns
|
|
1015
|
+
ema://rules/structural-invariants
|
|
1016
|
+
3. LLM checks workflow against rules
|
|
1017
|
+
4. LLM reports issues (MCP does NOT pre-compute)
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
### Key Resources for Analysis
|
|
1021
|
+
|
|
1022
|
+
| Resource | Purpose |
|
|
1023
|
+
|----------|---------|
|
|
1024
|
+
| `ema://rules/anti-patterns` | Common workflow mistakes (missing fallback, stateless response, etc.) |
|
|
1025
|
+
| `ema://rules/structural-invariants` | Hard constraints (no cycles, reachability, WORKFLOW_OUTPUT) |
|
|
1026
|
+
| `ema://rules/input-sources` | Which inputs accept which data types |
|
|
1027
|
+
| `ema://rules/optimizations` | Performance improvements |
|
|
1028
|
+
|
|
1029
|
+
### Voice AI: Automatic Action Instructions
|
|
1030
|
+
|
|
1031
|
+
When creating Voice AI personas with tools, `takeActionInstructions` is auto-generated:
|
|
1032
|
+
|
|
1033
|
+
```
|
|
1034
|
+
// Tools defined in workflow → Auto-generates:
|
|
1035
|
+
</Case 1>
|
|
1036
|
+
Create Ticket
|
|
1037
|
+
|
|
1038
|
+
Trigger When: User reports an issue
|
|
1039
|
+
|
|
1040
|
+
Required parameters: { "title": "", "description": "" }
|
|
1041
|
+
</Case 1>
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
### Dashboard: File Attachments in Rows
|
|
1045
|
+
|
|
1046
|
+
Dashboard rows can include file attachments via MCP:
|
|
1047
|
+
|
|
1048
|
+
```javascript
|
|
1049
|
+
persona(id="dashboard-id", data={
|
|
1050
|
+
method: "upload",
|
|
1051
|
+
items: [{
|
|
1052
|
+
"Input Document": { file: "/path/to/invoice.pdf" },
|
|
1053
|
+
"Customer Name": "Acme Corp"
|
|
1054
|
+
}]
|
|
1055
|
+
})
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1058
|
+
### Critical: Data Sources Before Search
|
|
1059
|
+
|
|
1060
|
+
Workflows with `search` nodes require documents to be uploaded first:
|
|
1061
|
+
|
|
1062
|
+
```
|
|
1063
|
+
1. Upload: persona(id="...", data={method:"upload", path:"/docs/faq.pdf"})
|
|
1064
|
+
2. Verify: persona(id="...", data={method:"stats"}) // success > 0
|
|
1065
|
+
3. Deploy: workflow(mode="deploy", ...)
|
|
1066
|
+
```
|
|
1067
|
+
|
|
1068
|
+
Without documents, search returns empty results.
|
|
1069
|
+
|
|
1070
|
+
---
|
|
1071
|
+
|
|
1006
1072
|
## Troubleshooting & Gotchas
|
|
1007
1073
|
|
|
1008
1074
|
### Common Mistakes
|
package/docs/mcp-tools-guide.md
CHANGED
|
@@ -28,15 +28,23 @@ Some clients show a **prefixed name** (for example Cursor: `mcp_ema_persona`). A
|
|
|
28
28
|
| `reference` | Envs, actions, templates, patterns, concepts, guidance | No (flag-based) |
|
|
29
29
|
| `sync` | Sync across environments | **YES** |
|
|
30
30
|
|
|
31
|
+
### `workflow` tool (get/deploy only)
|
|
32
|
+
|
|
33
|
+
| Mode | Purpose |
|
|
34
|
+
|------|---------|
|
|
35
|
+
| `get` | Returns workflow_def, schema, examples for LLM |
|
|
36
|
+
| `deploy` | Deploys LLM-generated workflow_def |
|
|
37
|
+
|
|
38
|
+
**LLM does all thinking** (analyze, compare, generate). MCP only provides data and executes.
|
|
39
|
+
|
|
31
40
|
### Deprecated tools (still work, show warnings)
|
|
32
41
|
|
|
33
42
|
| Tool | Status | Use Instead |
|
|
34
43
|
|------|--------|-------------|
|
|
35
|
-
| `workflow` | Deprecated | `persona` |
|
|
36
44
|
| `knowledge` | Deprecated | `data` |
|
|
37
45
|
| `demo` | Deprecated | `data` + `persona` |
|
|
38
46
|
|
|
39
|
-
> **Tip**: For new code, use `persona`, `data`, `sync`.
|
|
47
|
+
> **Tip**: For new code, use `persona`, `data`, `workflow`, `sync`.
|
|
40
48
|
|
|
41
49
|
## ⚠️ THE KEY INSIGHT: ONE CALL
|
|
42
50
|
|
|
@@ -102,21 +110,19 @@ persona(
|
|
|
102
110
|
persona(mode="get", id="abc-123")
|
|
103
111
|
|
|
104
112
|
// Modify config in ONE call
|
|
105
|
-
persona(mode="update", id="abc-123",
|
|
113
|
+
persona(mode="update", id="abc-123", config={widgets: [{name: "conversationSettings", conversationSettings: {...}}]})
|
|
106
114
|
```
|
|
107
115
|
|
|
108
|
-
###
|
|
116
|
+
### Get workflow data (for LLM to analyze/modify)
|
|
109
117
|
|
|
110
118
|
```typescript
|
|
111
|
-
|
|
112
|
-
persona(mode="get", id="abc-123", include_workflow=true) // Include full workflow
|
|
119
|
+
workflow(mode="get", persona_id="abc-123") // Returns workflow_def, schema, examples
|
|
113
120
|
```
|
|
114
121
|
|
|
115
|
-
|
|
122
|
+
The LLM then analyzes/modifies the workflow and deploys:
|
|
116
123
|
|
|
117
124
|
```typescript
|
|
118
|
-
|
|
119
|
-
persona(mode="update", id="abc-123", optimize=true, preview=false) // Apply fixes
|
|
125
|
+
workflow(mode="deploy", persona_id="abc-123", workflow_def={...}) // Deploy LLM's result
|
|
120
126
|
```
|
|
121
127
|
|
|
122
128
|
### Upload / manage knowledge base documents
|
|
@@ -212,7 +218,7 @@ knowledge(persona_id="target", mode="dashboard_clone", source_persona_id="source
|
|
|
212
218
|
persona(mode="version_create", id="My Bot", message="Before adding HITL")
|
|
213
219
|
|
|
214
220
|
// Make changes...
|
|
215
|
-
persona(mode="update", id="abc",
|
|
221
|
+
persona(mode="update", id="abc", config={...})
|
|
216
222
|
|
|
217
223
|
// If something breaks - restore
|
|
218
224
|
persona(mode="version_list", id="My Bot")
|
|
@@ -241,7 +247,7 @@ persona(mode="create", from="Voice AI Starter", name="My Bot", input="...", prev
|
|
|
241
247
|
|
|
242
248
|
**Update config:**
|
|
243
249
|
```typescript
|
|
244
|
-
persona(mode="update", id="abc",
|
|
250
|
+
persona(mode="update", id="abc", config={widgets: [{...}]})
|
|
245
251
|
```
|
|
246
252
|
|
|
247
253
|
**Clone:**
|
|
@@ -249,34 +255,12 @@ persona(mode="update", id="abc", proto_config={widgets: [{...}]})
|
|
|
249
255
|
persona(mode="clone", from="source-id", name="My Copy", include_data=true)
|
|
250
256
|
```
|
|
251
257
|
|
|
252
|
-
**Compare:**
|
|
253
|
-
```typescript
|
|
254
|
-
persona(mode="compare", id="abc", compare_to="def")
|
|
255
|
-
```
|
|
256
|
-
|
|
257
258
|
**Sanitize (two-step process):**
|
|
258
259
|
```typescript
|
|
259
260
|
persona(id="abc", sanitize=true) // Step 1: Preview - identify PII
|
|
260
261
|
persona(id="abc", sanitize=true, sanitize_examples=["Acme"], preview=false) // Step 2: Apply
|
|
261
262
|
```
|
|
262
263
|
|
|
263
|
-
**Analyze (recommended):**
|
|
264
|
-
```typescript
|
|
265
|
-
persona(mode="analyze", id="abc") // All aspects
|
|
266
|
-
persona(mode="analyze", id="abc", include=["workflow"]) // Workflow only
|
|
267
|
-
persona(mode="analyze", id="abc", include=["workflow", "config", "data"])
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
**Analyze + auto-fix:**
|
|
271
|
-
```typescript
|
|
272
|
-
persona(mode="analyze", id="abc", fix=true)
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
**Legacy (deprecated, use mode="analyze" instead):**
|
|
276
|
-
```typescript
|
|
277
|
-
persona(id="abc", optimize=true) // Routes to workflow tool internally
|
|
278
|
-
```
|
|
279
|
-
|
|
280
264
|
**Intent (qualification for complex requests):**
|
|
281
265
|
```typescript
|
|
282
266
|
// GREENFIELD: New persona creation
|
|
@@ -299,7 +283,7 @@ Returns:
|
|
|
299
283
|
- `status: "needs_qualification"` → `questions` to answer before proceeding
|
|
300
284
|
- `status: "needs_llm"` → `llm_prompt` to send to LLM for IntentSpec generation
|
|
301
285
|
|
|
302
|
-
For brownfield,
|
|
286
|
+
For brownfield, use `workflow(mode="get")` to fetch current state, then use intent to qualify the change.
|
|
303
287
|
|
|
304
288
|
**Templates:**
|
|
305
289
|
```typescript
|
|
@@ -396,6 +380,20 @@ data(persona_id="abc", mode="upload", file="/path/to/file.pdf")
|
|
|
396
380
|
data(persona_id="abc", mode="delete", file_id="file-123")
|
|
397
381
|
```
|
|
398
382
|
|
|
383
|
+
**Upload to specific widget (Document Generation personas):**
|
|
384
|
+
```typescript
|
|
385
|
+
// Document Proposal Manager widget names (verified from template):
|
|
386
|
+
data(persona_id="abc", mode="upload", file="/path/to/company.pdf", widget_name="upload") // Content Repository
|
|
387
|
+
data(persona_id="abc", mode="upload", file="/path/to/service-line.pdf", widget_name="upload1") // Service Line Documents
|
|
388
|
+
data(persona_id="abc", mode="upload", file="/path/to/style-guide.pdf", widget_name="upload2") // Style Guide
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
> **Important for Document Generation (Proposal Writer) personas:** Files must be uploaded to the correct widget to appear in the right repository in the UI. Without `widget_name`, files go to the default `fileUpload` widget which may not be visible in the Configuration tab.
|
|
392
|
+
>
|
|
393
|
+
> **Document Proposal Manager widgets:** `upload` (Content Repository), `upload1` (Service Line Documents), `upload2` (Style Guide)
|
|
394
|
+
>
|
|
395
|
+
> **For other templates:** Call `persona(id="abc", include_workflow=true)` and inspect `proto_config.widgets[].name` to find the exact widget names.
|
|
396
|
+
|
|
399
397
|
**Generate content (via Document Generation API):**
|
|
400
398
|
```typescript
|
|
401
399
|
// From a template
|
|
@@ -456,19 +454,50 @@ Start with:
|
|
|
456
454
|
- `ema://catalog/templates` (persona templates - dynamic)
|
|
457
455
|
- `ema://rules/anti-patterns` (what NOT to do)
|
|
458
456
|
|
|
459
|
-
##
|
|
457
|
+
## Workflow Generation (LLM Does the Work)
|
|
458
|
+
|
|
459
|
+
**MCP provides data and schema. LLM generates workflow_def.**
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
// 1. Get current workflow + schema (includes deprecation_warnings!)
|
|
463
|
+
result = workflow(mode="get", persona_id="abc")
|
|
464
|
+
// Returns: workflow_def, generation_schema, example_workflow, requirements, deprecation_warnings
|
|
465
|
+
|
|
466
|
+
// 2. LLM analyzes and generates new workflow_def
|
|
467
|
+
// (LLM uses schema and examples to build valid workflow)
|
|
468
|
+
// ⚠️ FIX any deprecated actions FIRST - check deprecation_warnings!
|
|
469
|
+
|
|
470
|
+
// 3. Deploy LLM's result (preview first!)
|
|
471
|
+
workflow(mode="deploy", persona_id="abc", workflow_def={...}, preview=true)
|
|
472
|
+
workflow(mode="deploy", persona_id="abc", workflow_def={...})
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### ⚠️ CRITICAL: Check Deprecated Actions First
|
|
476
|
+
|
|
477
|
+
**BEFORE generating any workflow:**
|
|
478
|
+
|
|
479
|
+
1. Check `deprecation_warnings` in `workflow(mode="get")` response
|
|
480
|
+
2. Use `reference(type="actions")` to verify current versions
|
|
481
|
+
3. **NEVER** copy workflow patterns from existing personas (they may use deprecated actions!)
|
|
482
|
+
|
|
483
|
+
**Correct workflow pattern:**
|
|
484
|
+
```
|
|
485
|
+
chat_trigger → search/v2 → respond_for_external_actions → WORKFLOW_OUTPUT
|
|
486
|
+
```
|
|
460
487
|
|
|
461
|
-
|
|
488
|
+
**Deprecated (DO NOT USE):**
|
|
489
|
+
```
|
|
490
|
+
search/v0 → Use search/v2
|
|
491
|
+
respond_with_sources → Use respond_for_external_actions
|
|
492
|
+
call_llm/v0 → Use call_llm/v2
|
|
493
|
+
```
|
|
462
494
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
| `missing_fallback` | critical | ✅ | Adds Fallback category |
|
|
470
|
-
| `type_mismatch` | critical | ✅ | Rewires to compatible source |
|
|
471
|
-
| `cycle` | critical | ❌ | Requires manual restructuring |
|
|
495
|
+
**Common issues to check when generating:**
|
|
496
|
+
- Missing `WORKFLOW_OUTPUT` in results
|
|
497
|
+
- Missing Fallback category in categorizers
|
|
498
|
+
- Orphan nodes not connected to output
|
|
499
|
+
- Type mismatches between connected nodes
|
|
500
|
+
- **Using deprecated actions** (check deprecation_warnings!)
|
|
472
501
|
|
|
473
502
|
## HITL (Human-in-the-Loop) Policy
|
|
474
503
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ema.co/mcp-toolkit",
|
|
3
|
-
"version": "2026.1.
|
|
3
|
+
"version": "2026.1.27-10",
|
|
4
4
|
"description": "Ema AI Employee toolkit - MCP server, CLI, and SDK for managing AI Employees across environments",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"generate:api-client": "npx @hey-api/openapi-ts -i src/sdk/generated/openapi.json -o src/sdk/generated/api-client -c @hey-api/client-fetch",
|
|
59
59
|
"generate:protos": "buf generate ../ema-repos/protos --template buf.gen.yaml",
|
|
60
60
|
"generate:proto-fields": "tsx scripts/generate-proto-fields.ts",
|
|
61
|
-
"generate:
|
|
61
|
+
"generate:deprecated-actions": "tsx scripts/generate-deprecated-actions.ts",
|
|
62
62
|
"generate:skill": "tsx scripts/generate-skill.ts",
|
|
63
63
|
"generate:skill:check": "tsx scripts/generate-skill.ts --check",
|
|
64
64
|
"generate:templates": "tsx scripts/generate-templates.ts",
|