n8n-nodes-smart-browser-automation 1.0.2 → 1.0.8
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.
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
4
4
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
5
|
+
const sse_js_1 = require("@modelcontextprotocol/sdk/client/sse.js");
|
|
5
6
|
class BrowserSessionManager {
|
|
6
7
|
static instance;
|
|
7
8
|
mcpClient = null;
|
|
@@ -29,13 +30,31 @@ class BrowserSessionManager {
|
|
|
29
30
|
}
|
|
30
31
|
// Initialize MCP client
|
|
31
32
|
this.mcpClient = new index_js_1.Client({ name: 'n8n-browser-automation', version: '1.0.0' }, { capabilities: {} });
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
33
|
+
// Determine transport based on endpoint type
|
|
34
|
+
const isUrl = mcpEndpoint.startsWith('http://') || mcpEndpoint.startsWith('https://');
|
|
35
|
+
if (isUrl) {
|
|
36
|
+
// Connect via SSE
|
|
37
|
+
this.transport = new sse_js_1.SSEClientTransport(new URL(mcpEndpoint));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
// Connect via Stdio
|
|
41
|
+
this.transport = new stdio_js_1.StdioClientTransport({
|
|
42
|
+
command: 'node',
|
|
43
|
+
args: [mcpEndpoint],
|
|
44
|
+
env: {
|
|
45
|
+
...process.env,
|
|
46
|
+
...(useCDP && cdpEndpoint ? { CDP_URL: cdpEndpoint } : {})
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
await this.mcpClient.connect(this.transport);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
this.isInitialized = false;
|
|
55
|
+
const transportType = isUrl ? 'SSE' : 'Stdio';
|
|
56
|
+
throw new Error(`Failed to connect to MCP server via ${transportType} at ${mcpEndpoint}. Error: ${error.message}`);
|
|
57
|
+
}
|
|
39
58
|
// Fetch available tools from MCP server
|
|
40
59
|
const toolsResponse = await this.mcpClient.listTools();
|
|
41
60
|
this.tools = toolsResponse.tools || [];
|
|
@@ -19,7 +19,7 @@ class SmartBrowserAutomation {
|
|
|
19
19
|
},
|
|
20
20
|
inputs: ['main'],
|
|
21
21
|
outputs: ['main'],
|
|
22
|
-
icon: '
|
|
22
|
+
icon: 'file:smartBrowserAutomation.svg',
|
|
23
23
|
credentials: [
|
|
24
24
|
{
|
|
25
25
|
name: 'smartBrowserAutomationApi',
|
|
@@ -103,11 +103,30 @@ class SmartBrowserAutomation {
|
|
|
103
103
|
default: 'initialize',
|
|
104
104
|
},
|
|
105
105
|
{
|
|
106
|
-
displayName: '
|
|
107
|
-
name: '
|
|
108
|
-
type: '
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
displayName: 'Options',
|
|
107
|
+
name: 'options',
|
|
108
|
+
type: 'collection',
|
|
109
|
+
placeholder: 'Add Option',
|
|
110
|
+
default: {},
|
|
111
|
+
options: [
|
|
112
|
+
{
|
|
113
|
+
displayName: 'Enabled Tool Names or IDs',
|
|
114
|
+
name: 'enabledTools',
|
|
115
|
+
type: 'multiOptions',
|
|
116
|
+
typeOptions: {
|
|
117
|
+
loadOptionsMethod: 'getAvailableTools',
|
|
118
|
+
},
|
|
119
|
+
default: [],
|
|
120
|
+
description: 'Select which tools to expose to the AI Agent or Manual list. Leave empty for all. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
displayName: 'Verbose',
|
|
124
|
+
name: 'verbose',
|
|
125
|
+
type: 'boolean',
|
|
126
|
+
default: false,
|
|
127
|
+
description: 'Whether to enable detailed logging',
|
|
128
|
+
},
|
|
129
|
+
],
|
|
111
130
|
},
|
|
112
131
|
],
|
|
113
132
|
};
|
|
@@ -119,16 +138,26 @@ class SmartBrowserAutomation {
|
|
|
119
138
|
const credentials = await this.getCredentials('smartBrowserAutomationApi');
|
|
120
139
|
const sessionManager = BrowserSessionManager_1.default.getInstance();
|
|
121
140
|
const tools = await sessionManager.initialize(credentials.mcpEndpoint, credentials.browserMode === 'cdp', credentials.cdpEndpoint);
|
|
122
|
-
|
|
123
|
-
|
|
141
|
+
const nodeTools = [
|
|
142
|
+
{
|
|
143
|
+
name: 'Connect to Browser (CDP)',
|
|
144
|
+
value: 'browser_connect_cdp',
|
|
145
|
+
description: 'Connect to a browser via CDP URL',
|
|
146
|
+
},
|
|
147
|
+
];
|
|
148
|
+
if (tools && tools.length > 0) {
|
|
149
|
+
nodeTools.push(...tools.map((tool) => ({
|
|
150
|
+
name: tool.name.split('_').map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(' '),
|
|
151
|
+
value: tool.name,
|
|
152
|
+
})));
|
|
124
153
|
}
|
|
125
|
-
return
|
|
126
|
-
name: tool.name.split('_').map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(' '),
|
|
127
|
-
value: tool.name,
|
|
128
|
-
}));
|
|
154
|
+
return nodeTools;
|
|
129
155
|
}
|
|
130
156
|
catch (error) {
|
|
131
|
-
return [
|
|
157
|
+
return [
|
|
158
|
+
{ name: 'Connect to Browser (CDP)', value: 'browser_connect_cdp' },
|
|
159
|
+
{ name: `Error loading MCP tools: ${error.message}`, value: 'error' }
|
|
160
|
+
];
|
|
132
161
|
}
|
|
133
162
|
},
|
|
134
163
|
},
|
|
@@ -136,12 +165,36 @@ class SmartBrowserAutomation {
|
|
|
136
165
|
// Expose tools to AI Agent nodes
|
|
137
166
|
async getTools() {
|
|
138
167
|
const credentials = await this.getCredentials('smartBrowserAutomationApi');
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
168
|
+
const options = this.getNodeParameter('options', 0, {});
|
|
169
|
+
const enabledTools = options.enabledTools || [];
|
|
170
|
+
let tools = await (0, DynamicBrowserTools_1.createDynamicBrowserTools)(credentials);
|
|
171
|
+
// Filter tools if a selection was made
|
|
172
|
+
if (enabledTools.length > 0) {
|
|
173
|
+
tools = tools.filter(tool => enabledTools.includes(tool.name));
|
|
174
|
+
}
|
|
175
|
+
// Add a custom tool for the AI to connect to a specific CDP URL
|
|
176
|
+
const connectTool = {
|
|
177
|
+
name: 'browser_connect_cdp',
|
|
178
|
+
displayName: 'Connect to Browser (CDP)',
|
|
179
|
+
description: 'Connect to a specific browser instance using a CDP URL (wss://...)',
|
|
180
|
+
properties: [
|
|
181
|
+
{
|
|
182
|
+
displayName: 'Endpoint URL',
|
|
183
|
+
name: 'endpoint',
|
|
184
|
+
type: 'string',
|
|
185
|
+
default: '',
|
|
186
|
+
required: true,
|
|
187
|
+
description: 'The wss:// or http:// endpoint for the browser CDP connection',
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
async execute(input) {
|
|
191
|
+
const sessionManager = BrowserSessionManager_1.default.getInstance();
|
|
192
|
+
const credentials = await this.getCredentials('smartBrowserAutomationApi');
|
|
193
|
+
await sessionManager.initialize(credentials.mcpEndpoint, true, input.endpoint);
|
|
194
|
+
return { success: true, message: `Connected to browser at ${input.endpoint}` };
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
return [connectTool, ...tools];
|
|
145
198
|
}
|
|
146
199
|
async execute() {
|
|
147
200
|
const items = this.getInputData();
|
|
@@ -151,24 +204,38 @@ class SmartBrowserAutomation {
|
|
|
151
204
|
const mode = this.getNodeParameter('mode', 0);
|
|
152
205
|
for (let i = 0; i < items.length; i++) {
|
|
153
206
|
try {
|
|
207
|
+
const options = this.getNodeParameter('options', i, {});
|
|
208
|
+
const verbose = options.verbose || false;
|
|
154
209
|
if (mode === 'manual') {
|
|
155
210
|
// Manual mode: User selects tool and provides arguments
|
|
156
211
|
const toolName = this.getNodeParameter('toolName', i);
|
|
157
212
|
const toolArgsStr = this.getNodeParameter('toolArgs', i);
|
|
158
|
-
const verbose = this.getNodeParameter('verbose', i, false);
|
|
159
|
-
// Initialize session if not ready
|
|
160
|
-
if (!sessionManager.isReady()) {
|
|
161
|
-
await sessionManager.initialize(credentials.mcpEndpoint, credentials.browserMode === 'cdp', credentials.cdpEndpoint);
|
|
162
|
-
}
|
|
163
213
|
let toolArgs;
|
|
164
214
|
try {
|
|
165
215
|
toolArgs = JSON.parse(toolArgsStr);
|
|
166
216
|
}
|
|
167
217
|
catch (error) {
|
|
168
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid JSON in Tool Arguments: ${error.message}
|
|
218
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid JSON in Tool Arguments: ${error.message}`);
|
|
219
|
+
}
|
|
220
|
+
// Special case: Manual Browser Connection
|
|
221
|
+
if (toolName === 'browser_connect_cdp') {
|
|
222
|
+
const endpoint = toolArgs.endpoint;
|
|
223
|
+
if (!endpoint) {
|
|
224
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Endpoint URL is required for browser_connect_cdp');
|
|
225
|
+
}
|
|
226
|
+
await sessionManager.initialize(credentials.mcpEndpoint, true, endpoint);
|
|
227
|
+
returnData.push({
|
|
228
|
+
json: { success: true, message: `Connected to browser at ${endpoint}` },
|
|
229
|
+
pairedItem: i,
|
|
230
|
+
});
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
// Initialize session if not ready
|
|
234
|
+
if (!sessionManager.isReady()) {
|
|
235
|
+
await sessionManager.initialize(credentials.mcpEndpoint, credentials.browserMode === 'cdp', credentials.cdpEndpoint);
|
|
169
236
|
}
|
|
170
237
|
if (verbose) {
|
|
171
|
-
console.log(`Executing tool: ${toolName} with args
|
|
238
|
+
console.log(`Executing tool: ${toolName} with args:`, toolArgs);
|
|
172
239
|
}
|
|
173
240
|
const result = await sessionManager.callTool(toolName, toolArgs);
|
|
174
241
|
returnData.push({
|
|
@@ -184,7 +251,9 @@ class SmartBrowserAutomation {
|
|
|
184
251
|
// AI Agent mode: Just initialize or close
|
|
185
252
|
const operation = this.getNodeParameter('operation', i);
|
|
186
253
|
if (operation === 'initialize') {
|
|
187
|
-
|
|
254
|
+
// In AI mode, we use the credentials or fallback to nothing
|
|
255
|
+
const cdpUrl = credentials.cdpEndpoint;
|
|
256
|
+
const tools = await sessionManager.initialize(credentials.mcpEndpoint, true, cdpUrl);
|
|
188
257
|
returnData.push({
|
|
189
258
|
json: {
|
|
190
259
|
success: true,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect x="4" y="10" width="56" height="44" rx="4" fill="#6366F1"/>
|
|
3
|
+
<rect x="4" y="10" width="56" height="10" rx="4" fill="#4F46E5"/>
|
|
4
|
+
<circle cx="10" cy="15" r="2" fill="#F87171"/>
|
|
5
|
+
<circle cx="16" cy="15" r="2" fill="#FBBF24"/>
|
|
6
|
+
<circle cx="22" cy="15" r="2" fill="#34D399"/>
|
|
7
|
+
<path d="M32 28C38.6274 28 44 33.3726 44 40C44 46.6274 38.6274 52 32 52C25.3726 52 20 46.6274 20 40C20 33.3726 25.3726 28 32 28Z" fill="white" fill-opacity="0.2"/>
|
|
8
|
+
<path d="M32 32C36.4183 32 40 35.5817 40 40C40 44.4183 36.4183 48 32 48C27.5817 48 24 44.4183 24 40C24 35.5817 27.5817 32 32 32Z" fill="white"/>
|
|
9
|
+
<path d="M32 37L35 40L32 43" stroke="#4F46E5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
10
|
+
<path d="M29 37L32 40L29 43" stroke="#4F46E5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" transform="rotate(180 30.5 40) translate(-3 0)"/>
|
|
11
|
+
</svg>
|