n8n-nodes-browser-smart-automation 0.1.0

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 (71) hide show
  1. package/dist/McpClient/McpClient.node.js +333 -0
  2. package/dist/McpClient/McpClient.node.js.map +1 -0
  3. package/dist/McpClient/listSearch.js +58 -0
  4. package/dist/McpClient/listSearch.js.map +1 -0
  5. package/dist/McpClient/resourceMapping.js +61 -0
  6. package/dist/McpClient/resourceMapping.js.map +1 -0
  7. package/dist/McpClient/utils.js +248 -0
  8. package/dist/McpClient/utils.js.map +1 -0
  9. package/dist/McpClientTool/McpClientTool.node.js +417 -0
  10. package/dist/McpClientTool/McpClientTool.node.js.map +1 -0
  11. package/dist/McpClientTool/loadOptions.js +61 -0
  12. package/dist/McpClientTool/loadOptions.js.map +1 -0
  13. package/dist/McpClientTool/types.js +17 -0
  14. package/dist/McpClientTool/types.js.map +1 -0
  15. package/dist/McpClientTool/utils.js +120 -0
  16. package/dist/McpClientTool/utils.js.map +1 -0
  17. package/dist/McpTrigger/FlushingTransport.js +61 -0
  18. package/dist/McpTrigger/FlushingTransport.js.map +1 -0
  19. package/dist/McpTrigger/McpServer.js +246 -0
  20. package/dist/McpTrigger/McpServer.js.map +1 -0
  21. package/dist/McpTrigger/McpTrigger.node.js +196 -0
  22. package/dist/McpTrigger/McpTrigger.node.js.map +1 -0
  23. package/dist/shared/descriptions.js +89 -0
  24. package/dist/shared/descriptions.js.map +1 -0
  25. package/dist/shared/helpers.js +47 -0
  26. package/dist/shared/helpers.js.map +1 -0
  27. package/dist/shared/httpProxyAgent.js +31 -0
  28. package/dist/shared/httpProxyAgent.js.map +1 -0
  29. package/dist/shared/logWrapper.js +31 -0
  30. package/dist/shared/logWrapper.js.map +1 -0
  31. package/dist/shared/schemaParsing.js +32 -0
  32. package/dist/shared/schemaParsing.js.map +1 -0
  33. package/dist/shared/sharedFields.js +41 -0
  34. package/dist/shared/sharedFields.js.map +1 -0
  35. package/dist/shared/types.js +17 -0
  36. package/dist/shared/types.js.map +1 -0
  37. package/dist/shared/utils.js +231 -0
  38. package/dist/shared/utils.js.map +1 -0
  39. package/jest.config.js +24 -0
  40. package/nodes/McpClient/McpClient.node.ts +327 -0
  41. package/nodes/McpClient/__test__/McpClient.node.test.ts +221 -0
  42. package/nodes/McpClient/__test__/utils.test.ts +302 -0
  43. package/nodes/McpClient/listSearch.ts +48 -0
  44. package/nodes/McpClient/resourceMapping.ts +48 -0
  45. package/nodes/McpClient/utils.ts +281 -0
  46. package/nodes/McpClientTool/McpClientTool.node.ts +468 -0
  47. package/nodes/McpClientTool/__test__/McpClientTool.node.test.ts +730 -0
  48. package/nodes/McpClientTool/loadOptions.ts +45 -0
  49. package/nodes/McpClientTool/types.ts +1 -0
  50. package/nodes/McpClientTool/utils.ts +116 -0
  51. package/nodes/McpTrigger/FlushingTransport.ts +61 -0
  52. package/nodes/McpTrigger/McpServer.ts +317 -0
  53. package/nodes/McpTrigger/McpTrigger.node.ts +204 -0
  54. package/nodes/McpTrigger/__test__/FlushingTransport.test.ts +102 -0
  55. package/nodes/McpTrigger/__test__/McpServer.test.ts +532 -0
  56. package/nodes/McpTrigger/__test__/McpTrigger.node.test.ts +171 -0
  57. package/nodes/mcp.dark.svg +7 -0
  58. package/nodes/mcp.svg +7 -0
  59. package/nodes/shared/__test__/utils.test.ts +318 -0
  60. package/nodes/shared/descriptions.ts +65 -0
  61. package/nodes/shared/helpers.ts +31 -0
  62. package/nodes/shared/httpProxyAgent.ts +11 -0
  63. package/nodes/shared/logWrapper.ts +13 -0
  64. package/nodes/shared/schemaParsing.ts +9 -0
  65. package/nodes/shared/sharedFields.ts +20 -0
  66. package/nodes/shared/types.ts +12 -0
  67. package/nodes/shared/utils.ts +296 -0
  68. package/officail/package.json +255 -0
  69. package/package.json +46 -0
  70. package/tsconfig.json +32 -0
  71. package/tsup.config.ts +11 -0
@@ -0,0 +1,7 @@
1
+ <svg width="180" height="180" viewBox="0 0 195 195" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g stroke="#fff" stroke-width="12" stroke-linecap="round">
3
+ <path d="M25 97.8528L92.8823 29.9706C102.255 20.598 117.451 20.598 126.823 29.9706V29.9706C136.196 39.3431 136.196 54.5391 126.823 63.9117L75.5581 115.177"/>
4
+ <path d="M76.2653 114.47L126.823 63.9117C136.196 54.5391 151.392 54.5391 160.765 63.9117L161.118 64.2652C170.491 73.6378 170.491 88.8338 161.118 98.2063L99.7248 159.6C96.6006 162.724 96.6006 167.789 99.7248 170.913L112.331 183.52"/>
5
+ <path d="M109.853 46.9411L59.6482 97.1457C50.2757 106.518 50.2757 121.714 59.6482 131.087V131.087C69.0208 140.459 84.2168 140.459 93.5894 131.087L143.794 80.8822"/>
6
+ </g>
7
+ </svg>
package/nodes/mcp.svg ADDED
@@ -0,0 +1,7 @@
1
+ <svg width="180" height="180" viewBox="0 0 195 195" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g stroke="#000" stroke-width="12" stroke-linecap="round">
3
+ <path d="M25 97.8528L92.8823 29.9706C102.255 20.598 117.451 20.598 126.823 29.9706V29.9706C136.196 39.3431 136.196 54.5391 126.823 63.9117L75.5581 115.177"/>
4
+ <path d="M76.2653 114.47L126.823 63.9117C136.196 54.5391 151.392 54.5391 160.765 63.9117L161.118 64.2652C170.491 73.6378 170.491 88.8338 161.118 98.2063L99.7248 159.6C96.6006 162.724 96.6006 167.789 99.7248 170.913L112.331 183.52"/>
5
+ <path d="M109.853 46.9411L59.6482 97.1457C50.2757 106.518 50.2757 121.714 59.6482 131.087V131.087C69.0208 140.459 84.2168 140.459 93.5894 131.087L143.794 80.8822"/>
6
+ </g>
7
+ </svg>
@@ -0,0 +1,318 @@
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
+ });
@@ -0,0 +1,65 @@
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
+ ];
@@ -0,0 +1,31 @@
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
+ }
@@ -0,0 +1,11 @@
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
+ }
@@ -0,0 +1,13 @@
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
+ }
@@ -0,0 +1,9 @@
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
+ }
@@ -0,0 +1,20 @@
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
+ }
@@ -0,0 +1,12 @@
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';