@alpic80/rivet-core 1.24.0-aidon.5 → 1.24.2-aidon.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +9 -6
  2. package/dist/cjs/bundle.cjs +1400 -263
  3. package/dist/cjs/bundle.cjs.map +4 -4
  4. package/dist/esm/api/createProcessor.js +2 -0
  5. package/dist/esm/api/streaming.js +44 -2
  6. package/dist/esm/exports.js +1 -0
  7. package/dist/esm/integrations/CodeRunner.js +10 -2
  8. package/dist/esm/integrations/mcp/MCPBase.js +87 -0
  9. package/dist/esm/integrations/mcp/MCPProvider.js +23 -0
  10. package/dist/esm/integrations/mcp/MCPUtils.js +33 -0
  11. package/dist/esm/model/GraphProcessor.js +3 -0
  12. package/dist/esm/model/NodeRegistration.js +0 -1
  13. package/dist/esm/model/Nodes.js +9 -0
  14. package/dist/esm/model/nodes/ChatNodeBase.js +1 -1
  15. package/dist/esm/model/nodes/CodeNode.js +1 -1
  16. package/dist/esm/model/nodes/GetAllDatasetsNode.js +1 -1
  17. package/dist/esm/model/nodes/GraphInputNode.js +2 -0
  18. package/dist/esm/model/nodes/MCPDiscoveryNode.js +210 -0
  19. package/dist/esm/model/nodes/MCPGetPromptNode.js +233 -0
  20. package/dist/esm/model/nodes/MCPToolCallNode.js +261 -0
  21. package/dist/esm/model/nodes/ObjectNode.js +42 -21
  22. package/dist/esm/model/nodes/PromptNode.js +1 -1
  23. package/dist/esm/model/nodes/TextNode.js +13 -2
  24. package/dist/esm/plugins/anthropic/anthropic.js +22 -3
  25. package/dist/esm/plugins/anthropic/nodes/ChatAnthropicNode.js +33 -3
  26. package/dist/esm/plugins/google/google.js +29 -14
  27. package/dist/esm/plugins/google/nodes/ChatGoogleNode.js +70 -5
  28. package/dist/esm/utils/interpolation.js +155 -17
  29. package/dist/esm/utils/openai.js +24 -0
  30. package/dist/types/api/createProcessor.d.ts +3 -2
  31. package/dist/types/api/streaming.d.ts +8 -1
  32. package/dist/types/exports.d.ts +1 -0
  33. package/dist/types/integrations/CodeRunner.d.ts +4 -3
  34. package/dist/types/integrations/mcp/MCPBase.d.ts +18 -0
  35. package/dist/types/integrations/mcp/MCPProvider.d.ts +153 -0
  36. package/dist/types/integrations/mcp/MCPUtils.d.ts +9 -0
  37. package/dist/types/model/GraphProcessor.d.ts +1 -1
  38. package/dist/types/model/Nodes.d.ts +13 -2
  39. package/dist/types/model/ProcessContext.d.ts +5 -1
  40. package/dist/types/model/Project.d.ts +2 -0
  41. package/dist/types/model/nodes/GetAllDatasetsNode.d.ts +2 -2
  42. package/dist/types/model/nodes/MCPDiscoveryNode.d.ts +9 -0
  43. package/dist/types/model/nodes/MCPGetPromptNode.d.ts +23 -0
  44. package/dist/types/model/nodes/MCPToolCallNode.d.ts +26 -0
  45. package/dist/types/model/nodes/ObjectNode.d.ts +3 -2
  46. package/dist/types/model/nodes/TextNode.d.ts +2 -1
  47. package/dist/types/plugins/anthropic/anthropic.d.ts +21 -3
  48. package/dist/types/plugins/anthropic/nodes/ChatAnthropicNode.d.ts +5 -0
  49. package/dist/types/plugins/google/google.d.ts +12 -2
  50. package/dist/types/plugins/google/nodes/ChatGoogleNode.d.ts +7 -0
  51. package/dist/types/utils/interpolation.d.ts +6 -1
  52. package/dist/types/utils/openai.d.ts +24 -0
  53. 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,
@@ -9,12 +9,14 @@ function nodeMatches(spec, event) {
9
9
  export async function* getProcessorEvents(processor, spec) {
10
10
  const previousIndexes = new Map();
11
11
  const usages = [];
12
+ let hasDelta = false;
12
13
  for await (const event of processor.events()) {
13
14
  if (event.type === 'partialOutput') {
14
15
  if (spec.partialOutputs === true ||
15
16
  nodeMatches(spec.partialOutputs, event)) {
16
17
  const currentOutput = coerceType(event.outputs['response'], 'string');
17
18
  const delta = currentOutput.slice(previousIndexes.get(event.node.id) ?? 0);
19
+ hasDelta = true;
18
20
  yield {
19
21
  type: 'partialOutput',
20
22
  nodeId: event.node.id,
@@ -76,8 +78,8 @@ export async function* getProcessorEvents(processor, spec) {
76
78
  usages.push(usage);
77
79
  }
78
80
  }
79
- if (spec.nodeFinish === true ||
80
- nodeMatches(spec.nodeFinish, event)) {
81
+ if ((spec.nodeFinish === true || nodeMatches(spec.nodeFinish, event)) &&
82
+ !(spec.removeFinalOutput && hasDelta)) {
81
83
  yield {
82
84
  type: 'nodeFinish',
83
85
  outputs: event.outputs,
@@ -88,6 +90,21 @@ export async function* getProcessorEvents(processor, spec) {
88
90
  }
89
91
  }
90
92
  }
93
+ export const createOnStreamUserEvents = (eventList, handleUserEvent) => {
94
+ if (!eventList?.trim()) {
95
+ return undefined;
96
+ }
97
+ const events = eventList.split(',').map(e => e.trim()).filter(Boolean);
98
+ if (!events.length) {
99
+ return undefined;
100
+ }
101
+ return Object.fromEntries(events.map(event => [
102
+ event,
103
+ async (data) => {
104
+ await handleUserEvent(event, data);
105
+ }
106
+ ]));
107
+ };
91
108
  /**
92
109
  * Creates a ReadableStream for processor events, following the Server-Sent Events protocol.
93
110
  * https://developer.mozilla.org/en-US/docs/Web/API/EventSource
@@ -105,6 +122,18 @@ spec) {
105
122
  }
106
123
  return new ReadableStream({
107
124
  async start(controller) {
125
+ const userEventHandler = async (eventName, data) => {
126
+ sendEvent(controller, 'event', {
127
+ name: eventName,
128
+ message: coerceType(data, 'string')
129
+ });
130
+ };
131
+ const streamEvents = createOnStreamUserEvents(spec.userStreamEvents, userEventHandler);
132
+ if (streamEvents) {
133
+ for (const [name, fn] of Object.entries(streamEvents)) {
134
+ processor.onUserEvent(name, fn);
135
+ }
136
+ }
108
137
  try {
109
138
  for await (const event of getProcessorEvents(processor, spec)) {
110
139
  sendEvent(controller, event.type, event);
@@ -132,6 +161,19 @@ export function getSingleNodeStream(processor, arg) {
132
161
  return new ReadableStream({
133
162
  async start(controller) {
134
163
  try {
164
+ const userEventHandler = async (eventName, data) => {
165
+ const payload = {
166
+ name: eventName,
167
+ message: coerceType(data, 'string')
168
+ };
169
+ controller.enqueue(`event: ${JSON.stringify(payload)}\n\n`);
170
+ };
171
+ const streamEvents = createOnStreamUserEvents(spec.userStreamEvents, userEventHandler);
172
+ if (streamEvents) {
173
+ for (const [name, fn] of Object.entries(streamEvents)) {
174
+ processor.onUserEvent(name, fn);
175
+ }
176
+ }
135
177
  for await (const event of getProcessorEvents(processor, spec)) {
136
178
  if (event.type === 'partialOutput') { //nodeIdOrTitle filter managed by spec
137
179
  controller.enqueue(`data: ${JSON.stringify(event.delta)}\n\n`);
@@ -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}`);
@@ -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');