n8n-nodes-smart-browser-automation 1.6.14 → 1.6.17

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.
@@ -49,6 +49,30 @@ class BrowserSessionManager {
49
49
  return BrowserSessionManager.instance;
50
50
  }
51
51
  async initialize(mcpEndpoint, useCDP, cdpEndpoint) {
52
+ // Validate endpoint inputs early to avoid confusing runtime errors
53
+ let trimmedMcpEndpoint = String(mcpEndpoint ?? '').trim();
54
+ if (!trimmedMcpEndpoint) {
55
+ throw new Error('MCP Endpoint is required');
56
+ }
57
+ // Be forgiving: if user passes host:port without scheme, assume http://
58
+ // Examples: localhost:3000, 127.0.0.1:3000, example.com:8080
59
+ if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmedMcpEndpoint)) {
60
+ trimmedMcpEndpoint = `http://${trimmedMcpEndpoint}`;
61
+ }
62
+ if (/^wss?:\/\//i.test(trimmedMcpEndpoint)) {
63
+ throw new Error(`Invalid MCP Endpoint: "${trimmedMcpEndpoint}". ` +
64
+ 'MCP Endpoint must be an http(s) URL (for SSE or Streamable HTTP). ' +
65
+ 'If you are using a browser CDP connection, put the ws(s) URL in the CDP Endpoint field instead.');
66
+ }
67
+ if (useCDP) {
68
+ const trimmedCdp = String(cdpEndpoint ?? '').trim();
69
+ if (!trimmedCdp) {
70
+ throw new Error('CDP endpoint is required when Browser Mode is CDP Connection');
71
+ }
72
+ if (!/^wss?:\/\//i.test(trimmedCdp)) {
73
+ throw new Error(`Invalid CDP Endpoint: "${trimmedCdp}". CDP Endpoint must be a ws(s) URL (e.g. ws://localhost:9222 or wss://.../devtools/...).`);
74
+ }
75
+ }
52
76
  // Only initialize if not already done or config changed
53
77
  if (this.isInitialized &&
54
78
  this.config.mcpEndpoint === mcpEndpoint &&
@@ -63,18 +87,26 @@ class BrowserSessionManager {
63
87
  // Initialize MCP client
64
88
  this.mcpClient = new index_js_1.Client({ name: 'n8n-browser-automation', version: '1.0.0' }, { capabilities: {} });
65
89
  // Determine transport based on endpoint type
66
- const isUrl = mcpEndpoint.startsWith('http://') || mcpEndpoint.startsWith('https://');
67
- const urlIsSse = isUrl && /(^|\/)sse\/?(\?|#|$)/i.test(new URL(mcpEndpoint).pathname);
90
+ const isUrl = trimmedMcpEndpoint.startsWith('http://') || trimmedMcpEndpoint.startsWith('https://');
91
+ let urlIsSse = false;
92
+ if (isUrl) {
93
+ try {
94
+ urlIsSse = /(^|\/)sse\/?(\?|#|$)/i.test(new URL(trimmedMcpEndpoint).pathname);
95
+ }
96
+ catch (e) {
97
+ throw new Error(`Invalid MCP Endpoint: "${trimmedMcpEndpoint}". MCP Endpoint must be a valid http(s) URL (or a local file path for stdio).`);
98
+ }
99
+ }
68
100
  if (isUrl) {
69
101
  if (urlIsSse) {
70
102
  // Connect via SSE
71
103
  const { SSEClientTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/client/sse.js')));
72
- this.transport = new SSEClientTransport(new URL(mcpEndpoint));
104
+ this.transport = new SSEClientTransport(new URL(trimmedMcpEndpoint));
73
105
  }
74
106
  else {
75
107
  // Connect via Streamable HTTP
76
108
  const { StreamableHTTPClientTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/client/streamableHttp.js')));
77
- this.transport = new StreamableHTTPClientTransport(new URL(mcpEndpoint));
109
+ this.transport = new StreamableHTTPClientTransport(new URL(trimmedMcpEndpoint));
78
110
  }
79
111
  }
80
112
  else {
@@ -95,7 +127,7 @@ class BrowserSessionManager {
95
127
  // Remote servers often need a moment to register the session before accepting POST calls
96
128
  if (isUrl) {
97
129
  if (process.env.NODE_ENV !== 'production') {
98
- console.log(`[MCP] Connected to ${mcpEndpoint}. Waiting for session stabilization...`);
130
+ console.log(`[MCP] Connected to ${trimmedMcpEndpoint}. Waiting for session stabilization...`);
99
131
  }
100
132
  await new Promise(resolve => setTimeout(resolve, 2000));
101
133
  }
@@ -103,7 +135,7 @@ class BrowserSessionManager {
103
135
  catch (error) {
104
136
  this.isInitialized = false;
105
137
  const transportType = isUrl ? (urlIsSse ? 'SSE' : 'Streamable HTTP') : 'Stdio';
106
- throw new Error(`Failed to connect to MCP server via ${transportType} at ${mcpEndpoint}. Error: ${error.message}`);
138
+ throw new Error(`Failed to connect to MCP server via ${transportType} at ${trimmedMcpEndpoint}. Error: ${error.message}`);
107
139
  }
108
140
  this.isInitialized = true;
109
141
  this.config = { mcpEndpoint, useCDP, cdpEndpoint };
@@ -1,5 +1,6 @@
1
- import { type INodeType, type INodeTypeDescription, type ISupplyDataFunctions, type SupplyData } from 'n8n-workflow';
1
+ import { type IExecuteFunctions, type INodeExecutionData, type INodeType, type INodeTypeDescription, type ISupplyDataFunctions, type SupplyData } from 'n8n-workflow';
2
2
  export declare class SmartBrowserAutomationTools implements INodeType {
3
3
  description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
4
5
  supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData>;
5
6
  }
@@ -66,8 +66,8 @@ class SmartBrowserAutomationTools {
66
66
  name: 'Browser Automation Tools',
67
67
  },
68
68
  inputs: [],
69
- outputs: [n8n_workflow_1.NodeConnectionTypes.AiTool],
70
- outputNames: ['Tools'],
69
+ outputs: [n8n_workflow_1.NodeConnectionTypes.AiTool, n8n_workflow_1.NodeConnectionTypes.Main],
70
+ outputNames: ['Tools', 'Debug'],
71
71
  icon: 'file:smartBrowserAutomation.svg',
72
72
  credentials: [
73
73
  {
@@ -86,6 +86,44 @@ class SmartBrowserAutomationTools {
86
86
  },
87
87
  ],
88
88
  };
89
+ async execute() {
90
+ const node = this.getNode();
91
+ const sessionManager = BrowserSessionManager_1.default.getInstance();
92
+ const itemIndex = 0;
93
+ try {
94
+ const credentials = await this.getCredentials('smartBrowserAutomationApi');
95
+ const cdpOverride = this.getNodeParameter('cdpOverride', itemIndex, '');
96
+ const useCDP = credentials.browserMode === 'cdp';
97
+ const cdpUrl = (cdpOverride || credentials.cdpEndpoint || '').trim();
98
+ await sessionManager.initialize(credentials.mcpEndpoint, useCDP, useCDP ? cdpUrl : undefined);
99
+ const mcpTools = await sessionManager.listTools();
100
+ const debugJson = {
101
+ mcpEndpoint: credentials.mcpEndpoint,
102
+ browserMode: credentials.browserMode,
103
+ useCDP,
104
+ cdpEndpoint: useCDP ? cdpUrl : undefined,
105
+ toolCount: mcpTools.length,
106
+ tools: mcpTools.map((t) => ({
107
+ name: t.name,
108
+ description: t.description ?? '',
109
+ inputSchema: t.inputSchema ?? undefined,
110
+ })),
111
+ };
112
+ return [[], [{ json: debugJson }]];
113
+ }
114
+ catch (error) {
115
+ const err = error;
116
+ const debugJson = {
117
+ error: true,
118
+ message: err?.message ? String(err.message) : String(err),
119
+ code: err?.code ? String(err.code) : undefined,
120
+ };
121
+ throw new n8n_workflow_1.NodeOperationError(node, debugJson.message, { itemIndex, description: JSON.stringify(debugJson) });
122
+ }
123
+ finally {
124
+ await sessionManager.close();
125
+ }
126
+ }
89
127
  async supplyData(itemIndex) {
90
128
  const node = this.getNode();
91
129
  const credentials = await this.getCredentials('smartBrowserAutomationApi');
@@ -99,6 +137,12 @@ class SmartBrowserAutomationTools {
99
137
  catch (error) {
100
138
  throw new n8n_workflow_1.NodeOperationError(node, `Failed to connect to MCP server: ${error.message}`, {
101
139
  itemIndex,
140
+ description: JSON.stringify({
141
+ mcpEndpoint: credentials.mcpEndpoint,
142
+ browserMode: credentials.browserMode,
143
+ useCDP,
144
+ cdpEndpoint: useCDP ? cdpUrl : undefined,
145
+ }),
102
146
  });
103
147
  }
104
148
  const mcpTools = await sessionManager.listTools();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-smart-browser-automation",
3
- "version": "1.6.14",
3
+ "version": "1.6.17",
4
4
  "description": "n8n node for AI-driven browser automation using MCP",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",