n8n-nodes-browser-smart-automation 0.1.2 → 0.1.4

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 (59) hide show
  1. package/dist/McpClientTool/McpClientTool.node.js +2 -13
  2. package/dist/McpClientTool/McpClientTool.node.js.map +1 -1
  3. package/dist/McpClientTool/utils.js +1 -1
  4. package/dist/McpClientTool/utils.js.map +1 -1
  5. package/dist/McpTrigger/McpTrigger.node.js +1 -1
  6. package/dist/McpTrigger/McpTrigger.node.js.map +1 -1
  7. package/dist/shared/N8nBinaryLoader.js +203 -0
  8. package/dist/shared/N8nBinaryLoader.js.map +1 -0
  9. package/dist/shared/N8nJsonLoader.js +89 -0
  10. package/dist/shared/N8nJsonLoader.js.map +1 -0
  11. package/dist/shared/N8nTool.js +106 -0
  12. package/dist/shared/N8nTool.js.map +1 -0
  13. package/dist/shared/embeddingInputValidation.js +55 -0
  14. package/dist/shared/embeddingInputValidation.js.map +1 -0
  15. package/dist/shared/helpers.js +220 -13
  16. package/dist/shared/helpers.js.map +1 -1
  17. package/dist/shared/httpProxyAgent.js +40 -2
  18. package/dist/shared/httpProxyAgent.js.map +1 -1
  19. package/dist/shared/logWrapper.js +347 -2
  20. package/dist/shared/logWrapper.js.map +1 -1
  21. package/dist/shared/schemaParsing.js +47 -4
  22. package/dist/shared/schemaParsing.js.map +1 -1
  23. package/dist/shared/sharedFields.js +142 -7
  24. package/dist/shared/sharedFields.js.map +1 -1
  25. package/dist/shared/typesN8nTool.js +17 -0
  26. package/dist/shared/typesN8nTool.js.map +1 -0
  27. package/dist/shared/utils.js +1 -1
  28. package/dist/shared/utils.js.map +1 -1
  29. package/package.json +23 -7
  30. package/jest.config.js +0 -24
  31. package/nodes/McpClient/McpClient.node.ts +0 -327
  32. package/nodes/McpClient/__test__/McpClient.node.test.ts +0 -221
  33. package/nodes/McpClient/__test__/utils.test.ts +0 -302
  34. package/nodes/McpClient/listSearch.ts +0 -48
  35. package/nodes/McpClient/resourceMapping.ts +0 -48
  36. package/nodes/McpClient/utils.ts +0 -281
  37. package/nodes/McpClientTool/McpClientTool.node.ts +0 -468
  38. package/nodes/McpClientTool/__test__/McpClientTool.node.test.ts +0 -730
  39. package/nodes/McpClientTool/loadOptions.ts +0 -45
  40. package/nodes/McpClientTool/types.ts +0 -1
  41. package/nodes/McpClientTool/utils.ts +0 -116
  42. package/nodes/McpTrigger/FlushingTransport.ts +0 -61
  43. package/nodes/McpTrigger/McpServer.ts +0 -317
  44. package/nodes/McpTrigger/McpTrigger.node.ts +0 -204
  45. package/nodes/McpTrigger/__test__/FlushingTransport.test.ts +0 -102
  46. package/nodes/McpTrigger/__test__/McpServer.test.ts +0 -532
  47. package/nodes/McpTrigger/__test__/McpTrigger.node.test.ts +0 -171
  48. package/nodes/shared/__test__/utils.test.ts +0 -318
  49. package/nodes/shared/descriptions.ts +0 -65
  50. package/nodes/shared/helpers.ts +0 -31
  51. package/nodes/shared/httpProxyAgent.ts +0 -11
  52. package/nodes/shared/logWrapper.ts +0 -13
  53. package/nodes/shared/schemaParsing.ts +0 -9
  54. package/nodes/shared/sharedFields.ts +0 -20
  55. package/nodes/shared/types.ts +0 -12
  56. package/nodes/shared/utils.ts +0 -296
  57. package/officail/package.json +0 -255
  58. package/tsconfig.json +0 -32
  59. package/tsup.config.ts +0 -16
@@ -1,204 +0,0 @@
1
- import { WebhookAuthorizationError } from 'n8n-nodes-base/dist/nodes/Webhook/error';
2
- import { validateWebhookAuthentication } from 'n8n-nodes-base/dist/nodes/Webhook/utils';
3
- import type { INodeTypeDescription, IWebhookFunctions, IWebhookResponseData } from 'n8n-workflow';
4
- import { NodeConnectionTypes, Node, nodeNameToToolName } from 'n8n-workflow';
5
-
6
- import { getConnectedTools } from '@utils/helpers';
7
-
8
- import type { CompressionResponse } from './FlushingTransport';
9
- import { McpServerManager } from './McpServer';
10
-
11
- const MCP_SSE_SETUP_PATH = 'sse';
12
- const MCP_SSE_MESSAGES_PATH = 'messages';
13
-
14
- export class McpTrigger extends Node {
15
- description: INodeTypeDescription = {
16
- displayName: 'MCP Server Trigger',
17
- name: 'mcpTrigger',
18
- icon: {
19
- light: 'file:../mcp.svg',
20
- dark: 'file:../mcp.dark.svg',
21
- },
22
- group: ['trigger'],
23
- version: [1, 1.1, 2],
24
- description: 'Expose n8n tools as an MCP Server endpoint',
25
- activationMessage:
26
- 'You can now connect your MCP Clients to the URL, using SSE or Streamable HTTP transports.',
27
- defaults: {
28
- name: 'MCP Server Trigger',
29
- },
30
- codex: {
31
- categories: ['AI', 'Core Nodes'],
32
- subcategories: {
33
- AI: ['Root Nodes', 'Model Context Protocol'],
34
- 'Core Nodes': ['Other Trigger Nodes'],
35
- },
36
- alias: ['Model Context Protocol', 'MCP Server'],
37
- resources: {
38
- primaryDocumentation: [
39
- {
40
- url: 'https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-langchain.mcptrigger/',
41
- },
42
- ],
43
- },
44
- },
45
- triggerPanel: {
46
- header: 'Listen for MCP events',
47
- executionsHelp: {
48
- inactive:
49
- "This trigger has two modes: test and production.<br /><br /><b>Use test mode while you build your workflow</b>. Click the 'execute step' button, then make an MCP request to the test URL. The executions will show up in the editor.<br /><br /><b>Use production mode to run your workflow automatically</b>. Publish the workflow, then make requests to the production URL. These executions will show up in the <a data-key='executions'>executions list</a>, but not the editor.",
50
- active:
51
- "This trigger has two modes: test and production.<br /><br /><b>Use test mode while you build your workflow</b>. Click the 'execute step' button, then make an MCP request to the test URL. The executions will show up in the editor.<br /><br /><b>Use production mode to run your workflow automatically</b>. Since your workflow is activated, you can make requests to the production URL. These executions will show up in the <a data-key='executions'>executions list</a>, but not the editor.",
52
- },
53
- activationHint:
54
- 'Once you’ve finished building your workflow, run it without having to click this button by using the production URL.',
55
- },
56
- inputs: [
57
- {
58
- type: NodeConnectionTypes.AiTool,
59
- displayName: 'Tools',
60
- },
61
- ],
62
- outputs: [],
63
- credentials: [
64
- {
65
- // eslint-disable-next-line n8n-nodes-base/node-class-description-credentials-name-unsuffixed
66
- name: 'httpBearerAuth',
67
- required: true,
68
- displayOptions: {
69
- show: {
70
- authentication: ['bearerAuth'],
71
- },
72
- },
73
- },
74
- {
75
- name: 'httpHeaderAuth',
76
- required: true,
77
- displayOptions: {
78
- show: {
79
- authentication: ['headerAuth'],
80
- },
81
- },
82
- },
83
- ],
84
- properties: [
85
- {
86
- displayName: 'Authentication',
87
- name: 'authentication',
88
- type: 'options',
89
- options: [
90
- { name: 'None', value: 'none' },
91
- { name: 'Bearer Auth', value: 'bearerAuth' },
92
- { name: 'Header Auth', value: 'headerAuth' },
93
- ],
94
- default: 'none',
95
- description: 'The way to authenticate',
96
- },
97
- {
98
- displayName: 'Path',
99
- name: 'path',
100
- type: 'string',
101
- default: '',
102
- placeholder: 'webhook',
103
- required: true,
104
- description: 'The base path for this MCP server',
105
- },
106
- ],
107
- webhooks: [
108
- {
109
- name: 'setup',
110
- httpMethod: 'GET',
111
- responseMode: 'onReceived',
112
- isFullPath: true,
113
- path: `={{$parameter["path"]}}{{parseFloat($nodeVersion)<2 ? '/${MCP_SSE_SETUP_PATH}' : ''}}`,
114
- nodeType: 'mcp',
115
- ndvHideMethod: true,
116
- ndvHideUrl: false,
117
- },
118
- {
119
- name: 'default',
120
- httpMethod: 'POST',
121
- responseMode: 'onReceived',
122
- isFullPath: true,
123
- path: `={{$parameter["path"]}}{{parseFloat($nodeVersion)<2 ? '/${MCP_SSE_MESSAGES_PATH}' : ''}}`,
124
- nodeType: 'mcp',
125
- ndvHideMethod: true,
126
- ndvHideUrl: true,
127
- },
128
- {
129
- name: 'default',
130
- httpMethod: 'DELETE',
131
- responseMode: 'onReceived',
132
- isFullPath: true,
133
- path: '={{$parameter["path"]}}',
134
- nodeType: 'mcp',
135
- ndvHideMethod: true,
136
- ndvHideUrl: true,
137
- },
138
- ],
139
- };
140
-
141
- async webhook(context: IWebhookFunctions): Promise<IWebhookResponseData> {
142
- const webhookName = context.getWebhookName();
143
- const req = context.getRequestObject();
144
- const resp = context.getResponseObject() as unknown as CompressionResponse;
145
-
146
- try {
147
- await validateWebhookAuthentication(context, 'authentication');
148
- } catch (error) {
149
- if (error instanceof WebhookAuthorizationError) {
150
- resp.writeHead(error.responseCode);
151
- resp.end(error.message);
152
- return { noWebhookResponse: true };
153
- }
154
- throw error;
155
- }
156
- const node = context.getNode();
157
- // Get a url/tool friendly name for the server, based on the node name
158
- const serverName = node.typeVersion > 1 ? nodeNameToToolName(node) : 'n8n-mcp-server';
159
-
160
- const mcpServerManager: McpServerManager = McpServerManager.instance(context.logger);
161
-
162
- if (webhookName === 'setup') {
163
- // Sets up the transport and opens the long-lived connection. This resp
164
- // will stay streaming, and is the channel that sends the events
165
-
166
- // Prior to version 2.0, we use different paths for the setup and messages.
167
- const postUrl =
168
- node.typeVersion < 2
169
- ? req.path.replace(new RegExp(`/${MCP_SSE_SETUP_PATH}$`), `/${MCP_SSE_MESSAGES_PATH}`)
170
- : req.path;
171
- await mcpServerManager.createServerWithSSETransport(serverName, postUrl, resp);
172
-
173
- return { noWebhookResponse: true };
174
- } else if (webhookName === 'default') {
175
- // Here we handle POST and DELETE requests.
176
- // POST can be either:
177
- // 1) Client calls in an established session using the SSE transport, or
178
- // 2) Client calls in an established session using the StreamableHTTPServerTransport
179
- // 3) Session setup requests using the StreamableHTTPServerTransport
180
- // DELETE is used to terminate the session using the StreamableHTTPServerTransport
181
-
182
- if (req.method === 'DELETE') {
183
- await mcpServerManager.handleDeleteRequest(req, resp);
184
- } else {
185
- // Check if there is a session and a transport is already established
186
- const sessionId = mcpServerManager.getSessionId(req);
187
-
188
- if (sessionId && mcpServerManager.getTransport(sessionId)) {
189
- const connectedTools = await getConnectedTools(context, true);
190
- const wasToolCall = await mcpServerManager.handlePostMessage(req, resp, connectedTools);
191
- if (wasToolCall) return { noWebhookResponse: true, workflowData: [[{ json: {} }]] };
192
- } else {
193
- // If no session is established, this is a setup request
194
- // for the StreamableHTTPServerTransport, so we create a new transport
195
- await mcpServerManager.createServerWithStreamableHTTPTransport(serverName, resp, req);
196
- }
197
- }
198
-
199
- return { noWebhookResponse: true };
200
- }
201
-
202
- return { workflowData: [[{ json: {} }]] };
203
- }
204
- }
@@ -1,102 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
3
- import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
4
- import type { IncomingMessage, ServerResponse } from 'http';
5
- import { mock } from 'jest-mock-extended';
6
-
7
- import { FlushingSSEServerTransport, FlushingStreamableHTTPTransport } from '../FlushingTransport';
8
- import type { CompressionResponse } from '../FlushingTransport';
9
-
10
- describe('FlushingSSEServerTransport', () => {
11
- const mockResponse = mock<CompressionResponse>();
12
- let transport: FlushingSSEServerTransport;
13
- const endpoint = '/test/endpoint';
14
-
15
- beforeEach(() => {
16
- jest.resetAllMocks();
17
- mockResponse.status.mockReturnThis();
18
- transport = new FlushingSSEServerTransport(endpoint, mockResponse);
19
- });
20
-
21
- it('should call flush after sending a message', async () => {
22
- // Create a sample JSONRPC message
23
- const message: JSONRPCMessage = {
24
- jsonrpc: '2.0',
25
- id: '123',
26
- result: { success: true },
27
- };
28
-
29
- // Send a message through the transport
30
- await transport.start();
31
- await transport.send(message);
32
-
33
- expect(mockResponse.writeHead).toHaveBeenCalledWith(200, {
34
- 'Content-Type': 'text/event-stream',
35
- 'Cache-Control': 'no-cache, no-transform',
36
- Connection: 'keep-alive',
37
- });
38
- expect(mockResponse.write).toHaveBeenCalledWith(
39
- // @ts-expect-error `_sessionId` is private
40
- `event: endpoint\ndata: /test/endpoint?sessionId=${transport._sessionId}\n\n`,
41
- );
42
- expect(mockResponse.write).toHaveBeenCalledWith(
43
- `event: message\ndata: ${JSON.stringify(message)}\n\n`,
44
- );
45
- expect(mockResponse.flush).toHaveBeenCalled();
46
- });
47
- });
48
-
49
- describe('FlushingStreamableHTTPTransport', () => {
50
- const mockResponse = mock<CompressionResponse>();
51
- let transport: FlushingStreamableHTTPTransport;
52
- const options = {
53
- sessionIdGenerator: () => 'test-session-id',
54
- onsessioninitialized: jest.fn() as () => void | Promise<void>,
55
- };
56
-
57
- beforeEach(() => {
58
- jest.resetAllMocks();
59
- mockResponse.status.mockReturnThis();
60
-
61
- // Mock the parent class methods before creating the instance
62
- jest.spyOn(StreamableHTTPServerTransport.prototype, 'send').mockResolvedValue();
63
- jest.spyOn(StreamableHTTPServerTransport.prototype, 'handleRequest').mockResolvedValue();
64
-
65
- transport = new FlushingStreamableHTTPTransport(options, mockResponse);
66
- });
67
-
68
- it('should call flush after sending a message', async () => {
69
- const message: JSONRPCMessage = {
70
- jsonrpc: '2.0',
71
- id: '123',
72
- result: { success: true },
73
- };
74
-
75
- await transport.send(message);
76
-
77
- expect(StreamableHTTPServerTransport.prototype.send).toHaveBeenCalledWith(message);
78
- expect(mockResponse.flush).toHaveBeenCalled();
79
- });
80
-
81
- it('should call flush after handling a request', async () => {
82
- const mockRequest = mock<IncomingMessage>();
83
- const mockServerResponse = mock<ServerResponse>();
84
- const parsedBody = { jsonrpc: '2.0', method: 'test', id: '123' };
85
-
86
- await transport.handleRequest(mockRequest, mockServerResponse, parsedBody);
87
-
88
- expect(StreamableHTTPServerTransport.prototype.handleRequest).toHaveBeenCalledWith(
89
- mockRequest,
90
- mockServerResponse,
91
- parsedBody,
92
- );
93
- expect(mockResponse.flush).toHaveBeenCalled();
94
- });
95
-
96
- it('should pass options correctly to parent constructor', () => {
97
- expect(transport).toBeInstanceOf(FlushingStreamableHTTPTransport);
98
- expect(transport).toBeInstanceOf(StreamableHTTPServerTransport);
99
- expect(typeof transport.send).toBe('function');
100
- expect(typeof transport.handleRequest).toBe('function');
101
- });
102
- });