mcpgraph 0.1.5 → 0.1.7
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/README.md +1 -0
- package/dist/api.d.ts +17 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +33 -4
- package/dist/api.js.map +1 -1
- package/dist/execution/context.d.ts +3 -6
- package/dist/execution/context.d.ts.map +1 -1
- package/dist/execution/context.js +11 -2
- package/dist/execution/context.js.map +1 -1
- package/dist/execution/controller.d.ts +29 -0
- package/dist/execution/controller.d.ts.map +1 -0
- package/dist/execution/controller.js +95 -0
- package/dist/execution/controller.js.map +1 -0
- package/dist/execution/executor.d.ts +8 -1
- package/dist/execution/executor.d.ts.map +1 -1
- package/dist/execution/executor.js +184 -37
- package/dist/execution/executor.js.map +1 -1
- package/dist/execution/nodes/entry-executor.d.ts +1 -1
- package/dist/execution/nodes/entry-executor.d.ts.map +1 -1
- package/dist/execution/nodes/entry-executor.js +3 -2
- package/dist/execution/nodes/entry-executor.js.map +1 -1
- package/dist/execution/nodes/exit-executor.d.ts +1 -1
- package/dist/execution/nodes/exit-executor.d.ts.map +1 -1
- package/dist/execution/nodes/exit-executor.js +3 -2
- package/dist/execution/nodes/exit-executor.js.map +1 -1
- package/dist/execution/nodes/mcp-tool-executor.d.ts +1 -1
- package/dist/execution/nodes/mcp-tool-executor.d.ts.map +1 -1
- package/dist/execution/nodes/mcp-tool-executor.js +3 -2
- package/dist/execution/nodes/mcp-tool-executor.js.map +1 -1
- package/dist/execution/nodes/switch-executor.d.ts +1 -1
- package/dist/execution/nodes/switch-executor.d.ts.map +1 -1
- package/dist/execution/nodes/switch-executor.js +7 -2
- package/dist/execution/nodes/switch-executor.js.map +1 -1
- package/dist/execution/nodes/transform-executor.d.ts +1 -1
- package/dist/execution/nodes/transform-executor.d.ts.map +1 -1
- package/dist/execution/nodes/transform-executor.js +3 -2
- package/dist/execution/nodes/transform-executor.js.map +1 -1
- package/dist/types/execution.d.ts +92 -0
- package/dist/types/execution.d.ts.map +1 -0
- package/dist/types/execution.js +5 -0
- package/dist/types/execution.js.map +1 -0
- package/docs/design.md +220 -0
- package/docs/implementation.md +377 -0
- package/docs/introspection-debugging.md +651 -0
- package/examples/api-usage.ts +49 -1
- package/examples/switch_example.yaml +80 -0
- package/package.json +2 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { EntryNode } from "../../types/config.js";
|
|
5
5
|
import type { ExecutionContext } from "../context.js";
|
|
6
|
-
export declare function executeEntryNode(node: EntryNode, toolInput: Record<string, unknown>, context: ExecutionContext): {
|
|
6
|
+
export declare function executeEntryNode(node: EntryNode, toolInput: Record<string, unknown>, context: ExecutionContext, startTime: number): {
|
|
7
7
|
output: unknown;
|
|
8
8
|
nextNode: string;
|
|
9
9
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry-executor.d.ts","sourceRoot":"","sources":["../../../src/execution/nodes/entry-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,EACf,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,OAAO,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"entry-executor.d.ts","sourceRoot":"","sources":["../../../src/execution/nodes/entry-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,EACf,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,OAAO,EAAE,gBAAgB,EACzB,SAAS,EAAE,MAAM,GAChB;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAevC"}
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
* Entry node executor
|
|
3
3
|
*/
|
|
4
4
|
import { logger } from "../../logger.js";
|
|
5
|
-
export function executeEntryNode(node, toolInput, context) {
|
|
5
|
+
export function executeEntryNode(node, toolInput, context, startTime) {
|
|
6
6
|
logger.debug(`Executing entry node: ${node.id}`);
|
|
7
7
|
// Entry node receives tool input and initializes context
|
|
8
8
|
// The input is already in the context, so we just pass it through
|
|
9
9
|
const output = toolInput;
|
|
10
|
+
const endTime = Date.now();
|
|
10
11
|
context.setNodeOutput(node.id, output);
|
|
11
|
-
context.addHistory(node.id, toolInput, output);
|
|
12
|
+
context.addHistory(node.id, "entry", toolInput, output, startTime, endTime);
|
|
12
13
|
return {
|
|
13
14
|
output,
|
|
14
15
|
nextNode: node.next,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry-executor.js","sourceRoot":"","sources":["../../../src/execution/nodes/entry-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,MAAM,UAAU,gBAAgB,CAC9B,IAAe,EACf,SAAkC,EAClC,OAAyB;
|
|
1
|
+
{"version":3,"file":"entry-executor.js","sourceRoot":"","sources":["../../../src/execution/nodes/entry-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,MAAM,UAAU,gBAAgB,CAC9B,IAAe,EACf,SAAkC,EAClC,OAAyB,EACzB,SAAiB;IAEjB,MAAM,CAAC,KAAK,CAAC,yBAAyB,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAEjD,yDAAyD;IACzD,kEAAkE;IAClE,MAAM,MAAM,GAAG,SAAS,CAAC;IACzB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE3B,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACvC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAE5E,OAAO;QACL,MAAM;QACN,QAAQ,EAAE,IAAI,CAAC,IAAI;KACpB,CAAC;AACJ,CAAC"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { ExitNode } from "../../types/config.js";
|
|
5
5
|
import type { ExecutionContext } from "../context.js";
|
|
6
|
-
export declare function executeExitNode(node: ExitNode, context: ExecutionContext): {
|
|
6
|
+
export declare function executeExitNode(node: ExitNode, context: ExecutionContext, startTime: number): {
|
|
7
7
|
output: unknown;
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=exit-executor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exit-executor.d.ts","sourceRoot":"","sources":["../../../src/execution/nodes/exit-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"exit-executor.d.ts","sourceRoot":"","sources":["../../../src/execution/nodes/exit-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,gBAAgB,EACzB,SAAS,EAAE,MAAM,GAChB;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,CAcrB"}
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
* Exit node executor
|
|
3
3
|
*/
|
|
4
4
|
import { logger } from "../../logger.js";
|
|
5
|
-
export function executeExitNode(node, context) {
|
|
5
|
+
export function executeExitNode(node, context, startTime) {
|
|
6
6
|
logger.debug(`Executing exit node: ${node.id}`);
|
|
7
7
|
// Exit node extracts the final result from context
|
|
8
8
|
// Use the last output or the context's output
|
|
9
9
|
const data = context.getData();
|
|
10
10
|
const output = data.output || data.last || {};
|
|
11
|
-
|
|
11
|
+
const endTime = Date.now();
|
|
12
|
+
context.addHistory(node.id, "exit", data, output, startTime, endTime);
|
|
12
13
|
return {
|
|
13
14
|
output,
|
|
14
15
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exit-executor.js","sourceRoot":"","sources":["../../../src/execution/nodes/exit-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,MAAM,UAAU,eAAe,CAC7B,IAAc,EACd,OAAyB;
|
|
1
|
+
{"version":3,"file":"exit-executor.js","sourceRoot":"","sources":["../../../src/execution/nodes/exit-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,MAAM,UAAU,eAAe,CAC7B,IAAc,EACd,OAAyB,EACzB,SAAiB;IAEjB,MAAM,CAAC,KAAK,CAAC,wBAAwB,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAEhD,mDAAmD;IACnD,8CAA8C;IAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE3B,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAEtE,OAAO;QACL,MAAM;KACP,CAAC;AACJ,CAAC"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import type { McpNode, ServerConfig } from "../../types/config.js";
|
|
5
5
|
import type { ExecutionContext } from "../context.js";
|
|
6
6
|
import type { McpClientManager } from "../../mcp/client-manager.js";
|
|
7
|
-
export declare function executeMcpToolNode(node: McpNode, context: ExecutionContext, clientManager: McpClientManager, serverConfig: ServerConfig): Promise<{
|
|
7
|
+
export declare function executeMcpToolNode(node: McpNode, context: ExecutionContext, clientManager: McpClientManager, serverConfig: ServerConfig, startTime: number): Promise<{
|
|
8
8
|
output: unknown;
|
|
9
9
|
nextNode: string;
|
|
10
10
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-tool-executor.d.ts","sourceRoot":"","sources":["../../../src/execution/nodes/mcp-tool-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAGpE,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,gBAAgB,EACzB,aAAa,EAAE,gBAAgB,EAC/B,YAAY,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"mcp-tool-executor.d.ts","sourceRoot":"","sources":["../../../src/execution/nodes/mcp-tool-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAGpE,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,gBAAgB,EACzB,aAAa,EAAE,gBAAgB,EAC/B,YAAY,EAAE,YAAY,EAC1B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAkEhD"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { evaluateJsonata } from "../../expressions/jsonata.js";
|
|
5
5
|
import { logger } from "../../logger.js";
|
|
6
|
-
export async function executeMcpToolNode(node, context, clientManager, serverConfig) {
|
|
6
|
+
export async function executeMcpToolNode(node, context, clientManager, serverConfig, startTime) {
|
|
7
7
|
logger.debug(`Executing MCP tool node: ${node.id} (${node.server}.${node.tool})`);
|
|
8
8
|
const exprContext = context.getData();
|
|
9
9
|
// Pre-transform: Apply JSONata to format tool arguments
|
|
@@ -54,8 +54,9 @@ export async function executeMcpToolNode(node, context, clientManager, serverCon
|
|
|
54
54
|
}
|
|
55
55
|
logger.debug(`MCP tool output: ${JSON.stringify(toolOutput, null, 2)}`);
|
|
56
56
|
const output = toolOutput;
|
|
57
|
+
const endTime = Date.now();
|
|
57
58
|
context.setNodeOutput(node.id, output);
|
|
58
|
-
context.addHistory(node.id, transformedArgs, output);
|
|
59
|
+
context.addHistory(node.id, "mcp", transformedArgs, output, startTime, endTime);
|
|
59
60
|
return {
|
|
60
61
|
output,
|
|
61
62
|
nextNode: node.next,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-tool-executor.js","sourceRoot":"","sources":["../../../src/execution/nodes/mcp-tool-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE/D,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,IAAa,EACb,OAAyB,EACzB,aAA+B,EAC/B,YAA0B;
|
|
1
|
+
{"version":3,"file":"mcp-tool-executor.js","sourceRoot":"","sources":["../../../src/execution/nodes/mcp-tool-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { SwitchNode } from "../../types/config.js";
|
|
5
5
|
import type { ExecutionContext } from "../context.js";
|
|
6
|
-
export declare function executeSwitchNode(node: SwitchNode, context: ExecutionContext): Promise<{
|
|
6
|
+
export declare function executeSwitchNode(node: SwitchNode, context: ExecutionContext, startTime: number): Promise<{
|
|
7
7
|
output: unknown;
|
|
8
8
|
nextNode: string;
|
|
9
9
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"switch-executor.d.ts","sourceRoot":"","sources":["../../../src/execution/nodes/switch-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"switch-executor.d.ts","sourceRoot":"","sources":["../../../src/execution/nodes/switch-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,gBAAgB,EACzB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAqChD"}
|
|
@@ -3,14 +3,16 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { evaluateJsonLogic } from "../../expressions/json-logic.js";
|
|
5
5
|
import { logger } from "../../logger.js";
|
|
6
|
-
export async function executeSwitchNode(node, context) {
|
|
6
|
+
export async function executeSwitchNode(node, context, startTime) {
|
|
7
7
|
logger.debug(`Executing switch node: ${node.id}`);
|
|
8
8
|
const exprContext = context.getData();
|
|
9
|
+
const endTime = Date.now();
|
|
9
10
|
// Evaluate conditions in order
|
|
10
11
|
for (const condition of node.conditions) {
|
|
11
12
|
// If no rule is specified, this is a default/fallback case
|
|
12
13
|
if (condition.rule === undefined || condition.rule === null) {
|
|
13
14
|
logger.debug(`Switch node ${node.id}: Using default/fallback target: ${condition.target}`);
|
|
15
|
+
context.addHistory(node.id, "switch", exprContext, exprContext, startTime, endTime);
|
|
14
16
|
return {
|
|
15
17
|
output: exprContext,
|
|
16
18
|
nextNode: condition.target,
|
|
@@ -20,6 +22,7 @@ export async function executeSwitchNode(node, context) {
|
|
|
20
22
|
const ruleResult = evaluateJsonLogic(condition.rule, exprContext);
|
|
21
23
|
if (ruleResult) {
|
|
22
24
|
logger.debug(`Switch node ${node.id}: Condition matched, routing to: ${condition.target}`);
|
|
25
|
+
context.addHistory(node.id, "switch", exprContext, exprContext, startTime, endTime);
|
|
23
26
|
return {
|
|
24
27
|
output: exprContext,
|
|
25
28
|
nextNode: condition.target,
|
|
@@ -27,6 +30,8 @@ export async function executeSwitchNode(node, context) {
|
|
|
27
30
|
}
|
|
28
31
|
}
|
|
29
32
|
// No conditions matched and no default case
|
|
30
|
-
|
|
33
|
+
const error = new Error(`Switch node ${node.id}: No conditions matched and no default case provided`);
|
|
34
|
+
context.addHistory(node.id, "switch", exprContext, null, startTime, endTime, error);
|
|
35
|
+
throw error;
|
|
31
36
|
}
|
|
32
37
|
//# sourceMappingURL=switch-executor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"switch-executor.js","sourceRoot":"","sources":["../../../src/execution/nodes/switch-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAgB,EAChB,OAAyB;
|
|
1
|
+
{"version":3,"file":"switch-executor.js","sourceRoot":"","sources":["../../../src/execution/nodes/switch-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAgB,EAChB,OAAyB,EACzB,SAAiB;IAEjB,MAAM,CAAC,KAAK,CAAC,0BAA0B,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAElD,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IACtC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE3B,+BAA+B;IAC/B,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACxC,2DAA2D;QAC3D,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YAC5D,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,CAAC,EAAE,oCAAoC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;YAC3F,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;YACpF,OAAO;gBACL,MAAM,EAAE,WAAW;gBACnB,QAAQ,EAAE,SAAS,CAAC,MAAM;aAC3B,CAAC;QACJ,CAAC;QAED,+BAA+B;QAC/B,MAAM,UAAU,GAAG,iBAAiB,CAAC,SAAS,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;QAElE,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,CAAC,EAAE,oCAAoC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;YAC3F,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;YACpF,OAAO;gBACL,MAAM,EAAE,WAAW;gBACnB,QAAQ,EAAE,SAAS,CAAC,MAAM;aAC3B,CAAC;QACJ,CAAC;IACH,CAAC;IAED,4CAA4C;IAC5C,MAAM,KAAK,GAAG,IAAI,KAAK,CACrB,eAAe,IAAI,CAAC,EAAE,sDAAsD,CAC7E,CAAC;IACF,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IACpF,MAAM,KAAK,CAAC;AACd,CAAC"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { TransformNode } from "../../types/config.js";
|
|
5
5
|
import type { ExecutionContext } from "../context.js";
|
|
6
|
-
export declare function executeTransformNode(node: TransformNode, context: ExecutionContext): Promise<{
|
|
6
|
+
export declare function executeTransformNode(node: TransformNode, context: ExecutionContext, startTime: number): Promise<{
|
|
7
7
|
output: unknown;
|
|
8
8
|
nextNode: string;
|
|
9
9
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform-executor.d.ts","sourceRoot":"","sources":["../../../src/execution/nodes/transform-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItD,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"transform-executor.d.ts","sourceRoot":"","sources":["../../../src/execution/nodes/transform-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItD,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,gBAAgB,EACzB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAmBhD"}
|
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { evaluateJsonata } from "../../expressions/jsonata.js";
|
|
5
5
|
import { logger } from "../../logger.js";
|
|
6
|
-
export async function executeTransformNode(node, context) {
|
|
6
|
+
export async function executeTransformNode(node, context, startTime) {
|
|
7
7
|
logger.debug(`Executing transform node: ${node.id}`);
|
|
8
8
|
logger.debug(`Transform expression: ${node.transform.expr}`);
|
|
9
9
|
const exprContext = context.getData();
|
|
10
10
|
logger.debug(`Transform context: ${JSON.stringify(exprContext, null, 2)}`);
|
|
11
11
|
const output = await evaluateJsonata(node.transform.expr, exprContext);
|
|
12
|
+
const endTime = Date.now();
|
|
12
13
|
logger.debug(`Transform output: ${JSON.stringify(output, null, 2)}`);
|
|
13
14
|
context.setNodeOutput(node.id, output);
|
|
14
|
-
context.addHistory(node.id, exprContext, output);
|
|
15
|
+
context.addHistory(node.id, "transform", exprContext, output, startTime, endTime);
|
|
15
16
|
return {
|
|
16
17
|
output,
|
|
17
18
|
nextNode: node.next,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform-executor.js","sourceRoot":"","sources":["../../../src/execution/nodes/transform-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAmB,EACnB,OAAyB;
|
|
1
|
+
{"version":3,"file":"transform-executor.js","sourceRoot":"","sources":["../../../src/execution/nodes/transform-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAmB,EACnB,OAAyB,EACzB,SAAiB;IAEjB,MAAM,CAAC,KAAK,CAAC,6BAA6B,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IACrD,MAAM,CAAC,KAAK,CAAC,yBAAyB,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;IAE7D,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IACtC,MAAM,CAAC,KAAK,CAAC,sBAAsB,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAE3E,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACvE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE3B,MAAM,CAAC,KAAK,CAAC,qBAAqB,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAErE,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACvC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAElF,OAAO;QACL,MAAM;QACN,QAAQ,EAAE,IAAI,CAAC,IAAI;KACpB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for execution introspection and debugging
|
|
3
|
+
*/
|
|
4
|
+
import type { NodeDefinition } from "./config.js";
|
|
5
|
+
import type { ExecutionContext } from "../execution/context.js";
|
|
6
|
+
export type ExecutionStatus = "not_started" | "running" | "paused" | "finished" | "error";
|
|
7
|
+
export interface NodeExecutionRecord {
|
|
8
|
+
nodeId: string;
|
|
9
|
+
nodeType: string;
|
|
10
|
+
startTime: number;
|
|
11
|
+
endTime: number;
|
|
12
|
+
duration: number;
|
|
13
|
+
input: unknown;
|
|
14
|
+
output: unknown;
|
|
15
|
+
error?: Error;
|
|
16
|
+
}
|
|
17
|
+
export interface ExecutionState {
|
|
18
|
+
status: ExecutionStatus;
|
|
19
|
+
currentNodeId: string | null;
|
|
20
|
+
executionHistory: NodeExecutionRecord[];
|
|
21
|
+
context: ExecutionContext;
|
|
22
|
+
error?: Error;
|
|
23
|
+
}
|
|
24
|
+
export interface ExecutionHooks {
|
|
25
|
+
/**
|
|
26
|
+
* Called before a node executes
|
|
27
|
+
* Return false to pause execution (breakpoint)
|
|
28
|
+
*/
|
|
29
|
+
onNodeStart?: (nodeId: string, node: NodeDefinition, context: ExecutionContext) => Promise<boolean>;
|
|
30
|
+
/**
|
|
31
|
+
* Called after a node completes successfully
|
|
32
|
+
*/
|
|
33
|
+
onNodeComplete?: (nodeId: string, node: NodeDefinition, input: unknown, output: unknown, duration: number) => Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Called when a node encounters an error
|
|
36
|
+
*/
|
|
37
|
+
onNodeError?: (nodeId: string, node: NodeDefinition, error: Error, context: ExecutionContext) => Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Called when execution pauses (breakpoint hit or manual pause)
|
|
40
|
+
*/
|
|
41
|
+
onPause?: (nodeId: string, context: ExecutionContext) => Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Called when execution resumes
|
|
44
|
+
*/
|
|
45
|
+
onResume?: () => Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
export interface ExecutionController {
|
|
48
|
+
/**
|
|
49
|
+
* Pause execution at the next node boundary
|
|
50
|
+
* Only valid when status is "running"
|
|
51
|
+
*/
|
|
52
|
+
pause(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Resume execution
|
|
55
|
+
* Only valid when status is "paused"
|
|
56
|
+
*/
|
|
57
|
+
resume(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Step to the next node (step over)
|
|
60
|
+
* Only valid when status is "paused"
|
|
61
|
+
*/
|
|
62
|
+
step(): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Get current execution state
|
|
65
|
+
*/
|
|
66
|
+
getState(): ExecutionState;
|
|
67
|
+
/**
|
|
68
|
+
* Set breakpoints
|
|
69
|
+
*/
|
|
70
|
+
setBreakpoints(nodeIds: string[]): void;
|
|
71
|
+
/**
|
|
72
|
+
* Clear breakpoints
|
|
73
|
+
*/
|
|
74
|
+
clearBreakpoints(): void;
|
|
75
|
+
}
|
|
76
|
+
export interface ExecutionOptions {
|
|
77
|
+
hooks?: ExecutionHooks;
|
|
78
|
+
breakpoints?: string[];
|
|
79
|
+
enableTelemetry?: boolean;
|
|
80
|
+
}
|
|
81
|
+
export interface ExecutionTelemetry {
|
|
82
|
+
totalDuration: number;
|
|
83
|
+
nodeDurations: Map<string, number>;
|
|
84
|
+
nodeCounts: Map<string, number>;
|
|
85
|
+
errorCount: number;
|
|
86
|
+
}
|
|
87
|
+
export interface ExecutionResult {
|
|
88
|
+
result: unknown;
|
|
89
|
+
executionHistory: NodeExecutionRecord[];
|
|
90
|
+
telemetry?: ExecutionTelemetry;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=execution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../../src/types/execution.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,MAAM,eAAe,GAAG,aAAa,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;AAE1F,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;IACxC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,WAAW,CAAC,EAAE,CACZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,gBAAgB,KACtB,OAAO,CAAC,OAAO,CAAC,CAAC;IAEtB;;OAEG;IACH,cAAc,CAAC,EAAE,CACf,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnB;;OAEG;IACH,WAAW,CAAC,EAAE,CACZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,gBAAgB,KACtB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvE;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,KAAK,IAAI,IAAI,CAAC;IAEd;;;OAGG;IACH,MAAM,IAAI,IAAI,CAAC;IAEf;;;OAGG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtB;;OAEG;IACH,QAAQ,IAAI,cAAc,CAAC;IAE3B;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAExC;;OAEG;IACH,gBAAgB,IAAI,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;IACxC,SAAS,CAAC,EAAE,kBAAkB,CAAC;CAChC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution.js","sourceRoot":"","sources":["../../src/types/execution.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
package/docs/design.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# mcpGraph Design Document
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document memorializes the high-level design and tooling recommendations for mcpGraph, a product similar to n8n that surfaces an MCP interface and internally implements a directed graph of MCP server calls. The system enables filtering and reformatting data between nodes, makes routing decisions based on node output, and maintains a declarative, observable configuration without embedding a full programming language.
|
|
6
|
+
|
|
7
|
+
## Core Architecture: The Orchestrator
|
|
8
|
+
|
|
9
|
+
### Recommended Platform: TypeScript (Node.js)
|
|
10
|
+
|
|
11
|
+
**Rationale:**
|
|
12
|
+
- The MCP SDK is most mature in TypeScript
|
|
13
|
+
- Frontend tools for visual graph editing (like React Flow) are industry standard
|
|
14
|
+
- Strong ecosystem for both backend orchestration and frontend visualization
|
|
15
|
+
|
|
16
|
+
### Graph Execution Engine
|
|
17
|
+
|
|
18
|
+
The system implements a custom graph execution engine that orchestrates the directed graph of MCP server calls.
|
|
19
|
+
|
|
20
|
+
**Design Approach:**
|
|
21
|
+
- **Custom Execution Loop**: A lightweight, sequential execution loop that provides full control over execution flow
|
|
22
|
+
- **Simple Architecture**: Direct mapping from YAML node definitions to execution, avoiding abstraction layers
|
|
23
|
+
- **Data Flow**: Execution context maintains state and data flow between nodes
|
|
24
|
+
- **Control Flow**: Supports conditional routing via switch nodes; cycles are supported for future loop constructs
|
|
25
|
+
- **Observability**: Built-in execution history tracking for debugging and introspection
|
|
26
|
+
|
|
27
|
+
**Implementation:**
|
|
28
|
+
The YAML configuration is parsed into a graph structure (`Graph` class) and executed by a custom `GraphExecutor` that:
|
|
29
|
+
- Starts at the tool's entry node
|
|
30
|
+
- Executes nodes sequentially based on the graph structure
|
|
31
|
+
- Tracks execution state and history
|
|
32
|
+
- Routes conditionally via switch nodes
|
|
33
|
+
- Continues until the exit node is reached
|
|
34
|
+
|
|
35
|
+
### MCP Integration
|
|
36
|
+
|
|
37
|
+
- Use `@modelcontextprotocol/sdk`
|
|
38
|
+
- The system exposes an MCP server interface
|
|
39
|
+
- The YAML configuration defines the MCP server metadata and the tools it exposes
|
|
40
|
+
- Each tool definition includes standard MCP tool metadata (name, description, input/output parameters)
|
|
41
|
+
- Each tool's graph has an explicit entry node that receives tool arguments and initializes execution
|
|
42
|
+
- Each tool's graph has an explicit exit node that returns the final result to the MCP tool caller
|
|
43
|
+
- Execution flows through the graph from entry node to exit node
|
|
44
|
+
|
|
45
|
+
## Declarative Logic & Data Transformation
|
|
46
|
+
|
|
47
|
+
To avoid embedding a full programming language while maintaining declarative, observable configurations, the system uses standardized expression engines.
|
|
48
|
+
|
|
49
|
+
### Data Reformatting: JSONata
|
|
50
|
+
|
|
51
|
+
**Why JSONata:**
|
|
52
|
+
- Declarative query and transformation language for JSON
|
|
53
|
+
- Single string expression enables clear observability
|
|
54
|
+
- Can log input, expression, and output to debug transformations
|
|
55
|
+
|
|
56
|
+
**Resources:** [JSONata Documentation](https://jsonata.org/)
|
|
57
|
+
|
|
58
|
+
**YAML Example:**
|
|
59
|
+
```yaml
|
|
60
|
+
transform:
|
|
61
|
+
expr: "$merge([payload, {'timestamp': $now()}])"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Routing Decisions: JSON Logic
|
|
65
|
+
|
|
66
|
+
**Why JSON Logic:**
|
|
67
|
+
- Allows complex rules (e.g., "if price > 100 and status == 'active'") as pure JSON objects
|
|
68
|
+
- Declarative and observable
|
|
69
|
+
- No embedded code execution
|
|
70
|
+
|
|
71
|
+
**Resources:** [JSON Logic Documentation](https://jsonlogic.com/)
|
|
72
|
+
|
|
73
|
+
**YAML Example:**
|
|
74
|
+
```yaml
|
|
75
|
+
condition:
|
|
76
|
+
and:
|
|
77
|
+
- ">": [{ var: "price" }, 100]
|
|
78
|
+
- "==": [{ var: "status" }, "active"]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## High-Level Design: The Graph Runner
|
|
82
|
+
|
|
83
|
+
The system exposes an MCP server that reads a YAML configuration file. When a tool is called on this MCP server, it executes the corresponding graph starting at the tool's entry node and continuing until the exit node is reached.
|
|
84
|
+
|
|
85
|
+
### The Workflow Lifecycle
|
|
86
|
+
|
|
87
|
+
1. **Parse**: Read the YAML configuration into a structure containing MCP server metadata, tool definitions, and the directed graph of nodes
|
|
88
|
+
2. **Initialize MCP Server**: Expose the MCP server with the tools defined in the configuration
|
|
89
|
+
3. **Tool Invocation**: When a tool is called:
|
|
90
|
+
- Receive tool arguments from the MCP client
|
|
91
|
+
- Start graph execution at the tool's entry node
|
|
92
|
+
- Entry node receives tool arguments and initializes the execution context
|
|
93
|
+
4. **Execute Node**:
|
|
94
|
+
- **Entry Node**: Receives tool arguments, initializes execution context, passes to next node
|
|
95
|
+
- **Pre-transform**: Apply JSONata to the incoming data to format the tool arguments (if node is an MCP tool call)
|
|
96
|
+
- **Call Tool**: Use the MCP SDK to `callTool` on the target server (if node is an MCP tool call)
|
|
97
|
+
- **Transform**: Apply JSONata expressions to transform data (if node is a transform node)
|
|
98
|
+
- **Route**: Evaluate JSON Logic against the current data state to decide which edge to follow next (if node is a switch/conditional)
|
|
99
|
+
- **Track History**: Record node execution with inputs and outputs for observability
|
|
100
|
+
5. **Exit Node**: When execution reaches the exit node, extract the final result and return it to the MCP tool caller
|
|
101
|
+
|
|
102
|
+
## Visual Tooling & Observability
|
|
103
|
+
|
|
104
|
+
### Component Recommendations
|
|
105
|
+
|
|
106
|
+
| Component | Tooling Recommendation |
|
|
107
|
+
|-----------|----------------------|
|
|
108
|
+
| **Visual Editor** | **React Flow** - Industry standard for node-based UIs, used by companies like Stripe and Typeform. Provides customizable nodes, edges, zooming, panning, and built-in components like MiniMap and Controls. [React Flow Documentation](https://reactflow.dev/) |
|
|
109
|
+
| **Observability** | OpenTelemetry - wrap each node execution in a "Span" to see the "Trace" of data through the graph in tools like Jaeger |
|
|
110
|
+
|
|
111
|
+
## Implementation Strategy: The YAML Standard
|
|
112
|
+
|
|
113
|
+
Graph definitions should feel like Kubernetes manifests or GitHub Actions - declarative and version-controlled.
|
|
114
|
+
|
|
115
|
+
### Configuration Structure
|
|
116
|
+
|
|
117
|
+
The YAML configuration centers around MCP server and tool definitions:
|
|
118
|
+
|
|
119
|
+
1. **MCP Server Metadata**: Defines the MCP server information (name, version, description)
|
|
120
|
+
2. **Tools**: Array of tool definitions, each containing:
|
|
121
|
+
- Standard MCP tool metadata (name, description)
|
|
122
|
+
- Input parameters schema (MCP tool parameter definitions)
|
|
123
|
+
- Output schema (what the tool returns)
|
|
124
|
+
- Note: Entry and exit nodes are defined in the nodes section with a `tool` field indicating which tool they belong to
|
|
125
|
+
3. **Nodes**: The directed graph of nodes that execute when tools are called. Node types include:
|
|
126
|
+
- **`entry`**: Entry point for a tool's graph execution. Receives tool arguments and initializes execution context.
|
|
127
|
+
- **`mcp`**: Calls an MCP tool on an internal or external MCP server using `callTool`
|
|
128
|
+
- **`transform`**: Applies JSONata expressions to transform data between nodes
|
|
129
|
+
- **`switch`**: Uses JSON Logic to conditionally route to different nodes based on data
|
|
130
|
+
- **`exit`**: Exit point for a tool's graph execution. Extracts and returns the final result to the MCP tool caller
|
|
131
|
+
|
|
132
|
+
### Example YAML Structure: count_files Tool
|
|
133
|
+
|
|
134
|
+
This example defines a `count_files` tool that takes a directory, lists its contents using the filesystem MCP server, counts the files, and returns the count:
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
version: "1.0"
|
|
138
|
+
|
|
139
|
+
# MCP Server Metadata
|
|
140
|
+
server:
|
|
141
|
+
name: "fileUtils"
|
|
142
|
+
version: "1.0.0"
|
|
143
|
+
description: "File utilities"
|
|
144
|
+
|
|
145
|
+
# Tool Definitions
|
|
146
|
+
tools:
|
|
147
|
+
- name: "count_files"
|
|
148
|
+
description: "Counts the number of files in a directory"
|
|
149
|
+
inputSchema:
|
|
150
|
+
type: "object"
|
|
151
|
+
properties:
|
|
152
|
+
directory:
|
|
153
|
+
type: "string"
|
|
154
|
+
description: "The directory path to count files in"
|
|
155
|
+
required:
|
|
156
|
+
- directory
|
|
157
|
+
outputSchema:
|
|
158
|
+
type: "object"
|
|
159
|
+
properties:
|
|
160
|
+
count:
|
|
161
|
+
type: "number"
|
|
162
|
+
description: "The number of files in the directory"
|
|
163
|
+
|
|
164
|
+
# MCP Servers used by the graph
|
|
165
|
+
servers:
|
|
166
|
+
filesystem:
|
|
167
|
+
command: "npx"
|
|
168
|
+
args:
|
|
169
|
+
- "-y"
|
|
170
|
+
- "@modelcontextprotocol/server-filesystem"
|
|
171
|
+
- "./tests/files"
|
|
172
|
+
|
|
173
|
+
# Graph Nodes
|
|
174
|
+
nodes:
|
|
175
|
+
# Entry node: Receives tool arguments
|
|
176
|
+
- id: "entry_count_files"
|
|
177
|
+
type: "entry"
|
|
178
|
+
tool: "count_files"
|
|
179
|
+
next: "list_directory_node"
|
|
180
|
+
|
|
181
|
+
# List directory contents
|
|
182
|
+
- id: "list_directory_node"
|
|
183
|
+
type: "mcp"
|
|
184
|
+
server: "filesystem"
|
|
185
|
+
tool: "list_directory"
|
|
186
|
+
args:
|
|
187
|
+
path: "$.input.directory"
|
|
188
|
+
next: "count_files_node"
|
|
189
|
+
|
|
190
|
+
# Transform and count files
|
|
191
|
+
- id: "count_files_node"
|
|
192
|
+
type: "transform"
|
|
193
|
+
transform:
|
|
194
|
+
expr: |
|
|
195
|
+
{ "count": $count($split(list_directory_node, "\n")) }
|
|
196
|
+
next: "exit_count_files"
|
|
197
|
+
|
|
198
|
+
# Exit node: Returns the count
|
|
199
|
+
- id: "exit_count_files"
|
|
200
|
+
type: "exit"
|
|
201
|
+
tool: "count_files"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Key Design Principles
|
|
205
|
+
|
|
206
|
+
1. **Declarative Configuration**: All logic expressed in YAML using standard expression languages (JSONata, JSON Logic)
|
|
207
|
+
2. **Observability**: Every transformation and decision is traceable and loggable
|
|
208
|
+
3. **No Embedded Code**: Avoid full programming languages to maintain clarity and safety
|
|
209
|
+
4. **Standard-Based**: Favor existing standards (JSONata, JSON Logic) over custom solutions
|
|
210
|
+
5. **Visual First**: Graph should be viewable and editable through a visual interface
|
|
211
|
+
6. **Execution Transparency**: Ability to observe graph execution in real-time
|
|
212
|
+
|
|
213
|
+
## System Components
|
|
214
|
+
|
|
215
|
+
1. **Executable**: Exposes an MCP server to run the graph (`mcpgraph` CLI)
|
|
216
|
+
2. **Programmatic API**: Exports `McpGraphApi` class for programmatic use (e.g., by visualizer applications)
|
|
217
|
+
3. **Graph Executor**: Core orchestration engine that executes the directed graph sequentially
|
|
218
|
+
4. **Execution Context**: Tracks execution state, data flow, and history
|
|
219
|
+
5. **Visual Editor**: Tools to visually view and edit the graph (future)
|
|
220
|
+
6. **Execution Observer**: Ability to observe and debug graph execution (future - see `docs/future-introspection-debugging.md`)
|