@alpic80/rivet-core 1.24.0-aidon.5 → 1.24.2-aidon.1
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 +9 -6
- package/dist/cjs/bundle.cjs +1382 -262
- package/dist/cjs/bundle.cjs.map +4 -4
- package/dist/esm/api/createProcessor.js +2 -0
- package/dist/esm/api/streaming.js +27 -0
- package/dist/esm/exports.js +1 -0
- package/dist/esm/integrations/CodeRunner.js +10 -2
- package/dist/esm/integrations/mcp/MCPBase.js +87 -0
- package/dist/esm/integrations/mcp/MCPProvider.js +23 -0
- package/dist/esm/integrations/mcp/MCPUtils.js +33 -0
- package/dist/esm/model/GraphProcessor.js +3 -0
- package/dist/esm/model/NodeRegistration.js +0 -1
- package/dist/esm/model/Nodes.js +9 -0
- package/dist/esm/model/nodes/ChatNodeBase.js +1 -1
- package/dist/esm/model/nodes/CodeNode.js +1 -1
- package/dist/esm/model/nodes/GetAllDatasetsNode.js +1 -1
- package/dist/esm/model/nodes/GraphInputNode.js +2 -0
- package/dist/esm/model/nodes/MCPDiscoveryNode.js +210 -0
- package/dist/esm/model/nodes/MCPGetPromptNode.js +233 -0
- package/dist/esm/model/nodes/MCPToolCallNode.js +261 -0
- package/dist/esm/model/nodes/ObjectNode.js +42 -21
- package/dist/esm/model/nodes/PromptNode.js +1 -1
- package/dist/esm/model/nodes/TextNode.js +13 -2
- package/dist/esm/plugins/anthropic/anthropic.js +22 -3
- package/dist/esm/plugins/anthropic/nodes/ChatAnthropicNode.js +33 -3
- package/dist/esm/plugins/google/google.js +29 -14
- package/dist/esm/plugins/google/nodes/ChatGoogleNode.js +70 -5
- package/dist/esm/utils/interpolation.js +155 -17
- package/dist/esm/utils/openai.js +24 -0
- package/dist/types/api/createProcessor.d.ts +3 -2
- package/dist/types/api/streaming.d.ts +8 -1
- package/dist/types/exports.d.ts +1 -0
- package/dist/types/integrations/CodeRunner.d.ts +4 -3
- package/dist/types/integrations/mcp/MCPBase.d.ts +18 -0
- package/dist/types/integrations/mcp/MCPProvider.d.ts +153 -0
- package/dist/types/integrations/mcp/MCPUtils.d.ts +9 -0
- package/dist/types/model/GraphProcessor.d.ts +1 -1
- package/dist/types/model/Nodes.d.ts +13 -2
- package/dist/types/model/ProcessContext.d.ts +5 -1
- package/dist/types/model/Project.d.ts +2 -0
- package/dist/types/model/nodes/GetAllDatasetsNode.d.ts +2 -2
- package/dist/types/model/nodes/MCPDiscoveryNode.d.ts +9 -0
- package/dist/types/model/nodes/MCPGetPromptNode.d.ts +23 -0
- package/dist/types/model/nodes/MCPToolCallNode.d.ts +26 -0
- package/dist/types/model/nodes/ObjectNode.d.ts +3 -2
- package/dist/types/model/nodes/TextNode.d.ts +2 -1
- package/dist/types/plugins/anthropic/anthropic.d.ts +21 -3
- package/dist/types/plugins/anthropic/nodes/ChatAnthropicNode.d.ts +5 -0
- package/dist/types/plugins/google/google.d.ts +12 -2
- package/dist/types/plugins/google/nodes/ChatGoogleNode.d.ts +7 -0
- package/dist/types/utils/interpolation.d.ts +6 -1
- package/dist/types/utils/openai.d.ts +24 -0
- package/package.json +3 -3
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import {} from '../NodeBase.js';
|
|
2
|
+
import { nanoid } from 'nanoid/non-secure';
|
|
3
|
+
import { NodeImpl } from '../NodeImpl.js';
|
|
4
|
+
import { nodeDefinition } from '../NodeDefinition.js';
|
|
5
|
+
import {} from '../GraphProcessor.js';
|
|
6
|
+
import {} from '../../index.js';
|
|
7
|
+
import { MCPError, MCPErrorType } from '../../integrations/mcp/MCPProvider.js';
|
|
8
|
+
import { coerceTypeOptional } from '../../utils/coerceType.js';
|
|
9
|
+
import { dedent, getInputOrData } from '../../utils/index.js';
|
|
10
|
+
import { getError } from '../../utils/errors.js';
|
|
11
|
+
import { getMCPBaseInputs } from '../../integrations/mcp/MCPBase.js';
|
|
12
|
+
import { getServerHelperMessage, getServerOptions, loadMCPConfiguration } from '../../integrations/mcp/MCPUtils.js';
|
|
13
|
+
import { keys } from '../../utils/typeSafety.js';
|
|
14
|
+
import { interpolate } from '../../utils/interpolation.js';
|
|
15
|
+
export class MCPGetPromptNodeImpl extends NodeImpl {
|
|
16
|
+
static create() {
|
|
17
|
+
const chartNode = {
|
|
18
|
+
type: 'mcpGetPrompt',
|
|
19
|
+
title: 'MCP Get Prompt',
|
|
20
|
+
id: nanoid(),
|
|
21
|
+
visualData: {
|
|
22
|
+
x: 0,
|
|
23
|
+
y: 0,
|
|
24
|
+
width: 250,
|
|
25
|
+
},
|
|
26
|
+
data: {
|
|
27
|
+
name: 'mcp-get-prompt-client',
|
|
28
|
+
version: '1.0.0',
|
|
29
|
+
transportType: 'stdio',
|
|
30
|
+
serverUrl: 'http://localhost:8080/mcp',
|
|
31
|
+
serverId: '',
|
|
32
|
+
promptName: '',
|
|
33
|
+
promptArguments: dedent `
|
|
34
|
+
{
|
|
35
|
+
"key": "value"
|
|
36
|
+
}`,
|
|
37
|
+
useNameInput: false,
|
|
38
|
+
useVersionInput: false,
|
|
39
|
+
usePromptNameInput: false,
|
|
40
|
+
usePromptArgumentsInput: false,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
return chartNode;
|
|
44
|
+
}
|
|
45
|
+
getInputDefinitions() {
|
|
46
|
+
const inputs = getMCPBaseInputs(this.data);
|
|
47
|
+
if (this.data.usePromptNameInput) {
|
|
48
|
+
inputs.push({
|
|
49
|
+
dataType: 'string',
|
|
50
|
+
id: 'promptName',
|
|
51
|
+
title: 'Prompt Name',
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (this.data.usePromptArgumentsInput) {
|
|
55
|
+
inputs.push({
|
|
56
|
+
dataType: 'object',
|
|
57
|
+
id: 'promptArguments',
|
|
58
|
+
title: 'Prompt Arguments',
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return inputs;
|
|
62
|
+
}
|
|
63
|
+
getOutputDefinitions() {
|
|
64
|
+
const outputDefinitions = [];
|
|
65
|
+
outputDefinitions.push({
|
|
66
|
+
id: 'prompt',
|
|
67
|
+
title: 'Prompt',
|
|
68
|
+
dataType: 'object',
|
|
69
|
+
description: 'Prompt response result',
|
|
70
|
+
});
|
|
71
|
+
return outputDefinitions;
|
|
72
|
+
}
|
|
73
|
+
async getEditors(context) {
|
|
74
|
+
const editors = [
|
|
75
|
+
{
|
|
76
|
+
type: 'string',
|
|
77
|
+
label: 'Name',
|
|
78
|
+
dataKey: 'name',
|
|
79
|
+
useInputToggleDataKey: 'useNameInput',
|
|
80
|
+
helperMessage: 'The name for the MCP Client',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: 'string',
|
|
84
|
+
label: 'Version',
|
|
85
|
+
dataKey: 'version',
|
|
86
|
+
useInputToggleDataKey: 'useVersionInput',
|
|
87
|
+
helperMessage: 'A version for the MCP Client',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
type: 'dropdown',
|
|
91
|
+
label: 'Transport Type',
|
|
92
|
+
dataKey: 'transportType',
|
|
93
|
+
options: [
|
|
94
|
+
{ label: 'HTTP', value: 'http' },
|
|
95
|
+
{ label: 'STDIO', value: 'stdio' },
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
type: 'string',
|
|
100
|
+
label: 'Prompt Name',
|
|
101
|
+
dataKey: 'promptName',
|
|
102
|
+
useInputToggleDataKey: 'usePromptNameInput',
|
|
103
|
+
helperMessage: 'The name for the MCP prompt',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
type: 'code',
|
|
107
|
+
label: 'Prompt Arguments',
|
|
108
|
+
dataKey: 'promptArguments',
|
|
109
|
+
useInputToggleDataKey: 'usePromptArgumentsInput',
|
|
110
|
+
language: 'json',
|
|
111
|
+
helperMessage: 'Arguments to provide the prompt',
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
if (this.data.transportType === 'http') {
|
|
115
|
+
editors.push({
|
|
116
|
+
type: 'string',
|
|
117
|
+
label: 'Server URL',
|
|
118
|
+
dataKey: 'serverUrl',
|
|
119
|
+
useInputToggleDataKey: 'useServerUrlInput',
|
|
120
|
+
helperMessage: 'The endpoint URL for the MCP server to connect',
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
else if (this.data.transportType === 'stdio') {
|
|
124
|
+
const serverOptions = await getServerOptions(context);
|
|
125
|
+
editors.push({
|
|
126
|
+
type: 'dropdown',
|
|
127
|
+
label: 'Server ID',
|
|
128
|
+
dataKey: 'serverId',
|
|
129
|
+
helperMessage: getServerHelperMessage(context, serverOptions.length),
|
|
130
|
+
options: serverOptions,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return editors;
|
|
134
|
+
}
|
|
135
|
+
getBody(context) {
|
|
136
|
+
let base;
|
|
137
|
+
if (this.data.transportType === 'http') {
|
|
138
|
+
base = this.data.useServerUrlInput ? '(Using Server URL Input)' : this.data.serverUrl;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
base = `Server ID: ${this.data.serverId || '(None)'}`;
|
|
142
|
+
}
|
|
143
|
+
const namePart = `Name: ${this.data.name}`;
|
|
144
|
+
const versionPart = `Version: ${this.data.version}`;
|
|
145
|
+
const parts = [namePart, versionPart, base];
|
|
146
|
+
if (context.executor !== 'nodejs') {
|
|
147
|
+
parts.push('(Requires Node Executor)');
|
|
148
|
+
}
|
|
149
|
+
return parts.join('\n');
|
|
150
|
+
}
|
|
151
|
+
static getUIData() {
|
|
152
|
+
return {
|
|
153
|
+
infoBoxBody: dedent `
|
|
154
|
+
Connects to an MCP (Model Context Protocol) server and gets a prompt response.
|
|
155
|
+
`,
|
|
156
|
+
infoBoxTitle: 'MCP Get Prompt Node',
|
|
157
|
+
contextMenuTitle: 'MCP Get Prompt',
|
|
158
|
+
group: ['MCP'],
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
async process(inputs, context) {
|
|
162
|
+
const name = getInputOrData(this.data, inputs, 'name', 'string');
|
|
163
|
+
const version = getInputOrData(this.data, inputs, 'version', 'string');
|
|
164
|
+
const promptName = getInputOrData(this.data, inputs, 'promptName', 'string');
|
|
165
|
+
let promptArguments;
|
|
166
|
+
if (this.data.usePromptArgumentsInput) {
|
|
167
|
+
promptArguments = getInputOrData(this.data, inputs, 'promptArguments', 'object');
|
|
168
|
+
if (promptArguments == null) {
|
|
169
|
+
throw new MCPError(MCPErrorType.INVALID_SCHEMA, 'Cannot parse tool argument with input toggle on');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
const inputMap = keys(inputs)
|
|
174
|
+
.filter((key) => key.startsWith('input'))
|
|
175
|
+
.reduce((acc, key) => {
|
|
176
|
+
const stringValue = coerceTypeOptional(inputs[key], 'string') ?? '';
|
|
177
|
+
const interpolationKey = key.slice('input-'.length);
|
|
178
|
+
acc[interpolationKey] = stringValue;
|
|
179
|
+
return acc;
|
|
180
|
+
}, {});
|
|
181
|
+
const interpolated = interpolate(this.data.promptArguments ?? '', inputMap);
|
|
182
|
+
promptArguments = JSON.parse(interpolated);
|
|
183
|
+
}
|
|
184
|
+
const getPromptRequest = {
|
|
185
|
+
name: promptName,
|
|
186
|
+
arguments: promptArguments,
|
|
187
|
+
};
|
|
188
|
+
const transportType = getInputOrData(this.data, inputs, 'transportType', 'string');
|
|
189
|
+
let getPromptResponse = undefined;
|
|
190
|
+
try {
|
|
191
|
+
if (!context.mcpProvider) {
|
|
192
|
+
throw new Error('MCP Provider not found');
|
|
193
|
+
}
|
|
194
|
+
if (transportType === 'http') {
|
|
195
|
+
const serverUrl = getInputOrData(this.data, inputs, 'serverUrl', 'string');
|
|
196
|
+
if (!serverUrl || serverUrl === '') {
|
|
197
|
+
throw new MCPError(MCPErrorType.SERVER_NOT_FOUND, 'No server URL was provided');
|
|
198
|
+
}
|
|
199
|
+
if (!serverUrl.includes('/mcp')) {
|
|
200
|
+
throw new MCPError(MCPErrorType.SERVER_COMMUNICATION_FAILED, 'Include /mcp in your server URL. For example: http://localhost:8080/mcp');
|
|
201
|
+
}
|
|
202
|
+
getPromptResponse = await context.mcpProvider.getHTTPrompt({ name, version }, serverUrl, getPromptRequest);
|
|
203
|
+
}
|
|
204
|
+
else if (transportType === 'stdio') {
|
|
205
|
+
const serverId = this.data.serverId ?? '';
|
|
206
|
+
const mcpConfig = await loadMCPConfiguration(context);
|
|
207
|
+
if (!mcpConfig.mcpServers[serverId]) {
|
|
208
|
+
throw new MCPError(MCPErrorType.SERVER_NOT_FOUND, `Server ${serverId} not found in MCP config`);
|
|
209
|
+
}
|
|
210
|
+
const serverConfig = {
|
|
211
|
+
config: mcpConfig.mcpServers[serverId],
|
|
212
|
+
serverId,
|
|
213
|
+
};
|
|
214
|
+
getPromptResponse = await context.mcpProvider.getStdioPrompt({ name, version }, serverConfig, getPromptRequest);
|
|
215
|
+
}
|
|
216
|
+
const output = {};
|
|
217
|
+
output['response'] = {
|
|
218
|
+
type: 'object',
|
|
219
|
+
value: getPromptResponse,
|
|
220
|
+
};
|
|
221
|
+
return output;
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
const { message } = getError(err);
|
|
225
|
+
if (context.executor === 'browser') {
|
|
226
|
+
throw new Error('Failed to create Client without a node executor');
|
|
227
|
+
}
|
|
228
|
+
console.log(message);
|
|
229
|
+
throw err;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
export const mcpGetPromptNode = nodeDefinition(MCPGetPromptNodeImpl, 'MCP Get Prompt');
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import {} from '../NodeBase.js';
|
|
2
|
+
import { nanoid } from 'nanoid/non-secure';
|
|
3
|
+
import { NodeImpl } from '../NodeImpl.js';
|
|
4
|
+
import { nodeDefinition } from '../NodeDefinition.js';
|
|
5
|
+
import {} from '../GraphProcessor.js';
|
|
6
|
+
import {} from '../../index.js';
|
|
7
|
+
import { MCPError, MCPErrorType } from '../../integrations/mcp/MCPProvider.js';
|
|
8
|
+
import { coerceTypeOptional } from '../../utils/coerceType.js';
|
|
9
|
+
import { getInputOrData } from '../../utils/index.js';
|
|
10
|
+
import { getError } from '../../utils/errors.js';
|
|
11
|
+
import { getMCPBaseInputs } from '../../integrations/mcp/MCPBase.js';
|
|
12
|
+
import { getServerHelperMessage, getServerOptions, loadMCPConfiguration } from '../../integrations/mcp/MCPUtils.js';
|
|
13
|
+
import { dedent } from 'ts-dedent';
|
|
14
|
+
import {} from '../EditorDefinition.js';
|
|
15
|
+
import { interpolate } from '../../utils/interpolation.js';
|
|
16
|
+
import { keys } from '../../utils/typeSafety.js';
|
|
17
|
+
export class MCPToolCallNodeImpl extends NodeImpl {
|
|
18
|
+
static create() {
|
|
19
|
+
const chartNode = {
|
|
20
|
+
type: 'mcpToolCall',
|
|
21
|
+
title: 'MCP Tool Call',
|
|
22
|
+
id: nanoid(),
|
|
23
|
+
visualData: {
|
|
24
|
+
x: 0,
|
|
25
|
+
y: 0,
|
|
26
|
+
width: 250,
|
|
27
|
+
},
|
|
28
|
+
data: {
|
|
29
|
+
name: 'mcp-tool-call-client',
|
|
30
|
+
version: '1.0.0',
|
|
31
|
+
transportType: 'stdio',
|
|
32
|
+
serverUrl: 'http://localhost:8080/mcp',
|
|
33
|
+
serverId: '',
|
|
34
|
+
toolName: '',
|
|
35
|
+
toolArguments: dedent `
|
|
36
|
+
{
|
|
37
|
+
"key": "value"
|
|
38
|
+
}`,
|
|
39
|
+
toolCallId: '',
|
|
40
|
+
useNameInput: false,
|
|
41
|
+
useVersionInput: false,
|
|
42
|
+
useToolNameInput: true,
|
|
43
|
+
useToolArgumentsInput: true,
|
|
44
|
+
useToolCallIdInput: true,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
return chartNode;
|
|
48
|
+
}
|
|
49
|
+
getInputDefinitions() {
|
|
50
|
+
const inputs = getMCPBaseInputs(this.data);
|
|
51
|
+
if (this.data.useToolNameInput) {
|
|
52
|
+
inputs.push({
|
|
53
|
+
dataType: 'string',
|
|
54
|
+
id: 'toolName',
|
|
55
|
+
title: 'Tool Name',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
if (this.data.useToolArgumentsInput) {
|
|
59
|
+
inputs.push({
|
|
60
|
+
dataType: 'object',
|
|
61
|
+
id: 'toolArguments',
|
|
62
|
+
title: 'Tool Arguments',
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (this.data.useToolCallIdInput) {
|
|
66
|
+
inputs.push({
|
|
67
|
+
dataType: 'object',
|
|
68
|
+
id: 'toolCallId',
|
|
69
|
+
title: 'Tool ID',
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return inputs;
|
|
73
|
+
}
|
|
74
|
+
getOutputDefinitions() {
|
|
75
|
+
const outputDefinitions = [];
|
|
76
|
+
outputDefinitions.push({
|
|
77
|
+
id: 'response',
|
|
78
|
+
title: 'Response',
|
|
79
|
+
dataType: 'object',
|
|
80
|
+
description: 'Response from the Tool Call',
|
|
81
|
+
});
|
|
82
|
+
outputDefinitions.push({
|
|
83
|
+
id: 'toolCallId',
|
|
84
|
+
title: 'Tool ID',
|
|
85
|
+
dataType: 'string',
|
|
86
|
+
description: 'ID associated with the Tool Call',
|
|
87
|
+
});
|
|
88
|
+
return outputDefinitions;
|
|
89
|
+
}
|
|
90
|
+
async getEditors(context) {
|
|
91
|
+
const editors = [
|
|
92
|
+
{
|
|
93
|
+
type: 'string',
|
|
94
|
+
label: 'Name',
|
|
95
|
+
dataKey: 'name',
|
|
96
|
+
useInputToggleDataKey: 'useNameInput',
|
|
97
|
+
helperMessage: 'The name for the MCP Client',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
type: 'string',
|
|
101
|
+
label: 'Version',
|
|
102
|
+
dataKey: 'version',
|
|
103
|
+
useInputToggleDataKey: 'useVersionInput',
|
|
104
|
+
helperMessage: 'A version for the MCP Client',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
type: 'dropdown',
|
|
108
|
+
label: 'Transport Type',
|
|
109
|
+
dataKey: 'transportType',
|
|
110
|
+
options: [
|
|
111
|
+
{ label: 'HTTP', value: 'http' },
|
|
112
|
+
{ label: 'STDIO', value: 'stdio' },
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
type: 'string',
|
|
117
|
+
label: 'Tool Name',
|
|
118
|
+
dataKey: 'toolName',
|
|
119
|
+
useInputToggleDataKey: 'useToolNameInput',
|
|
120
|
+
helperMessage: 'The name for the MCP Tool Call',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
type: 'code',
|
|
124
|
+
label: 'Tool Arguments',
|
|
125
|
+
dataKey: 'toolArguments',
|
|
126
|
+
language: 'json',
|
|
127
|
+
useInputToggleDataKey: 'useToolArgumentsInput',
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
type: 'string',
|
|
131
|
+
label: 'Tool ID',
|
|
132
|
+
dataKey: 'toolCallId',
|
|
133
|
+
useInputToggleDataKey: 'useToolCallIdInput',
|
|
134
|
+
helperMessage: 'The name for the MCP Tool Call',
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
if (this.data.transportType === 'http') {
|
|
138
|
+
editors.push({
|
|
139
|
+
type: 'string',
|
|
140
|
+
label: 'Server URL',
|
|
141
|
+
dataKey: 'serverUrl',
|
|
142
|
+
useInputToggleDataKey: 'useServerUrlInput',
|
|
143
|
+
helperMessage: 'The endpoint URL for the MCP server to connect',
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
else if (this.data.transportType === 'stdio') {
|
|
147
|
+
const serverOptions = await getServerOptions(context);
|
|
148
|
+
editors.push({
|
|
149
|
+
type: 'dropdown',
|
|
150
|
+
label: 'Server ID',
|
|
151
|
+
dataKey: 'serverId',
|
|
152
|
+
helperMessage: getServerHelperMessage(context, serverOptions.length),
|
|
153
|
+
options: serverOptions,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return editors;
|
|
157
|
+
}
|
|
158
|
+
getBody(context) {
|
|
159
|
+
let base;
|
|
160
|
+
if (this.data.transportType === 'http') {
|
|
161
|
+
base = this.data.useServerUrlInput ? '(Using Server URL Input)' : this.data.serverUrl;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
base = `Server ID: ${this.data.serverId || '(None)'}`;
|
|
165
|
+
}
|
|
166
|
+
const namePart = `Name: ${this.data.name}`;
|
|
167
|
+
const versionPart = `Version: ${this.data.version}`;
|
|
168
|
+
const parts = [namePart, versionPart, base];
|
|
169
|
+
if (context.executor !== 'nodejs') {
|
|
170
|
+
parts.push('(Requires Node Executor)');
|
|
171
|
+
}
|
|
172
|
+
return parts.join('\n');
|
|
173
|
+
}
|
|
174
|
+
static getUIData() {
|
|
175
|
+
return {
|
|
176
|
+
infoBoxBody: dedent `
|
|
177
|
+
Connects to an MCP (Model Context Protocol) server and gets a tool call response.
|
|
178
|
+
`,
|
|
179
|
+
infoBoxTitle: 'MCP Tool Call Node',
|
|
180
|
+
contextMenuTitle: 'MCP Tool Call',
|
|
181
|
+
group: ['MCP'],
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
async process(inputs, context) {
|
|
185
|
+
const name = getInputOrData(this.data, inputs, 'name', 'string');
|
|
186
|
+
const version = getInputOrData(this.data, inputs, 'version', 'string');
|
|
187
|
+
const toolName = getInputOrData(this.data, inputs, 'toolName', 'string');
|
|
188
|
+
const toolCallId = getInputOrData(this.data, inputs, 'toolCallId', 'string');
|
|
189
|
+
let toolArguments;
|
|
190
|
+
if (this.data.useToolArgumentsInput) {
|
|
191
|
+
toolArguments = getInputOrData(this.data, inputs, 'toolArguments', 'object');
|
|
192
|
+
if (toolArguments == null) {
|
|
193
|
+
throw new MCPError(MCPErrorType.INVALID_SCHEMA, 'Cannot parse tool argument with input toggle on');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
const inputMap = keys(inputs)
|
|
198
|
+
.filter((key) => key.startsWith('input'))
|
|
199
|
+
.reduce((acc, key) => {
|
|
200
|
+
const stringValue = coerceTypeOptional(inputs[key], 'string') ?? '';
|
|
201
|
+
const interpolationKey = key.slice('input-'.length);
|
|
202
|
+
acc[interpolationKey] = stringValue;
|
|
203
|
+
return acc;
|
|
204
|
+
}, {});
|
|
205
|
+
const interpolated = interpolate(this.data.toolArguments ?? '', inputMap);
|
|
206
|
+
toolArguments = JSON.parse(interpolated);
|
|
207
|
+
}
|
|
208
|
+
const toolCall = {
|
|
209
|
+
name: toolName,
|
|
210
|
+
arguments: toolArguments,
|
|
211
|
+
};
|
|
212
|
+
const transportType = getInputOrData(this.data, inputs, 'transportType', 'string');
|
|
213
|
+
let toolResponse = undefined;
|
|
214
|
+
try {
|
|
215
|
+
if (!context.mcpProvider) {
|
|
216
|
+
throw new Error('MCP Provider not found');
|
|
217
|
+
}
|
|
218
|
+
if (transportType === 'http') {
|
|
219
|
+
const serverUrl = getInputOrData(this.data, inputs, 'serverUrl', 'string');
|
|
220
|
+
if (!serverUrl || serverUrl === '') {
|
|
221
|
+
throw new MCPError(MCPErrorType.SERVER_NOT_FOUND, 'No server URL was provided');
|
|
222
|
+
}
|
|
223
|
+
if (!serverUrl.includes('/mcp')) {
|
|
224
|
+
throw new MCPError(MCPErrorType.SERVER_COMMUNICATION_FAILED, 'Include /mcp in your server URL. For example: http://localhost:8080/mcp');
|
|
225
|
+
}
|
|
226
|
+
toolResponse = await context.mcpProvider.httpToolCall({ name, version }, serverUrl, toolCall);
|
|
227
|
+
}
|
|
228
|
+
else if (transportType === 'stdio') {
|
|
229
|
+
const serverId = this.data.serverId ?? '';
|
|
230
|
+
const mcpConfig = await loadMCPConfiguration(context);
|
|
231
|
+
if (!mcpConfig.mcpServers[serverId]) {
|
|
232
|
+
throw new MCPError(MCPErrorType.SERVER_NOT_FOUND, `Server ${serverId} not found in MCP config`);
|
|
233
|
+
}
|
|
234
|
+
const serverConfig = {
|
|
235
|
+
config: mcpConfig.mcpServers[serverId],
|
|
236
|
+
serverId,
|
|
237
|
+
};
|
|
238
|
+
toolResponse = await context.mcpProvider.stdioToolCall({ name, version }, serverConfig, toolCall);
|
|
239
|
+
}
|
|
240
|
+
const output = {};
|
|
241
|
+
output['response'] = {
|
|
242
|
+
type: 'object[]',
|
|
243
|
+
value: toolResponse?.content,
|
|
244
|
+
};
|
|
245
|
+
output['toolCallId'] = {
|
|
246
|
+
type: 'string',
|
|
247
|
+
value: toolCallId,
|
|
248
|
+
};
|
|
249
|
+
return output;
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
const { message } = getError(err);
|
|
253
|
+
if (context.executor === 'browser') {
|
|
254
|
+
throw new Error('Failed to create Client without a node executor');
|
|
255
|
+
}
|
|
256
|
+
console.log(message);
|
|
257
|
+
throw err;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
export const mcpToolCallNode = nodeDefinition(MCPToolCallNodeImpl, 'MCP Tool Call');
|
|
@@ -5,6 +5,7 @@ import { nodeDefinition } from '../NodeDefinition.js';
|
|
|
5
5
|
import {} from '../DataValue.js';
|
|
6
6
|
import { dedent } from 'ts-dedent';
|
|
7
7
|
import {} from '../EditorDefinition.js';
|
|
8
|
+
import { resolveExpressionRawValue, unwrapPotentialDataValue } from '../../utils/interpolation.js';
|
|
8
9
|
const DEFAULT_JSON_TEMPLATE = `{
|
|
9
10
|
"key": "{{input}}"
|
|
10
11
|
}`;
|
|
@@ -26,17 +27,23 @@ export class ObjectNodeImpl extends NodeImpl {
|
|
|
26
27
|
return chartNode;
|
|
27
28
|
}
|
|
28
29
|
getInputDefinitions() {
|
|
29
|
-
//
|
|
30
|
-
const
|
|
31
|
-
|
|
30
|
+
// Provide default empty string for jsonTemplate if undefined
|
|
31
|
+
const jsonTemplate = this.chartNode.data.jsonTemplate ?? '';
|
|
32
|
+
const matches = jsonTemplate.match(/\{\{([^}]+?)\}\}/g); // matches is string[] | null
|
|
33
|
+
const allTokens = matches ?? []; // allTokens is now explicitly string[]
|
|
34
|
+
const inputTokens = allTokens
|
|
35
|
+
// id and title should not have the {{ and }}
|
|
36
|
+
.map((token) => token.slice(2, -2).trim())
|
|
37
|
+
.filter((tokenContent) => !tokenContent.startsWith('@graphInputs.') && !tokenContent.startsWith('@context.'))
|
|
38
|
+
.filter((token) => token !== '');
|
|
39
|
+
return [...new Set(inputTokens)].map((inputName) => {
|
|
32
40
|
return {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
title: inputName.slice(2, -2),
|
|
41
|
+
id: inputName,
|
|
42
|
+
title: inputName,
|
|
36
43
|
dataType: 'any',
|
|
37
44
|
required: false,
|
|
38
45
|
};
|
|
39
|
-
})
|
|
46
|
+
});
|
|
40
47
|
}
|
|
41
48
|
getOutputDefinitions() {
|
|
42
49
|
return [
|
|
@@ -77,10 +84,22 @@ export class ObjectNodeImpl extends NodeImpl {
|
|
|
77
84
|
group: ['Objects'],
|
|
78
85
|
};
|
|
79
86
|
}
|
|
80
|
-
interpolate(baseString, values) {
|
|
81
|
-
return baseString.replace(/("?)\{\{([^}]
|
|
87
|
+
interpolate(baseString, values, graphInputNodeValues, contextValues) {
|
|
88
|
+
return baseString.replace(/("?)\{\{([^}]+?)\}\}("?)/g, (_m, openQuote, key, _closeQuote) => {
|
|
82
89
|
const isQuoted = Boolean(openQuote);
|
|
83
|
-
const
|
|
90
|
+
const trimmedKey = key.trim(); // Use trimmedKey for lookups
|
|
91
|
+
let value;
|
|
92
|
+
const graphInputPrefix = '@graphInputs.';
|
|
93
|
+
const contextPrefix = '@context.';
|
|
94
|
+
if (trimmedKey.startsWith(graphInputPrefix) && graphInputNodeValues) {
|
|
95
|
+
value = resolveExpressionRawValue(graphInputNodeValues, trimmedKey.substring(graphInputPrefix.length), 'graphInputs');
|
|
96
|
+
}
|
|
97
|
+
else if (trimmedKey.startsWith(contextPrefix) && contextValues) {
|
|
98
|
+
value = resolveExpressionRawValue(contextValues, trimmedKey.substring(contextPrefix.length), 'context');
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
value = values[trimmedKey]; // Original logic for non-@ variables
|
|
102
|
+
}
|
|
84
103
|
if (value == null) {
|
|
85
104
|
return 'null';
|
|
86
105
|
}
|
|
@@ -96,23 +115,25 @@ export class ObjectNodeImpl extends NodeImpl {
|
|
|
96
115
|
return JSON.stringify(value);
|
|
97
116
|
});
|
|
98
117
|
}
|
|
99
|
-
async process(inputs) {
|
|
118
|
+
async process(inputs, context) {
|
|
100
119
|
const inputMap = Object.keys(inputs).reduce((acc, key) => {
|
|
101
|
-
acc[key] = inputs[key]
|
|
120
|
+
acc[key] = unwrapPotentialDataValue(inputs[key]);
|
|
102
121
|
return acc;
|
|
103
122
|
}, {});
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
123
|
+
const interpolatedString = this.interpolate(this.chartNode.data.jsonTemplate, inputMap, context.graphInputNodeValues, // Pass graph inputs
|
|
124
|
+
context.contextValues // Pass context values
|
|
125
|
+
);
|
|
126
|
+
let outputValue;
|
|
127
|
+
try {
|
|
128
|
+
outputValue = JSON.parse(interpolatedString);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
throw new Error(`Failed to parse JSON template: ${err.message}`);
|
|
112
132
|
}
|
|
133
|
+
const outputType = Array.isArray(outputValue) ? 'object[]' : 'object';
|
|
113
134
|
return {
|
|
114
135
|
output: {
|
|
115
|
-
type:
|
|
136
|
+
type: outputType,
|
|
116
137
|
value: outputValue,
|
|
117
138
|
},
|
|
118
139
|
};
|
|
@@ -177,7 +177,7 @@ export class PromptNodeImpl extends NodeImpl {
|
|
|
177
177
|
}
|
|
178
178
|
async process(inputs, context) {
|
|
179
179
|
const inputMap = mapValues(inputs, (input) => coerceType(input, 'string'));
|
|
180
|
-
const outputValue = interpolate(this.chartNode.data.promptText, inputMap);
|
|
180
|
+
const outputValue = interpolate(this.chartNode.data.promptText, inputMap, context.graphInputNodeValues, context.contextValues);
|
|
181
181
|
const type = getInputOrData(this.data, inputs, 'type', 'string');
|
|
182
182
|
const isCacheBreakpoint = getInputOrData(this.data, inputs, 'isCacheBreakpoint', 'boolean');
|
|
183
183
|
if (['assistant', 'system', 'user', 'function'].includes(type) === false) {
|
|
@@ -20,6 +20,7 @@ export class TextNodeImpl extends NodeImpl {
|
|
|
20
20
|
},
|
|
21
21
|
data: {
|
|
22
22
|
text: '{{input}}',
|
|
23
|
+
normalizeLineEndings: true, // Default to true for better compatibility
|
|
23
24
|
},
|
|
24
25
|
};
|
|
25
26
|
return chartNode;
|
|
@@ -61,6 +62,12 @@ export class TextNodeImpl extends NodeImpl {
|
|
|
61
62
|
language: 'prompt-interpolation-markdown',
|
|
62
63
|
theme: 'prompt-interpolation',
|
|
63
64
|
},
|
|
65
|
+
{
|
|
66
|
+
type: 'toggle',
|
|
67
|
+
label: 'Normalize Line Endings',
|
|
68
|
+
dataKey: 'normalizeLineEndings',
|
|
69
|
+
helperMessage: 'Normalize line endings to use only LF (\\n) instead of CRLF (\\r\\n).',
|
|
70
|
+
},
|
|
64
71
|
];
|
|
65
72
|
}
|
|
66
73
|
getBody() {
|
|
@@ -72,13 +79,17 @@ export class TextNodeImpl extends NodeImpl {
|
|
|
72
79
|
text: truncated,
|
|
73
80
|
};
|
|
74
81
|
}
|
|
75
|
-
async process(inputs) {
|
|
82
|
+
async process(inputs, context) {
|
|
76
83
|
const inputMap = Object.keys(inputs).reduce((acc, key) => {
|
|
77
84
|
const stringValue = coerceTypeOptional(inputs[key], 'string') ?? '';
|
|
78
85
|
acc[key] = stringValue;
|
|
79
86
|
return acc;
|
|
80
87
|
}, {});
|
|
81
|
-
|
|
88
|
+
let outputValue = interpolate(this.chartNode.data.text, inputMap, context.graphInputNodeValues, // Pass graph inputs
|
|
89
|
+
context.contextValues);
|
|
90
|
+
if (this.chartNode.data.normalizeLineEndings) {
|
|
91
|
+
outputValue = outputValue.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
92
|
+
}
|
|
82
93
|
return {
|
|
83
94
|
output: {
|
|
84
95
|
type: 'string',
|