n8n-nodes-browser-smart-automation 0.1.2 → 0.1.5
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/dist/McpClientTool/McpClientTool.node.js +2 -13
- package/dist/McpClientTool/McpClientTool.node.js.map +1 -1
- package/dist/McpClientTool/utils.js +1 -1
- package/dist/McpClientTool/utils.js.map +1 -1
- package/dist/McpTrigger/McpTrigger.node.js +1 -1
- package/dist/McpTrigger/McpTrigger.node.js.map +1 -1
- package/dist/shared/N8nBinaryLoader.js +203 -0
- package/dist/shared/N8nBinaryLoader.js.map +1 -0
- package/dist/shared/N8nJsonLoader.js +89 -0
- package/dist/shared/N8nJsonLoader.js.map +1 -0
- package/dist/shared/N8nTool.js +106 -0
- package/dist/shared/N8nTool.js.map +1 -0
- package/dist/shared/embeddingInputValidation.js +55 -0
- package/dist/shared/embeddingInputValidation.js.map +1 -0
- package/dist/shared/helpers.js +220 -13
- package/dist/shared/helpers.js.map +1 -1
- package/dist/shared/httpProxyAgent.js +40 -2
- package/dist/shared/httpProxyAgent.js.map +1 -1
- package/dist/shared/logWrapper.js +347 -2
- package/dist/shared/logWrapper.js.map +1 -1
- package/dist/shared/schemaParsing.js +47 -4
- package/dist/shared/schemaParsing.js.map +1 -1
- package/dist/shared/sharedFields.js +142 -7
- package/dist/shared/sharedFields.js.map +1 -1
- package/dist/shared/typesN8nTool.js +17 -0
- package/dist/shared/typesN8nTool.js.map +1 -0
- package/dist/shared/utils.js +1 -1
- package/dist/shared/utils.js.map +1 -1
- package/package.json +25 -7
- package/jest.config.js +0 -24
- package/nodes/McpClient/McpClient.node.ts +0 -327
- package/nodes/McpClient/__test__/McpClient.node.test.ts +0 -221
- package/nodes/McpClient/__test__/utils.test.ts +0 -302
- package/nodes/McpClient/listSearch.ts +0 -48
- package/nodes/McpClient/resourceMapping.ts +0 -48
- package/nodes/McpClient/utils.ts +0 -281
- package/nodes/McpClientTool/McpClientTool.node.ts +0 -468
- package/nodes/McpClientTool/__test__/McpClientTool.node.test.ts +0 -730
- package/nodes/McpClientTool/loadOptions.ts +0 -45
- package/nodes/McpClientTool/types.ts +0 -1
- package/nodes/McpClientTool/utils.ts +0 -116
- package/nodes/McpTrigger/FlushingTransport.ts +0 -61
- package/nodes/McpTrigger/McpServer.ts +0 -317
- package/nodes/McpTrigger/McpTrigger.node.ts +0 -204
- package/nodes/McpTrigger/__test__/FlushingTransport.test.ts +0 -102
- package/nodes/McpTrigger/__test__/McpServer.test.ts +0 -532
- package/nodes/McpTrigger/__test__/McpTrigger.node.test.ts +0 -171
- package/nodes/shared/__test__/utils.test.ts +0 -318
- package/nodes/shared/descriptions.ts +0 -65
- package/nodes/shared/helpers.ts +0 -31
- package/nodes/shared/httpProxyAgent.ts +0 -11
- package/nodes/shared/logWrapper.ts +0 -13
- package/nodes/shared/schemaParsing.ts +0 -9
- package/nodes/shared/sharedFields.ts +0 -20
- package/nodes/shared/types.ts +0 -12
- package/nodes/shared/utils.ts +0 -296
- package/officail/package.json +0 -255
- package/tsconfig.json +0 -32
- package/tsup.config.ts +0 -16
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
-
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
3
|
-
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
4
|
-
import { mockDeep } from 'jest-mock-extended';
|
|
5
|
-
import type { IExecuteFunctions } from 'n8n-workflow';
|
|
6
|
-
|
|
7
|
-
import type { McpAuthenticationOption, McpServerTransport } from '../types';
|
|
8
|
-
import { connectMcpClient, getAuthHeaders, tryRefreshOAuth2Token } from '../utils';
|
|
9
|
-
|
|
10
|
-
jest.mock('@modelcontextprotocol/sdk/client/index.js');
|
|
11
|
-
jest.mock('@modelcontextprotocol/sdk/client/streamableHttp.js');
|
|
12
|
-
jest.mock('@modelcontextprotocol/sdk/client/sse.js');
|
|
13
|
-
|
|
14
|
-
const MockedClient = Client as jest.MockedClass<typeof Client>;
|
|
15
|
-
const MockedHTTPTransport = StreamableHTTPClientTransport as jest.MockedClass<
|
|
16
|
-
typeof StreamableHTTPClientTransport
|
|
17
|
-
>;
|
|
18
|
-
const MockedSSETransport = SSEClientTransport as jest.MockedClass<typeof SSEClientTransport>;
|
|
19
|
-
|
|
20
|
-
describe('utils', () => {
|
|
21
|
-
describe('tryRefreshOAuth2Token', () => {
|
|
22
|
-
it('should refresh an OAuth2 token without headers', async () => {
|
|
23
|
-
const ctx = mockDeep<IExecuteFunctions>();
|
|
24
|
-
ctx.helpers.refreshOAuth2Token.mockResolvedValue({
|
|
25
|
-
access_token: 'new-access-token',
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const headers = await tryRefreshOAuth2Token(ctx, 'mcpOAuth2Api');
|
|
29
|
-
|
|
30
|
-
expect(headers).toEqual({ Authorization: 'Bearer new-access-token' });
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should refresh an OAuth2 token with headers', async () => {
|
|
34
|
-
const ctx = mockDeep<IExecuteFunctions>();
|
|
35
|
-
ctx.helpers.refreshOAuth2Token.mockResolvedValue({
|
|
36
|
-
access_token: 'new-access-token',
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const headers = await tryRefreshOAuth2Token(ctx, 'mcpOAuth2Api', {
|
|
40
|
-
Foo: 'bar',
|
|
41
|
-
Authorization: 'Bearer old-access-token',
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
expect(headers).toEqual({
|
|
45
|
-
Foo: 'bar',
|
|
46
|
-
Authorization: 'Bearer new-access-token',
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('should return null if the authentication method is not oAuth2Api', async () => {
|
|
51
|
-
const ctx = mockDeep<IExecuteFunctions>();
|
|
52
|
-
|
|
53
|
-
const headers = await tryRefreshOAuth2Token(ctx, 'headerAuth');
|
|
54
|
-
|
|
55
|
-
expect(headers).toBeNull();
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('should return null if the refreshOAuth2Token returns no access_token', async () => {
|
|
59
|
-
const ctx = mockDeep<IExecuteFunctions>();
|
|
60
|
-
ctx.helpers.refreshOAuth2Token.mockResolvedValue({
|
|
61
|
-
access_token: null,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
const headers = await tryRefreshOAuth2Token(ctx, 'mcpOAuth2Api');
|
|
65
|
-
|
|
66
|
-
expect(headers).toBeNull();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('should return null if the refreshOAuth2Token throws an error', async () => {
|
|
70
|
-
const ctx = mockDeep<IExecuteFunctions>();
|
|
71
|
-
ctx.helpers.refreshOAuth2Token.mockRejectedValue(new Error('Failed to refresh OAuth2 token'));
|
|
72
|
-
|
|
73
|
-
const headers = await tryRefreshOAuth2Token(ctx, 'mcpOAuth2Api');
|
|
74
|
-
|
|
75
|
-
expect(headers).toBeNull();
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
describe('getAuthHeaders', () => {
|
|
80
|
-
it('should return the headers for mcpOAuth2Api', async () => {
|
|
81
|
-
const ctx = mockDeep<IExecuteFunctions>();
|
|
82
|
-
ctx.getCredentials.mockResolvedValue({
|
|
83
|
-
oauthTokenData: {
|
|
84
|
-
access_token: 'access-token',
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const result = await getAuthHeaders(ctx, 'mcpOAuth2Api');
|
|
89
|
-
|
|
90
|
-
expect(result).toEqual({ headers: { Authorization: 'Bearer access-token' } });
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('should return the headers for headerAuth', async () => {
|
|
94
|
-
const ctx = mockDeep<IExecuteFunctions>();
|
|
95
|
-
ctx.getCredentials.mockResolvedValue({
|
|
96
|
-
name: 'Foo',
|
|
97
|
-
value: 'bar',
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const result = await getAuthHeaders(ctx, 'headerAuth');
|
|
101
|
-
|
|
102
|
-
expect(result).toEqual({ headers: { Foo: 'bar' } });
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('should return the headers for bearerAuth', async () => {
|
|
106
|
-
const ctx = mockDeep<IExecuteFunctions>();
|
|
107
|
-
ctx.getCredentials.mockResolvedValue({
|
|
108
|
-
token: 'access-token',
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
const result = await getAuthHeaders(ctx, 'bearerAuth');
|
|
112
|
-
|
|
113
|
-
expect(result).toEqual({ headers: { Authorization: 'Bearer access-token' } });
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('should return the headers for multipleHeadersAuth', async () => {
|
|
117
|
-
const ctx = mockDeep<IExecuteFunctions>();
|
|
118
|
-
ctx.getCredentials.mockResolvedValue({
|
|
119
|
-
headers: {
|
|
120
|
-
values: [
|
|
121
|
-
{ name: 'Foo', value: 'bar' },
|
|
122
|
-
{ name: 'Test', value: '123' },
|
|
123
|
-
],
|
|
124
|
-
},
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
const result = await getAuthHeaders(ctx, 'multipleHeadersAuth');
|
|
128
|
-
|
|
129
|
-
expect(result).toEqual({ headers: { Foo: 'bar', Test: '123' } });
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('should return an empty object for none', async () => {
|
|
133
|
-
const ctx = mockDeep<IExecuteFunctions>();
|
|
134
|
-
|
|
135
|
-
const result = await getAuthHeaders(ctx, 'none');
|
|
136
|
-
|
|
137
|
-
expect(result).toEqual({});
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('should return an empty object for an unknown authentication method', async () => {
|
|
141
|
-
const ctx = mockDeep<IExecuteFunctions>();
|
|
142
|
-
|
|
143
|
-
const result = await getAuthHeaders(ctx, 'unknown' as McpAuthenticationOption);
|
|
144
|
-
|
|
145
|
-
expect(result).toEqual({});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it.each([
|
|
149
|
-
'headerAuth',
|
|
150
|
-
'bearerAuth',
|
|
151
|
-
'mcpOAuth2Api',
|
|
152
|
-
'multipleHeadersAuth',
|
|
153
|
-
] as McpAuthenticationOption[])(
|
|
154
|
-
'should return an empty object for %s when it fails',
|
|
155
|
-
async (authentication) => {
|
|
156
|
-
const ctx = mockDeep<IExecuteFunctions>();
|
|
157
|
-
ctx.getCredentials.mockRejectedValue(new Error('Failed to get credentials'));
|
|
158
|
-
|
|
159
|
-
const result = await getAuthHeaders(ctx, authentication);
|
|
160
|
-
|
|
161
|
-
expect(result).toEqual({});
|
|
162
|
-
},
|
|
163
|
-
);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
describe('connectMcpClient', () => {
|
|
167
|
-
const mockClient = {
|
|
168
|
-
connect: jest.fn(),
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
beforeEach(() => {
|
|
172
|
-
jest.resetAllMocks();
|
|
173
|
-
const mockHttpTransport = {} as unknown as StreamableHTTPClientTransport;
|
|
174
|
-
const mockSSETransport = {} as unknown as SSEClientTransport;
|
|
175
|
-
MockedClient.mockImplementation(() => mockClient as unknown as Client);
|
|
176
|
-
MockedHTTPTransport.mockImplementation(() => mockHttpTransport);
|
|
177
|
-
MockedSSETransport.mockImplementation(() => mockSSETransport);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
describe.each([
|
|
181
|
-
['httpStreamable', StreamableHTTPClientTransport],
|
|
182
|
-
['sse', SSEClientTransport],
|
|
183
|
-
] as Array<
|
|
184
|
-
[McpServerTransport, typeof StreamableHTTPClientTransport | typeof SSEClientTransport]
|
|
185
|
-
>)('%s transport', (transport, Transport) => {
|
|
186
|
-
it('should retry on 401 and succeed', async () => {
|
|
187
|
-
const unauthorizedError = new Error('Request failed with status 401');
|
|
188
|
-
const onUnauthorized = jest.fn().mockResolvedValue({ Authorization: 'Bearer new-token' });
|
|
189
|
-
mockClient.connect
|
|
190
|
-
.mockRejectedValueOnce(unauthorizedError)
|
|
191
|
-
.mockResolvedValueOnce(undefined);
|
|
192
|
-
|
|
193
|
-
const result = await connectMcpClient({
|
|
194
|
-
serverTransport: transport,
|
|
195
|
-
endpointUrl: 'https://example.com',
|
|
196
|
-
headers: { Authorization: 'Bearer old-token' },
|
|
197
|
-
name: 'test-client',
|
|
198
|
-
version: 1,
|
|
199
|
-
onUnauthorized,
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
expect(result.ok).toBe(true);
|
|
203
|
-
expect(mockClient.connect).toHaveBeenCalledTimes(2);
|
|
204
|
-
expect(onUnauthorized).toHaveBeenCalledWith({ Authorization: 'Bearer old-token' });
|
|
205
|
-
expect(Transport).toHaveBeenCalledTimes(2);
|
|
206
|
-
expect(Transport).toHaveBeenNthCalledWith(
|
|
207
|
-
1,
|
|
208
|
-
expect.any(URL),
|
|
209
|
-
expect.objectContaining({
|
|
210
|
-
requestInit: expect.objectContaining({
|
|
211
|
-
headers: expect.objectContaining({
|
|
212
|
-
Authorization: 'Bearer old-token',
|
|
213
|
-
}),
|
|
214
|
-
}),
|
|
215
|
-
}),
|
|
216
|
-
);
|
|
217
|
-
expect(Transport).toHaveBeenNthCalledWith(
|
|
218
|
-
2,
|
|
219
|
-
expect.any(URL),
|
|
220
|
-
expect.objectContaining({
|
|
221
|
-
requestInit: expect.objectContaining({
|
|
222
|
-
headers: expect.objectContaining({
|
|
223
|
-
Authorization: 'Bearer new-token',
|
|
224
|
-
}),
|
|
225
|
-
}),
|
|
226
|
-
}),
|
|
227
|
-
);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it('should not retry on not 401', async () => {
|
|
231
|
-
const error = new Error('Internal Server Error');
|
|
232
|
-
mockClient.connect.mockRejectedValueOnce(error);
|
|
233
|
-
|
|
234
|
-
const result = await connectMcpClient({
|
|
235
|
-
serverTransport: transport,
|
|
236
|
-
endpointUrl: 'https://example.com',
|
|
237
|
-
headers: { Authorization: 'Bearer old-token' },
|
|
238
|
-
name: 'test-client',
|
|
239
|
-
version: 1,
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
expect(result.ok).toBe(false);
|
|
243
|
-
if (!result.ok) {
|
|
244
|
-
expect(result.error.type).toBe('connection');
|
|
245
|
-
}
|
|
246
|
-
expect(mockClient.connect).toHaveBeenCalledTimes(1);
|
|
247
|
-
expect(Transport).toHaveBeenCalledTimes(1);
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it('should not retry when onUnauthorized is not provided', async () => {
|
|
251
|
-
const error = new Error('Request failed with status 401');
|
|
252
|
-
mockClient.connect.mockRejectedValueOnce(error);
|
|
253
|
-
|
|
254
|
-
const result = await connectMcpClient({
|
|
255
|
-
serverTransport: transport,
|
|
256
|
-
endpointUrl: 'https://example.com',
|
|
257
|
-
headers: { Authorization: 'Bearer old-token' },
|
|
258
|
-
name: 'test-client',
|
|
259
|
-
version: 1,
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
expect(result.ok).toBe(false);
|
|
263
|
-
if (!result.ok) {
|
|
264
|
-
expect(result.error.type).toBe('auth');
|
|
265
|
-
}
|
|
266
|
-
expect(mockClient.connect).toHaveBeenCalledTimes(1);
|
|
267
|
-
expect(Transport).toHaveBeenCalledTimes(1);
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it('should not retry when onUnauthorized returns null', async () => {
|
|
271
|
-
const error = new Error('Request failed with status 401');
|
|
272
|
-
const onUnauthorized = jest.fn().mockResolvedValue(null);
|
|
273
|
-
mockClient.connect.mockRejectedValueOnce(error);
|
|
274
|
-
|
|
275
|
-
const result = await connectMcpClient({
|
|
276
|
-
serverTransport: transport,
|
|
277
|
-
endpointUrl: 'https://example.com',
|
|
278
|
-
headers: { Authorization: 'Bearer old-token' },
|
|
279
|
-
name: 'test-client',
|
|
280
|
-
version: 1,
|
|
281
|
-
onUnauthorized,
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
expect(result.ok).toBe(false);
|
|
285
|
-
if (!result.ok) {
|
|
286
|
-
expect(result.error.type).toBe('auth');
|
|
287
|
-
}
|
|
288
|
-
expect(mockClient.connect).toHaveBeenCalledTimes(1);
|
|
289
|
-
expect(Transport).toHaveBeenCalledTimes(1);
|
|
290
|
-
expect(onUnauthorized).toHaveBeenCalledWith({ Authorization: 'Bearer old-token' });
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
it('should not retry more than once', async () => {
|
|
294
|
-
const error = new Error('Request failed with status 401');
|
|
295
|
-
mockClient.connect.mockRejectedValue(error);
|
|
296
|
-
const onUnauthorized = jest.fn().mockResolvedValue({ Authorization: 'Bearer new-token' });
|
|
297
|
-
|
|
298
|
-
const result = await connectMcpClient({
|
|
299
|
-
serverTransport: transport,
|
|
300
|
-
endpointUrl: 'https://example.com',
|
|
301
|
-
headers: { Authorization: 'Bearer old-token' },
|
|
302
|
-
name: 'test-client',
|
|
303
|
-
version: 1,
|
|
304
|
-
onUnauthorized,
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
expect(result.ok).toBe(false);
|
|
308
|
-
if (!result.ok) {
|
|
309
|
-
expect(result.error.type).toBe('auth');
|
|
310
|
-
}
|
|
311
|
-
expect(mockClient.connect).toHaveBeenCalledTimes(2);
|
|
312
|
-
expect(Transport).toHaveBeenCalledTimes(2);
|
|
313
|
-
expect(onUnauthorized).toHaveBeenCalledTimes(1);
|
|
314
|
-
expect(onUnauthorized).toHaveBeenCalledWith({ Authorization: 'Bearer old-token' });
|
|
315
|
-
});
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
});
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import type { IDisplayOptions, INodeCredentialDescription, INodeProperties } from 'n8n-workflow';
|
|
2
|
-
|
|
3
|
-
export const transportSelect = ({
|
|
4
|
-
defaultOption,
|
|
5
|
-
displayOptions,
|
|
6
|
-
}: {
|
|
7
|
-
defaultOption: 'sse' | 'httpStreamable';
|
|
8
|
-
displayOptions?: IDisplayOptions;
|
|
9
|
-
}): INodeProperties => ({
|
|
10
|
-
displayName: 'Server Transport',
|
|
11
|
-
name: 'serverTransport',
|
|
12
|
-
type: 'options',
|
|
13
|
-
options: [
|
|
14
|
-
{
|
|
15
|
-
name: 'HTTP Streamable',
|
|
16
|
-
value: 'httpStreamable',
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
name: 'Server Sent Events (Deprecated)',
|
|
20
|
-
value: 'sse',
|
|
21
|
-
},
|
|
22
|
-
],
|
|
23
|
-
default: defaultOption,
|
|
24
|
-
description: 'The transport used by your endpoint',
|
|
25
|
-
displayOptions,
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
export const credentials: INodeCredentialDescription[] = [
|
|
29
|
-
{
|
|
30
|
-
name: 'httpBearerAuth',
|
|
31
|
-
required: true,
|
|
32
|
-
displayOptions: {
|
|
33
|
-
show: {
|
|
34
|
-
authentication: ['bearerAuth'],
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
name: 'httpHeaderAuth',
|
|
40
|
-
required: true,
|
|
41
|
-
displayOptions: {
|
|
42
|
-
show: {
|
|
43
|
-
authentication: ['headerAuth'],
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
name: 'mcpOAuth2Api',
|
|
49
|
-
required: true,
|
|
50
|
-
displayOptions: {
|
|
51
|
-
show: {
|
|
52
|
-
authentication: ['mcpOAuth2Api'],
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
name: 'httpMultipleHeadersAuth',
|
|
58
|
-
required: true,
|
|
59
|
-
displayOptions: {
|
|
60
|
-
show: {
|
|
61
|
-
authentication: ['multipleHeadersAuth'],
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
];
|
package/nodes/shared/helpers.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { IWebhookFunctions } from 'n8n-workflow';
|
|
3
|
-
import { NodeConnectionTypes } from 'n8n-workflow';
|
|
4
|
-
import type { Tool } from '@langchain/core/tools';
|
|
5
|
-
|
|
6
|
-
export async function getConnectedTools(
|
|
7
|
-
context: IWebhookFunctions,
|
|
8
|
-
includeBuiltIn: boolean = true
|
|
9
|
-
): Promise<Tool[]> {
|
|
10
|
-
const tools: Tool[] = [];
|
|
11
|
-
|
|
12
|
-
try {
|
|
13
|
-
// Get tools from connected nodes via the 'Tools' input connection
|
|
14
|
-
const connectedTools = (await context.getInputConnectionData(
|
|
15
|
-
NodeConnectionTypes.AiTool,
|
|
16
|
-
0,
|
|
17
|
-
)) as Tool[] | Tool | undefined;
|
|
18
|
-
|
|
19
|
-
if (connectedTools) {
|
|
20
|
-
if (Array.isArray(connectedTools)) {
|
|
21
|
-
tools.push(...connectedTools);
|
|
22
|
-
} else {
|
|
23
|
-
tools.push(connectedTools);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
} catch (error) {
|
|
27
|
-
// No tools connected or error retrieving them - return empty array
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return tools;
|
|
31
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { RequestInit, Response } from 'node-fetch';
|
|
3
|
-
|
|
4
|
-
// Simple pass-through fetch since we don't have the original proxy logic.
|
|
5
|
-
// In a real n8n node, you might want to integrate with n8n's proxy settings.
|
|
6
|
-
export async function proxyFetch(url: string | URL, init?: RequestInit): Promise<Response> {
|
|
7
|
-
// @ts-ignore - native fetch vs node-fetch types mismatch might occur, but for now we try global fetch or assume node-fetch is available
|
|
8
|
-
// If running in Node 18+, global fetch exists.
|
|
9
|
-
// n8n environments often have fetch.
|
|
10
|
-
return (globalThis.fetch as unknown as typeof fetch)(url as any, init as any);
|
|
11
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { DynamicTool } from '@langchain/core/tools';
|
|
2
|
-
import type { ISupplyDataFunctions } from 'n8n-workflow';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Wraps a LangChain tool with logging functionality for n8n
|
|
6
|
-
*/
|
|
7
|
-
export function logWrapper(
|
|
8
|
-
tool: DynamicTool,
|
|
9
|
-
context: ISupplyDataFunctions,
|
|
10
|
-
): DynamicTool {
|
|
11
|
-
// Simply return the tool as-is since we don't have full n8n logging infrastructure
|
|
12
|
-
return tool;
|
|
13
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility for parsing JSON schemas for n8n resource mapping
|
|
3
|
-
*/
|
|
4
|
-
export function convertJsonSchemaToZod(schema: any): any {
|
|
5
|
-
// This is a simplified stub - real implementation would convert JSON Schema to Zod schema
|
|
6
|
-
// For now, return a passthrough that accepts any object
|
|
7
|
-
const { z } = require('zod');
|
|
8
|
-
return z.any();
|
|
9
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { INodeProperties } from 'n8n-workflow';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Returns a connection hint notice field for AI nodes
|
|
5
|
-
*/
|
|
6
|
-
export function getConnectionHintNoticeField(
|
|
7
|
-
connectionTypes: string[],
|
|
8
|
-
): INodeProperties {
|
|
9
|
-
return {
|
|
10
|
-
displayName: '',
|
|
11
|
-
name: 'notice',
|
|
12
|
-
type: 'notice',
|
|
13
|
-
default: '',
|
|
14
|
-
displayOptions: {
|
|
15
|
-
show: {
|
|
16
|
-
'@version': [{ _cnd: { gte: 1 } }],
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
}
|
package/nodes/shared/types.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { JSONSchema7 } from 'json-schema';
|
|
2
|
-
|
|
3
|
-
export type McpTool = { name: string; description?: string; inputSchema: JSONSchema7 };
|
|
4
|
-
|
|
5
|
-
export type McpServerTransport = 'sse' | 'httpStreamable';
|
|
6
|
-
|
|
7
|
-
export type McpAuthenticationOption =
|
|
8
|
-
| 'none'
|
|
9
|
-
| 'headerAuth'
|
|
10
|
-
| 'bearerAuth'
|
|
11
|
-
| 'mcpOAuth2Api'
|
|
12
|
-
| 'multipleHeadersAuth';
|