@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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {} from '../index.js';
|
|
2
2
|
import { getProcessorEvents, getProcessorSSEStream, getSingleNodeStream } from './streaming.js';
|
|
3
|
+
// eslint-disable-next-line import/no-cycle -- Necessary cycle
|
|
3
4
|
import { GraphProcessor } from '../model/GraphProcessor.js';
|
|
4
5
|
import { deserializeProject } from '../utils/serialization/serialization.js';
|
|
5
6
|
import { DEFAULT_CHAT_NODE_TIMEOUT } from '../utils/defaults.js';
|
|
@@ -89,6 +90,7 @@ export function coreCreateProcessor(project, options) {
|
|
|
89
90
|
nativeApi: options.nativeApi,
|
|
90
91
|
datasetProvider: options.datasetProvider,
|
|
91
92
|
audioProvider: options.audioProvider,
|
|
93
|
+
mcpProvider: options.mcpProvider,
|
|
92
94
|
codeRunner: options.codeRunner,
|
|
93
95
|
projectPath: options.projectPath,
|
|
94
96
|
projectReferenceLoader: options.projectReferenceLoader,
|
|
@@ -88,6 +88,21 @@ export async function* getProcessorEvents(processor, spec) {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
+
export const createOnStreamUserEvents = (eventList, handleUserEvent) => {
|
|
92
|
+
if (!eventList?.trim()) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
const events = eventList.split(',').map(e => e.trim()).filter(Boolean);
|
|
96
|
+
if (!events.length) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
return Object.fromEntries(events.map(event => [
|
|
100
|
+
event,
|
|
101
|
+
async (data) => {
|
|
102
|
+
await handleUserEvent(event, data);
|
|
103
|
+
}
|
|
104
|
+
]));
|
|
105
|
+
};
|
|
91
106
|
/**
|
|
92
107
|
* Creates a ReadableStream for processor events, following the Server-Sent Events protocol.
|
|
93
108
|
* https://developer.mozilla.org/en-US/docs/Web/API/EventSource
|
|
@@ -105,6 +120,18 @@ spec) {
|
|
|
105
120
|
}
|
|
106
121
|
return new ReadableStream({
|
|
107
122
|
async start(controller) {
|
|
123
|
+
const userEventHandler = async (eventName, data) => {
|
|
124
|
+
sendEvent(controller, 'event', {
|
|
125
|
+
name: eventName,
|
|
126
|
+
message: coerceType(data, 'string')
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
const streamEvents = createOnStreamUserEvents(spec.userStreamEvents, userEventHandler);
|
|
130
|
+
if (streamEvents) {
|
|
131
|
+
for (const [name, fn] of Object.entries(streamEvents)) {
|
|
132
|
+
processor.onUserEvent(name, fn);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
108
135
|
try {
|
|
109
136
|
for await (const event of getProcessorEvents(processor, spec)) {
|
|
110
137
|
sendEvent(controller, event.type, event);
|
package/dist/esm/exports.js
CHANGED
|
@@ -14,6 +14,7 @@ export * from './model/ProcessContext.js';
|
|
|
14
14
|
export * from './integrations/integrations.js';
|
|
15
15
|
import './integrations/enableIntegrations.js';
|
|
16
16
|
export * from './integrations/VectorDatabase.js';
|
|
17
|
+
export * from './integrations/mcp/MCPProvider.js';
|
|
17
18
|
export * from './integrations/EmbeddingGenerator.js';
|
|
18
19
|
export * from './integrations/LLMProvider.js';
|
|
19
20
|
export * from './recording/ExecutionRecorder.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// eslint-disable-next-line import/no-cycle -- There has to be a cycle if we're to import the entirety of Rivet here.
|
|
2
2
|
import * as Rivet from '../exports.js';
|
|
3
3
|
export class IsomorphicCodeRunner {
|
|
4
|
-
async runCode(code, inputs, options) {
|
|
4
|
+
async runCode(code, inputs, options, graphInputs, contextValues) {
|
|
5
5
|
const argNames = ['inputs'];
|
|
6
6
|
const args = [inputs];
|
|
7
7
|
if (options.includeRequire) {
|
|
@@ -22,6 +22,14 @@ export class IsomorphicCodeRunner {
|
|
|
22
22
|
argNames.push('Rivet');
|
|
23
23
|
args.push(Rivet);
|
|
24
24
|
}
|
|
25
|
+
if (graphInputs) {
|
|
26
|
+
argNames.push('graphInputs');
|
|
27
|
+
args.push(graphInputs);
|
|
28
|
+
}
|
|
29
|
+
if (contextValues) {
|
|
30
|
+
argNames.push('context');
|
|
31
|
+
args.push(contextValues);
|
|
32
|
+
}
|
|
25
33
|
argNames.push(code);
|
|
26
34
|
const AsyncFunction = async function () { }.constructor;
|
|
27
35
|
const codeFunction = new AsyncFunction(...argNames);
|
|
@@ -30,7 +38,7 @@ export class IsomorphicCodeRunner {
|
|
|
30
38
|
}
|
|
31
39
|
}
|
|
32
40
|
export class NotAllowedCodeRunner {
|
|
33
|
-
async runCode(_code, _inputs, _options) {
|
|
41
|
+
async runCode(_code, _inputs, _options, _graphInputs, _contextValues) {
|
|
34
42
|
throw new Error('Dynamic code execution is disabled.');
|
|
35
43
|
}
|
|
36
44
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { getServerHelperMessage, getServerOptions } from './MCPUtils.js';
|
|
2
|
+
export const getMCPBaseInputs = (data) => {
|
|
3
|
+
const inputs = [];
|
|
4
|
+
if (data.useNameInput) {
|
|
5
|
+
inputs.push({
|
|
6
|
+
dataType: 'string',
|
|
7
|
+
id: 'name',
|
|
8
|
+
title: 'Name',
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
if (data.useVersionInput) {
|
|
12
|
+
inputs.push({
|
|
13
|
+
dataType: 'string',
|
|
14
|
+
id: 'version',
|
|
15
|
+
title: 'Version',
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
if (data.transportType === 'http' && data.useServerUrlInput) {
|
|
19
|
+
inputs.push({
|
|
20
|
+
dataType: 'string',
|
|
21
|
+
id: 'serverUrl',
|
|
22
|
+
title: 'Server URL',
|
|
23
|
+
description: 'The endpoint URL for the MCP server',
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return inputs;
|
|
27
|
+
};
|
|
28
|
+
export const getMCPBaseEditors = async (context, data) => {
|
|
29
|
+
const editors = [];
|
|
30
|
+
editors.push([
|
|
31
|
+
{
|
|
32
|
+
type: 'toggle',
|
|
33
|
+
label: 'Output Tools',
|
|
34
|
+
dataKey: 'useToolsOutput',
|
|
35
|
+
helperMessage: 'Toggle on if you want to get a Tools output',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: 'toggle',
|
|
39
|
+
label: 'Output Prompts',
|
|
40
|
+
dataKey: 'usePromptsOutput',
|
|
41
|
+
helperMessage: 'Toggle on if you want to get a Prompts output',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
type: 'string',
|
|
45
|
+
label: 'Name',
|
|
46
|
+
dataKey: 'name',
|
|
47
|
+
useInputToggleDataKey: 'useNameInput',
|
|
48
|
+
helperMessage: 'The name for the MCP Client',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: 'string',
|
|
52
|
+
label: 'Version',
|
|
53
|
+
dataKey: 'version',
|
|
54
|
+
useInputToggleDataKey: 'useVersionInput',
|
|
55
|
+
helperMessage: 'A version for the MCP Client',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
type: 'dropdown',
|
|
59
|
+
label: 'Transport Type',
|
|
60
|
+
dataKey: 'transportType',
|
|
61
|
+
options: [
|
|
62
|
+
{ label: 'HTTP', value: 'http' },
|
|
63
|
+
{ label: 'STDIO', value: 'stdio' },
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
]);
|
|
67
|
+
if (data.transportType === 'http') {
|
|
68
|
+
editors.push({
|
|
69
|
+
type: 'string',
|
|
70
|
+
label: 'Server URL',
|
|
71
|
+
dataKey: 'serverUrl',
|
|
72
|
+
useInputToggleDataKey: 'useServerUrlInput',
|
|
73
|
+
helperMessage: 'The endpoint URL for the MCP server to connect',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else if (data.transportType === 'stdio') {
|
|
77
|
+
const serverOptions = await getServerOptions(context);
|
|
78
|
+
editors.push({
|
|
79
|
+
type: 'dropdown',
|
|
80
|
+
label: 'Server ID',
|
|
81
|
+
dataKey: 'serverId',
|
|
82
|
+
helperMessage: getServerHelperMessage(context, serverOptions.length),
|
|
83
|
+
options: serverOptions,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return editors;
|
|
87
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derived from types here - https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/types.ts
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Errors
|
|
6
|
+
*/
|
|
7
|
+
export var MCPErrorType;
|
|
8
|
+
(function (MCPErrorType) {
|
|
9
|
+
MCPErrorType["CONFIG_NOT_FOUND"] = "CONFIG_NOT_FOUND";
|
|
10
|
+
MCPErrorType["SERVER_NOT_FOUND"] = "SERVER_NOT_FOUND";
|
|
11
|
+
MCPErrorType["SERVER_COMMUNICATION_FAILED"] = "SERVER_COMMUNICATION_FAILED";
|
|
12
|
+
MCPErrorType["INVALID_SCHEMA"] = "INVALID_SCHEMA";
|
|
13
|
+
})(MCPErrorType || (MCPErrorType = {}));
|
|
14
|
+
export class MCPError extends Error {
|
|
15
|
+
type;
|
|
16
|
+
details;
|
|
17
|
+
constructor(type, message, details) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.type = type;
|
|
20
|
+
this.details = details;
|
|
21
|
+
this.name = 'Error';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { MCPError, MCPErrorType } from './MCPProvider.js';
|
|
2
|
+
export const loadMCPConfiguration = async (context) => {
|
|
3
|
+
if (context.executor !== 'nodejs') {
|
|
4
|
+
throw new MCPError(MCPErrorType.CONFIG_NOT_FOUND, 'MCP config is not supported in browser environment');
|
|
5
|
+
}
|
|
6
|
+
const mcpConfig = context.project.metadata.mcpServer;
|
|
7
|
+
if (!mcpConfig || mcpConfig.mcpServers == null) {
|
|
8
|
+
throw new MCPError(MCPErrorType.CONFIG_NOT_FOUND, 'MCP configuration not defined in Project tab');
|
|
9
|
+
}
|
|
10
|
+
return mcpConfig;
|
|
11
|
+
};
|
|
12
|
+
export const getServerOptions = async (context) => {
|
|
13
|
+
if (context.executor === 'nodejs' && context.nativeApi) {
|
|
14
|
+
try {
|
|
15
|
+
const config = await loadMCPConfiguration(context);
|
|
16
|
+
return Object.entries(config.mcpServers)
|
|
17
|
+
.filter(([, config]) => !config.disabled)
|
|
18
|
+
.map(([id]) => ({
|
|
19
|
+
label: id,
|
|
20
|
+
value: id,
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
catch { }
|
|
24
|
+
}
|
|
25
|
+
return [];
|
|
26
|
+
};
|
|
27
|
+
export const getServerHelperMessage = (context, optionsLength) => {
|
|
28
|
+
if (optionsLength > 0)
|
|
29
|
+
return 'Select an MCP server from local configuration located in the Project tab';
|
|
30
|
+
if (context.executor !== 'nodejs')
|
|
31
|
+
return 'MCP nodes require Node Executor';
|
|
32
|
+
return 'No MCP servers found in config';
|
|
33
|
+
};
|
|
@@ -79,6 +79,7 @@ export class GraphProcessor {
|
|
|
79
79
|
// @ts-expect-error
|
|
80
80
|
#nodesNotInCycle = undefined;
|
|
81
81
|
#nodeAbortControllers = new Map();
|
|
82
|
+
#graphInputNodeValues = {};
|
|
82
83
|
/** User input nodes that are pending user input. */
|
|
83
84
|
#pendingUserInputs = undefined;
|
|
84
85
|
get isRunning() {
|
|
@@ -499,6 +500,7 @@ export class GraphProcessor {
|
|
|
499
500
|
this.#abortSuccessfully = false;
|
|
500
501
|
this.#nodeAbortControllers = new Map();
|
|
501
502
|
this.#loadedProjects = {};
|
|
503
|
+
this.#graphInputNodeValues = {};
|
|
502
504
|
}
|
|
503
505
|
/** Main function for running a graph. Runs a graph and returns the outputs from the output nodes of the graph. */
|
|
504
506
|
async processGraph(
|
|
@@ -1227,6 +1229,7 @@ export class GraphProcessor {
|
|
|
1227
1229
|
});
|
|
1228
1230
|
return results;
|
|
1229
1231
|
},
|
|
1232
|
+
graphInputNodeValues: this.#graphInputNodeValues,
|
|
1230
1233
|
};
|
|
1231
1234
|
await this.#waitUntilUnpaused();
|
|
1232
1235
|
const results = await instance.process(inputValues, context);
|
|
@@ -101,7 +101,6 @@ export class NodeRegistration {
|
|
|
101
101
|
if (!ImplClass) {
|
|
102
102
|
throw new Error(`Unknown node type: ${type}`);
|
|
103
103
|
}
|
|
104
|
-
// eslint-disable-next-line new-cap
|
|
105
104
|
const impl = new ImplClass.impl(node, ImplClass.pluginImpl);
|
|
106
105
|
if (!impl) {
|
|
107
106
|
throw new Error(`Unknown node type: ${type}`);
|
package/dist/esm/model/Nodes.js
CHANGED
|
@@ -162,6 +162,12 @@ import { toTreeNode } from './nodes/ToTreeNode.js';
|
|
|
162
162
|
export * from './nodes/ToTreeNode.js';
|
|
163
163
|
import { loopUntilNode } from './nodes/LoopUntilNode.js';
|
|
164
164
|
export * from './nodes/LoopUntilNode.js';
|
|
165
|
+
import { mcpDiscoveryNode } from './nodes/MCPDiscoveryNode.js';
|
|
166
|
+
export * from './nodes/MCPDiscoveryNode.js';
|
|
167
|
+
import { mcpToolCallNode } from './nodes/MCPToolCallNode.js';
|
|
168
|
+
export * from './nodes/MCPToolCallNode.js';
|
|
169
|
+
import { mcpGetPromptNode } from './nodes/MCPGetPromptNode.js';
|
|
170
|
+
export * from './nodes/MCPGetPromptNode.js';
|
|
165
171
|
import { referencedGraphAliasNode } from './nodes/ReferencedGraphAliasNode.js';
|
|
166
172
|
export * from './nodes/ReferencedGraphAliasNode.js';
|
|
167
173
|
export const registerBuiltInNodes = (registry) => {
|
|
@@ -247,6 +253,9 @@ export const registerBuiltInNodes = (registry) => {
|
|
|
247
253
|
.register(cronNode)
|
|
248
254
|
.register(toTreeNode)
|
|
249
255
|
.register(loopUntilNode)
|
|
256
|
+
.register(mcpDiscoveryNode)
|
|
257
|
+
.register(mcpToolCallNode)
|
|
258
|
+
.register(mcpGetPromptNode)
|
|
250
259
|
.register(referencedGraphAliasNode);
|
|
251
260
|
};
|
|
252
261
|
let globalRivetNodeRegistry = registerBuiltInNodes(new NodeRegistration());
|
|
@@ -755,7 +755,7 @@ export const ChatNodeBase = {
|
|
|
755
755
|
type: 'function',
|
|
756
756
|
}));
|
|
757
757
|
const { messages } = getChatNodeMessages(inputs);
|
|
758
|
-
const isReasoningModel = finalModel.startsWith('o1') || finalModel.startsWith('o3');
|
|
758
|
+
const isReasoningModel = finalModel.startsWith('o1') || finalModel.startsWith('o3') || finalModel.startsWith('o4');
|
|
759
759
|
const completionMessages = await Promise.all(messages.map((message) => chatMessageToOpenAIChatCompletionMessage(message, { isReasoningModel })));
|
|
760
760
|
let { maxTokens } = data;
|
|
761
761
|
const openaiModel = {
|
|
@@ -155,7 +155,7 @@ export class CodeNodeImpl extends NodeImpl {
|
|
|
155
155
|
includeRivet: this.data.allowRivet ?? false,
|
|
156
156
|
includeProcess: this.data.allowProcess ?? false,
|
|
157
157
|
includeConsole: this.data.allowConsole ?? false,
|
|
158
|
-
});
|
|
158
|
+
}, context.graphInputNodeValues, context.contextValues);
|
|
159
159
|
if (outputs == null || typeof outputs !== 'object' || ('then' in outputs && typeof outputs.then === 'function')) {
|
|
160
160
|
throw new Error('Code node must return an object with output values.');
|
|
161
161
|
}
|
|
@@ -36,7 +36,7 @@ export class GetAllDatasetsNodeImpl extends NodeImpl {
|
|
|
36
36
|
getEditors() {
|
|
37
37
|
return [];
|
|
38
38
|
}
|
|
39
|
-
async process(context) {
|
|
39
|
+
async process(_inputs, context) {
|
|
40
40
|
const { datasetProvider } = context;
|
|
41
41
|
if (datasetProvider == null) {
|
|
42
42
|
throw new Error('datasetProvider is required');
|
|
@@ -124,6 +124,8 @@ export class GraphInputNodeImpl extends NodeImpl {
|
|
|
124
124
|
type: this.data.dataType,
|
|
125
125
|
value: inputValue,
|
|
126
126
|
};
|
|
127
|
+
// Store the resolved value in the context for access by other nodes
|
|
128
|
+
context.graphInputNodeValues[this.data.id] = value;
|
|
127
129
|
return { ['data']: value };
|
|
128
130
|
}
|
|
129
131
|
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { nanoid } from 'nanoid';
|
|
2
|
+
import { NodeImpl } from '../NodeImpl.js';
|
|
3
|
+
import {} from '../../index.js';
|
|
4
|
+
import { MCPError, MCPErrorType } from '../../integrations/mcp/MCPProvider.js';
|
|
5
|
+
import { dedent, getInputOrData } from '../../utils/index.js';
|
|
6
|
+
import { nodeDefinition } from '../NodeDefinition.js';
|
|
7
|
+
import { getServerHelperMessage, getServerOptions, loadMCPConfiguration } from '../../integrations/mcp/MCPUtils.js';
|
|
8
|
+
import { getMCPBaseInputs } from '../../integrations/mcp/MCPBase.js';
|
|
9
|
+
class MCPDiscoveryNodeImpl extends NodeImpl {
|
|
10
|
+
static create() {
|
|
11
|
+
const chartNode = {
|
|
12
|
+
type: 'mcpDiscovery',
|
|
13
|
+
title: 'MCP Discovery',
|
|
14
|
+
id: nanoid(),
|
|
15
|
+
visualData: {
|
|
16
|
+
x: 0,
|
|
17
|
+
y: 0,
|
|
18
|
+
width: 250,
|
|
19
|
+
},
|
|
20
|
+
data: {
|
|
21
|
+
name: 'mcp-client',
|
|
22
|
+
version: '1.0.0',
|
|
23
|
+
transportType: 'stdio',
|
|
24
|
+
serverUrl: 'http://localhost:8080/mcp',
|
|
25
|
+
serverId: '',
|
|
26
|
+
useNameInput: false,
|
|
27
|
+
useVersionInput: false,
|
|
28
|
+
useToolsOutput: true,
|
|
29
|
+
usePromptsOutput: true,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
return chartNode;
|
|
33
|
+
}
|
|
34
|
+
getInputDefinitions() {
|
|
35
|
+
const inputs = getMCPBaseInputs(this.data);
|
|
36
|
+
return inputs;
|
|
37
|
+
}
|
|
38
|
+
getOutputDefinitions() {
|
|
39
|
+
const outputDefinitions = [];
|
|
40
|
+
if (this.data.useToolsOutput) {
|
|
41
|
+
outputDefinitions.push({
|
|
42
|
+
id: 'tools',
|
|
43
|
+
title: 'Tools',
|
|
44
|
+
dataType: 'object[]',
|
|
45
|
+
description: 'Tools returned from the MCP server',
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (this.data.usePromptsOutput) {
|
|
49
|
+
outputDefinitions.push({
|
|
50
|
+
id: 'prompts',
|
|
51
|
+
title: 'Prompts',
|
|
52
|
+
dataType: 'object[]',
|
|
53
|
+
description: 'Prompts returned from the MCP server',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return outputDefinitions;
|
|
57
|
+
}
|
|
58
|
+
async getEditors(context) {
|
|
59
|
+
const editors = [
|
|
60
|
+
{
|
|
61
|
+
type: 'toggle',
|
|
62
|
+
label: 'Output Tools',
|
|
63
|
+
dataKey: 'useToolsOutput',
|
|
64
|
+
helperMessage: 'Toggle on if you want to get a Tools output',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
type: 'toggle',
|
|
68
|
+
label: 'Output Prompts',
|
|
69
|
+
dataKey: 'usePromptsOutput',
|
|
70
|
+
helperMessage: 'Toggle on if you want to get a Prompts output',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
type: 'string',
|
|
74
|
+
label: 'Name',
|
|
75
|
+
dataKey: 'name',
|
|
76
|
+
useInputToggleDataKey: 'useNameInput',
|
|
77
|
+
helperMessage: 'The name for the MCP Client',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
type: 'string',
|
|
81
|
+
label: 'Version',
|
|
82
|
+
dataKey: 'version',
|
|
83
|
+
useInputToggleDataKey: 'useVersionInput',
|
|
84
|
+
helperMessage: 'A version for the MCP Client',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
type: 'dropdown',
|
|
88
|
+
label: 'Transport Type',
|
|
89
|
+
dataKey: 'transportType',
|
|
90
|
+
options: [
|
|
91
|
+
{ label: 'HTTP', value: 'http' },
|
|
92
|
+
{ label: 'STDIO', value: 'stdio' },
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
if (this.data.transportType === 'http') {
|
|
97
|
+
editors.push({
|
|
98
|
+
type: 'string',
|
|
99
|
+
label: 'Server URL',
|
|
100
|
+
dataKey: 'serverUrl',
|
|
101
|
+
useInputToggleDataKey: 'useServerUrlInput',
|
|
102
|
+
helperMessage: 'The base URL endpoint for the MCP server with `/mcp`',
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
else if (this.data.transportType === 'stdio') {
|
|
106
|
+
const serverOptions = await getServerOptions(context);
|
|
107
|
+
editors.push({
|
|
108
|
+
type: 'dropdown',
|
|
109
|
+
label: 'Server ID',
|
|
110
|
+
dataKey: 'serverId',
|
|
111
|
+
helperMessage: getServerHelperMessage(context, serverOptions.length),
|
|
112
|
+
options: serverOptions,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return editors;
|
|
116
|
+
}
|
|
117
|
+
getBody(context) {
|
|
118
|
+
let base;
|
|
119
|
+
if (this.data.transportType === 'http') {
|
|
120
|
+
base = this.data.useServerUrlInput ? '(Using Server URL Input)' : this.data.serverUrl;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
base = `Server ID: ${this.data.serverId || '(None)'}`;
|
|
124
|
+
}
|
|
125
|
+
const namePart = `Name: ${this.data.name}`;
|
|
126
|
+
const versionPart = `Version: ${this.data.version}`;
|
|
127
|
+
const parts = [namePart, versionPart, base];
|
|
128
|
+
if (context.executor !== 'nodejs') {
|
|
129
|
+
parts.push('(Requires Node Executor)');
|
|
130
|
+
}
|
|
131
|
+
return parts.join('\n');
|
|
132
|
+
}
|
|
133
|
+
static getUIData() {
|
|
134
|
+
return {
|
|
135
|
+
infoBoxBody: dedent `
|
|
136
|
+
Connects to an MCP (Model Context Protocol) server to discover capabilities like tools and prompts.
|
|
137
|
+
`,
|
|
138
|
+
infoBoxTitle: 'MCP Discovery Node',
|
|
139
|
+
contextMenuTitle: 'MCP Discovery',
|
|
140
|
+
group: ['MCP'],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
async process(inputs, context) {
|
|
144
|
+
const name = getInputOrData(this.data, inputs, 'name', 'string');
|
|
145
|
+
const version = getInputOrData(this.data, inputs, 'version', 'string');
|
|
146
|
+
const transportType = getInputOrData(this.data, inputs, 'transportType', 'string');
|
|
147
|
+
let tools = [];
|
|
148
|
+
let prompts = [];
|
|
149
|
+
try {
|
|
150
|
+
if (!context.mcpProvider) {
|
|
151
|
+
throw new Error('MCP Provider not found');
|
|
152
|
+
}
|
|
153
|
+
if (transportType === 'http') {
|
|
154
|
+
const serverUrl = getInputOrData(this.data, inputs, 'serverUrl', 'string');
|
|
155
|
+
if (!serverUrl || serverUrl === '') {
|
|
156
|
+
throw new MCPError(MCPErrorType.SERVER_NOT_FOUND, 'No server URL was provided');
|
|
157
|
+
}
|
|
158
|
+
if (!serverUrl.includes('/mcp')) {
|
|
159
|
+
throw new MCPError(MCPErrorType.SERVER_COMMUNICATION_FAILED, 'Include /mcp in your server URL. For example: http://localhost:8080/mcp');
|
|
160
|
+
}
|
|
161
|
+
tools = await context.mcpProvider.getHTTPTools({ name, version }, serverUrl);
|
|
162
|
+
prompts = await context.mcpProvider.getHTTPrompts({ name, version }, serverUrl);
|
|
163
|
+
}
|
|
164
|
+
else if (transportType === 'stdio') {
|
|
165
|
+
const serverId = this.data.serverId ?? '';
|
|
166
|
+
const mcpConfig = await loadMCPConfiguration(context);
|
|
167
|
+
if (!mcpConfig.mcpServers[serverId]) {
|
|
168
|
+
throw new MCPError(MCPErrorType.SERVER_NOT_FOUND, `Server ${serverId} not found in MCP config`);
|
|
169
|
+
}
|
|
170
|
+
const serverConfig = {
|
|
171
|
+
config: mcpConfig.mcpServers[serverId],
|
|
172
|
+
serverId,
|
|
173
|
+
};
|
|
174
|
+
tools = await context.mcpProvider.getStdioTools({ name, version }, serverConfig);
|
|
175
|
+
prompts = await context.mcpProvider.getStdioPrompts({ name, version }, serverConfig);
|
|
176
|
+
}
|
|
177
|
+
const output = {};
|
|
178
|
+
const gptFunctions = tools.map((tool) => ({
|
|
179
|
+
name: tool.name,
|
|
180
|
+
description: tool.description ?? '',
|
|
181
|
+
parameters: tool.inputSchema,
|
|
182
|
+
strict: false,
|
|
183
|
+
}));
|
|
184
|
+
if (this.data.useToolsOutput) {
|
|
185
|
+
output['tools'] = {
|
|
186
|
+
type: 'gpt-function[]',
|
|
187
|
+
value: gptFunctions,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (this.data.usePromptsOutput) {
|
|
191
|
+
output['prompts'] = {
|
|
192
|
+
type: 'object[]',
|
|
193
|
+
value: prompts.map((prompt) => ({
|
|
194
|
+
name: prompt.name,
|
|
195
|
+
description: prompt.description,
|
|
196
|
+
arguments: prompt.arugments,
|
|
197
|
+
})),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
return output;
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
if (context.executor === 'browser') {
|
|
204
|
+
throw new Error('Failed to create Client without Node Executor');
|
|
205
|
+
}
|
|
206
|
+
throw err;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
export const mcpDiscoveryNode = nodeDefinition(MCPDiscoveryNodeImpl, 'MCP Discovery');
|