n8n-nodes-smart-browser-automation 1.6.22 → 1.6.24

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.
@@ -12,6 +12,7 @@ declare class BrowserSessionManager {
12
12
  private tools;
13
13
  private constructor();
14
14
  static getInstance(): BrowserSessionManager;
15
+ private parseUrl;
15
16
  initialize(mcpEndpoint: string, useCDP: boolean, cdpEndpoint?: string): Promise<MCPTool[]>;
16
17
  private getAllTools;
17
18
  callTool(toolName: string, toolArgs?: any): Promise<any>;
@@ -48,6 +48,17 @@ class BrowserSessionManager {
48
48
  }
49
49
  return BrowserSessionManager.instance;
50
50
  }
51
+ /*
52
+ * Safe URL parser helper
53
+ */
54
+ parseUrl(url, description) {
55
+ try {
56
+ return new URL(url);
57
+ }
58
+ catch (error) {
59
+ throw new Error(`Invalid ${description}: "${url}". Please ensure it is a valid URL (e.g. starting with http:// or https://).`);
60
+ }
61
+ }
51
62
  async initialize(mcpEndpoint, useCDP, cdpEndpoint) {
52
63
  // Validate endpoint inputs early to avoid confusing runtime errors
53
64
  let trimmedMcpEndpoint = String(mcpEndpoint ?? '').trim();
@@ -55,10 +66,11 @@ class BrowserSessionManager {
55
66
  throw new Error('MCP Endpoint is required');
56
67
  }
57
68
  // Be forgiving: if user passes host:port without scheme, assume http://
58
- // Examples: localhost:3000, 127.0.0.1:3000, example.com:8080
59
69
  if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmedMcpEndpoint)) {
60
70
  trimmedMcpEndpoint = `http://${trimmedMcpEndpoint}`;
61
71
  }
72
+ // Validate URL format immediately
73
+ const mcpUrlObj = this.parseUrl(trimmedMcpEndpoint, 'MCP Endpoint');
62
74
  if (/^wss?:\/\//i.test(trimmedMcpEndpoint)) {
63
75
  throw new Error(`Invalid MCP Endpoint: "${trimmedMcpEndpoint}". ` +
64
76
  'MCP Endpoint must be an http(s) URL (for SSE or Streamable HTTP). ' +
@@ -91,22 +103,23 @@ class BrowserSessionManager {
91
103
  let urlIsSse = false;
92
104
  if (isUrl) {
93
105
  try {
94
- urlIsSse = /(^|\/)sse\/?(\?|#|$)/i.test(new URL(trimmedMcpEndpoint).pathname);
106
+ urlIsSse = /(^|\/)sse\/?(\?|#|$)/i.test(mcpUrlObj.pathname);
95
107
  }
96
108
  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).`);
109
+ // Should be caught by parseUrl above, but just in case
110
+ throw new Error(`Invalid MCP Endpoint path: "${trimmedMcpEndpoint}".`);
98
111
  }
99
112
  }
100
113
  if (isUrl) {
101
114
  if (urlIsSse) {
102
115
  // Connect via SSE
103
116
  const { SSEClientTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/client/sse.js')));
104
- this.transport = new SSEClientTransport(new URL(trimmedMcpEndpoint));
117
+ this.transport = new SSEClientTransport(new URL(trimmedMcpEndpoint)); // Safe because we parsed it above
105
118
  }
106
119
  else {
107
120
  // Connect via Streamable HTTP
108
121
  const { StreamableHTTPClientTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/client/streamableHttp.js')));
109
- this.transport = new StreamableHTTPClientTransport(new URL(trimmedMcpEndpoint));
122
+ this.transport = new StreamableHTTPClientTransport(new URL(trimmedMcpEndpoint)); // Safe because we parsed it above
110
123
  }
111
124
  }
112
125
  else {
@@ -86,8 +86,8 @@ class SmartBrowserAutomationTools {
86
86
  name: 'Browser Automation Tools',
87
87
  },
88
88
  inputs: [],
89
- outputs: [n8n_workflow_1.NodeConnectionTypes.AiTool],
90
- outputNames: ['Tools'],
89
+ outputs: [n8n_workflow_1.NodeConnectionTypes.AiTool, n8n_workflow_1.NodeConnectionTypes.Main],
90
+ outputNames: ['Tools', 'Debug'],
91
91
  icon: 'file:smartBrowserAutomation.svg',
92
92
  credentials: [
93
93
  {
@@ -140,7 +140,7 @@ class SmartBrowserAutomationTools {
140
140
  const e = error;
141
141
  const mode = useCDP ? 'cdp' : 'launch';
142
142
  const details = `mcp=${mcpEndpoint} | mode=${mode}${useCDP ? ` | cdp=${cdpUrl || 'unset'}` : ''}`;
143
- throw new n8n_workflow_1.NodeOperationError(node, `Failed to connect to MCP: ${e.message}`, {
143
+ throw new n8n_workflow_1.NodeOperationError(node, `Failed to connect to MCP. ${details}. Original error: ${e.message}`, {
144
144
  itemIndex,
145
145
  description: details,
146
146
  });
@@ -149,6 +149,18 @@ class SmartBrowserAutomationTools {
149
149
  if (!mcpTools.length) {
150
150
  throw new n8n_workflow_1.NodeOperationError(node, 'MCP Server returned no tools', { itemIndex });
151
151
  }
152
+ // Emit a debug item on the Main output so users can see endpoints and tool list in the UI
153
+ const debugJson = {
154
+ mcpEndpoint,
155
+ browserMode: credentials.browserMode,
156
+ cdpEndpoint: useCDP ? cdpUrl : undefined,
157
+ toolCount: mcpTools.length,
158
+ tools: mcpTools.map((t) => ({
159
+ name: t.name,
160
+ description: t.description ?? '',
161
+ })),
162
+ };
163
+ this.addOutputData(n8n_workflow_1.NodeConnectionTypes.Main, 0, [[{ json: debugJson }]]);
152
164
  const { DynamicStructuredTool } = await importDynamicStructuredTool();
153
165
  const { Toolkit } = await importToolkitBase();
154
166
  const ctx = this;
@@ -173,13 +185,33 @@ class SmartBrowserAutomationTools {
173
185
  catch { }
174
186
  const { index } = ctx.addInputData(n8n_workflow_1.NodeConnectionTypes.AiTool, [[{ json: input }]], runIndex);
175
187
  try {
188
+ console.log(`[AI Agent] Executing tool ${toolName} with args:`, JSON.stringify(args));
176
189
  const result = await sessionManager.callTool(toolName, args);
177
190
  const output = { tool: toolName, result };
178
- ctx.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiTool, index, [[{ json: output }]]);
191
+ // Ensure the output is an array of INodeExecutionData
192
+ const executionData = [[{ json: output }]];
193
+ ctx.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiTool, index, executionData);
179
194
  return formatMcpToolResult(result);
180
195
  }
181
196
  catch (e) {
182
- ctx.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiTool, index, e);
197
+ const errorMsg = e.message || String(e);
198
+ console.error(`[AI Agent] Tool ${toolName} failed:`, errorMsg);
199
+ // Fix: properly format error for addOutputData
200
+ const errorOutput = [[{
201
+ json: {
202
+ tool: toolName,
203
+ error: errorMsg,
204
+ stack: e.stack
205
+ }
206
+ }]];
207
+ // Safely attempt to report error to UI
208
+ try {
209
+ ctx.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiTool, index, errorOutput);
210
+ }
211
+ catch (addError) {
212
+ console.error('[AI Agent] Failed to add error output:', addError);
213
+ }
214
+ // Re-throw so the AI Agent knows it failed
183
215
  throw e;
184
216
  }
185
217
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-smart-browser-automation",
3
- "version": "1.6.22",
3
+ "version": "1.6.24",
4
4
  "description": "n8n node for AI-driven browser automation using MCP",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",
@@ -63,4 +63,4 @@
63
63
  "@modelcontextprotocol/sdk": "1.17.0",
64
64
  "zod": "3.25.76"
65
65
  }
66
- }
66
+ }