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(
|
|
106
|
+
urlIsSse = /(^|\/)sse\/?(\?|#|$)/i.test(mcpUrlObj.pathname);
|
|
95
107
|
}
|
|
96
108
|
catch (e) {
|
|
97
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
+
}
|