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

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.
@@ -85,70 +85,82 @@ class BrowserSessionManager {
85
85
  throw new Error(`Invalid CDP Endpoint: "${trimmedCdp}". CDP Endpoint must be a ws(s) URL (e.g. ws://localhost:9222 or wss://.../devtools/...).`);
86
86
  }
87
87
  }
88
- // Only initialize if not already done or config changed
89
- if (this.isInitialized &&
88
+ // Check checks if we are already initialized with the same config
89
+ // BUT if we are using CDP, we want to ensure we call the connect tool again
90
+ // to make sure the browser session is active (referencing user request).
91
+ const isConfigMatch = this.isInitialized &&
90
92
  this.config.mcpEndpoint === mcpEndpoint &&
91
93
  this.config.useCDP === useCDP &&
92
- this.config.cdpEndpoint === cdpEndpoint) {
94
+ this.config.cdpEndpoint === cdpEndpoint;
95
+ if (isConfigMatch && !useCDP) {
96
+ // For non-CDP (standard MCP tools), we can safely return cached tools
93
97
  return this.tools;
94
98
  }
95
- // Close existing session if config changed
96
- if (this.mcpClient) {
97
- await this.close();
98
- }
99
- // Initialize MCP client
100
- this.mcpClient = new index_js_1.Client({ name: 'n8n-browser-automation', version: '1.0.0' }, { capabilities: {} });
101
- // Determine transport based on endpoint type
102
- const isUrl = trimmedMcpEndpoint.startsWith('http://') || trimmedMcpEndpoint.startsWith('https://');
103
- let urlIsSse = false;
104
- if (isUrl) {
105
- try {
106
- urlIsSse = /(^|\/)sse\/?(\?|#|$)/i.test(mcpUrlObj.pathname);
107
- }
108
- catch (e) {
109
- // Should be caught by parseUrl above, but just in case
110
- throw new Error(`Invalid MCP Endpoint path: "${trimmedMcpEndpoint}".`);
111
- }
112
- }
113
- if (isUrl) {
114
- if (urlIsSse) {
115
- // Connect via SSE
116
- const { SSEClientTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/client/sse.js')));
117
- this.transport = new SSEClientTransport(new URL(trimmedMcpEndpoint)); // Safe because we parsed it above
118
- }
119
- else {
120
- // Connect via Streamable HTTP
121
- const { StreamableHTTPClientTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/client/streamableHttp.js')));
122
- this.transport = new StreamableHTTPClientTransport(new URL(trimmedMcpEndpoint)); // Safe because we parsed it above
99
+ // If config changed, or strict strict re-connect is needed (though we try to reuse client if endpoint is same)
100
+ if (this.config.mcpEndpoint !== mcpEndpoint || !this.mcpClient) {
101
+ // Full re-initialization needed
102
+ if (this.mcpClient) {
103
+ await this.close();
123
104
  }
105
+ this.isInitialized = false;
124
106
  }
125
107
  else {
126
- // Connect via Stdio (lazy import so environments that only use SSE don't need stdio deps)
127
- const { StdioClientTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/client/stdio.js')));
128
- this.transport = new StdioClientTransport({
129
- command: 'node',
130
- args: [mcpEndpoint],
131
- env: {
132
- ...process.env,
133
- ...(useCDP && cdpEndpoint ? { CDP_URL: cdpEndpoint } : {}),
134
- },
135
- });
108
+ // MCP endpoint is same, just need to re-run tools logic (like CDP connect)
109
+ // We can skip client creation
136
110
  }
137
- try {
138
- await this.mcpClient.connect(this.transport);
139
- // Stability delay for HTTP connections to ensure session is fully open
140
- // Remote servers often need a moment to register the session before accepting POST calls
111
+ if (!this.mcpClient) {
112
+ // Initialize MCP client
113
+ this.mcpClient = new index_js_1.Client({ name: 'n8n-browser-automation', version: '1.0.0' }, { capabilities: {} });
114
+ // Determine transport based on endpoint type
115
+ const isUrl = trimmedMcpEndpoint.startsWith('http://') || trimmedMcpEndpoint.startsWith('https://');
116
+ let urlIsSse = false;
141
117
  if (isUrl) {
142
- if (process.env.NODE_ENV !== 'production') {
143
- console.log(`[MCP] Connected to ${trimmedMcpEndpoint}. Waiting for session stabilization...`);
118
+ try {
119
+ urlIsSse = /(^|\/)sse\/?(\?|#|$)/i.test(mcpUrlObj.pathname);
120
+ }
121
+ catch (e) {
122
+ throw new Error(`Invalid MCP Endpoint path: "${trimmedMcpEndpoint}".`);
144
123
  }
145
- await new Promise(resolve => setTimeout(resolve, 2000));
146
124
  }
147
- }
148
- catch (error) {
149
- this.isInitialized = false;
150
- const transportType = isUrl ? (urlIsSse ? 'SSE' : 'Streamable HTTP') : 'Stdio';
151
- throw new Error(`Failed to connect to MCP server via ${transportType} at ${trimmedMcpEndpoint}. Error: ${error.message}`);
125
+ if (isUrl) {
126
+ if (urlIsSse) {
127
+ // Connect via SSE
128
+ const { SSEClientTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/client/sse.js')));
129
+ this.transport = new SSEClientTransport(new URL(trimmedMcpEndpoint)); // Safe because we parsed it above
130
+ }
131
+ else {
132
+ // Connect via Streamable HTTP
133
+ const { StreamableHTTPClientTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/client/streamableHttp.js')));
134
+ this.transport = new StreamableHTTPClientTransport(new URL(trimmedMcpEndpoint)); // Safe because we parsed it above
135
+ }
136
+ }
137
+ else {
138
+ // Connect via Stdio (lazy import so environments that only use SSE don't need stdio deps)
139
+ const { StdioClientTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/client/stdio.js')));
140
+ this.transport = new StdioClientTransport({
141
+ command: 'node',
142
+ args: [mcpEndpoint],
143
+ env: {
144
+ ...process.env,
145
+ ...(useCDP && cdpEndpoint ? { CDP_URL: cdpEndpoint } : {}),
146
+ },
147
+ });
148
+ }
149
+ try {
150
+ await this.mcpClient.connect(this.transport);
151
+ // Stability delay for HTTP connections to ensure session is fully open
152
+ if (isUrl) {
153
+ if (process.env.NODE_ENV !== 'production') {
154
+ console.log(`[MCP] Connected to ${trimmedMcpEndpoint}. Waiting for session stabilization...`);
155
+ }
156
+ await new Promise(resolve => setTimeout(resolve, 2000));
157
+ }
158
+ }
159
+ catch (error) {
160
+ this.isInitialized = false;
161
+ const transportType = isUrl ? (urlIsSse ? 'SSE' : 'Streamable HTTP') : 'Stdio';
162
+ throw new Error(`Failed to connect to MCP server via ${transportType} at ${trimmedMcpEndpoint}. Error: ${error.message}`);
163
+ }
152
164
  }
153
165
  this.isInitialized = true;
154
166
  this.config = { mcpEndpoint, useCDP, cdpEndpoint };
@@ -149,13 +149,17 @@ 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
+ // Filter out 'browser_connect_cdp' from the tools list if we are managing it automatically
153
+ // This prevents the AI from trying to connect manually and confusing the state
154
+ const exposedTools = mcpTools.filter((t) => t.name !== 'browser_connect_cdp');
152
155
  // Emit a debug item on the Main output so users can see endpoints and tool list in the UI
153
156
  const debugJson = {
154
157
  mcpEndpoint,
155
158
  browserMode: credentials.browserMode,
156
159
  cdpEndpoint: useCDP ? cdpUrl : undefined,
157
- toolCount: mcpTools.length,
158
- tools: mcpTools.map((t) => ({
160
+ initializationMode: 'strict_cdp_connect',
161
+ toolCount: exposedTools.length,
162
+ tools: exposedTools.map((t) => ({
159
163
  name: t.name,
160
164
  description: t.description ?? '',
161
165
  })),
@@ -164,7 +168,7 @@ class SmartBrowserAutomationTools {
164
168
  const { DynamicStructuredTool } = await importDynamicStructuredTool();
165
169
  const { Toolkit } = await importToolkitBase();
166
170
  const ctx = this;
167
- const tools = await Promise.all(mcpTools.map(async (tool) => {
171
+ const tools = await Promise.all(exposedTools.map(async (tool) => {
168
172
  const schema = await (0, jsonSchemaToZod_1.jsonSchemaToZod)(tool.inputSchema);
169
173
  const toolName = tool.name;
170
174
  const toolDescription = (tool.description ?? '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-smart-browser-automation",
3
- "version": "1.6.24",
3
+ "version": "1.6.25",
4
4
  "description": "n8n node for AI-driven browser automation using MCP",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",