n8n-nodes-smart-browser-automation 1.6.27 → 1.6.28

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.
@@ -1,6 +1,15 @@
1
1
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
2
  import { type ISupplyDataFunctions, type IExecuteFunctions } from 'n8n-workflow';
3
3
  import { type McpTool } from './McpUtils';
4
+ export type Result<T, E> = {
5
+ ok: true;
6
+ result: T;
7
+ } | {
8
+ ok: false;
9
+ error: E;
10
+ };
11
+ export declare const createResultOk: <T>(result: T) => Result<T, never>;
12
+ export declare const createResultError: <E>(error: E) => Result<never, E>;
4
13
  export interface McpConfig {
5
14
  mcpEndpoint: string;
6
15
  browserMode: string;
@@ -8,6 +17,7 @@ export interface McpConfig {
8
17
  transportType?: 'sse' | 'stdio';
9
18
  timeout?: number;
10
19
  }
20
+ export declare function connectMcpClient(config: McpConfig): Promise<Result<Client, Error>>;
11
21
  export declare function connectAndGetTools(_ctx: ISupplyDataFunctions | IExecuteFunctions, config: McpConfig): Promise<{
12
22
  client: Client;
13
23
  mcpTools: McpTool[];
@@ -1,61 +1,96 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createResultError = exports.createResultOk = void 0;
4
+ exports.connectMcpClient = connectMcpClient;
3
5
  exports.connectAndGetTools = connectAndGetTools;
4
6
  const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
5
7
  const sse_js_1 = require("@modelcontextprotocol/sdk/client/sse.js");
6
8
  const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
7
- const stdio_js_1 = require("@modelcontextprotocol/sdk/client/stdio.js");
8
- async function connectAndGetTools(_ctx, config) {
9
+ const createResultOk = (result) => ({ ok: true, result });
10
+ exports.createResultOk = createResultOk;
11
+ const createResultError = (error) => ({ ok: false, error });
12
+ exports.createResultError = createResultError;
13
+ // --- URL Helpers from read.txt ---
14
+ function safeCreateUrl(url, baseUrl) {
15
+ try {
16
+ return (0, exports.createResultOk)(new URL(url, baseUrl));
17
+ }
18
+ catch (error) {
19
+ return (0, exports.createResultError)(error);
20
+ }
21
+ }
22
+ function normalizeAndValidateUrl(input) {
23
+ // Remove invisible characters first (my addition, keeping robustness)
24
+ const cleanInput = input.replace(/[^\x20-\x7E]/g, '').trim();
25
+ const withProtocol = !/^https?:\/\//i.test(cleanInput) ? `http://${cleanInput}` : cleanInput;
26
+ const parsedUrl = safeCreateUrl(withProtocol);
27
+ if (!parsedUrl.ok) {
28
+ return (0, exports.createResultError)(parsedUrl.error);
29
+ }
30
+ return parsedUrl;
31
+ }
32
+ // --- Main Connection Logic ---
33
+ async function connectMcpClient(config) {
34
+ const endpointUrl = config.mcpEndpoint;
35
+ // Handle Stdio separately as it's not a URL
36
+ // Rough heuristic: if it looks like a command (no / or ://), it's stdio.
37
+ // But users might put "localhost:3000". `normalizeAndValidateUrl` handles localhost:3000 by adding http.
38
+ // We need a way to distinguish.
39
+ // For now, let's assume if it validates as a URL, we try URL.
40
+ // But "python main.py" is not a URL.
9
41
  let client;
10
42
  let Transport;
43
+ // User explicitly requested "only url" and "client method"
44
+ // We strictly enforce URL validation and connection.
45
+ const validUrl = normalizeAndValidateUrl(endpointUrl);
46
+ if (!validUrl.ok) {
47
+ return (0, exports.createResultError)(new Error(`Invalid MCP Endpoint URL: ${endpointUrl} (Error: ${validUrl.error.message})`));
48
+ }
49
+ const urlObj = validUrl.result;
50
+ // Detect SSE
51
+ // Logic from read.txt
52
+ const isSse = /(^|\/)sse\/?(\?|#|$)/i.test(urlObj.pathname);
53
+ client = new index_js_1.Client({ name: 'n8n-smart-browser-automation', version: '1.0.0' }, { capabilities: {} });
11
54
  try {
12
- const mcpEndpoint = config.mcpEndpoint.trim();
13
- const isUrl = mcpEndpoint.startsWith('http://') || mcpEndpoint.startsWith('https://');
14
- if (isUrl) {
15
- // Check for SSE
16
- let urlIsSse = false;
17
- try {
18
- const url = new URL(mcpEndpoint);
19
- urlIsSse = /(^|\/)sse\/?(\?|#|$)/i.test(url.pathname);
20
- }
21
- catch (e) {
22
- return {
23
- client: null,
24
- mcpTools: [],
25
- error: { error: 'Invalid URL', message: `Invalid MCP Endpoint URL: ${mcpEndpoint}` }
26
- };
27
- }
28
- if (urlIsSse) {
29
- Transport = new sse_js_1.SSEClientTransport(new URL(mcpEndpoint));
30
- }
31
- else {
32
- Transport = new streamableHttp_js_1.StreamableHTTPClientTransport(new URL(mcpEndpoint));
33
- }
55
+ if (isSse) {
56
+ Transport = new sse_js_1.SSEClientTransport(urlObj, {
57
+ fetch: fetch,
58
+ });
34
59
  }
35
60
  else {
36
- // Stdio
37
- Transport = new stdio_js_1.StdioClientTransport({
38
- command: 'node',
39
- args: [mcpEndpoint],
40
- // Pass env if needed, including CDP_URL if we want the server to pick it up via env
41
- env: {
42
- ...process.env,
43
- ...(config.cdpEndpoint ? { CDP_URL: config.cdpEndpoint } : {}),
44
- },
61
+ Transport = new streamableHttp_js_1.StreamableHTTPClientTransport(urlObj, {
62
+ fetch: fetch,
45
63
  });
46
64
  }
47
- client = new index_js_1.Client({ name: 'n8n-smart-browser-automation', version: '1.0.0' }, { capabilities: {} });
48
65
  await client.connect(Transport);
49
- // List tools
66
+ return (0, exports.createResultOk)(client);
67
+ }
68
+ catch (e) {
69
+ return (0, exports.createResultError)(new Error(`Connection Failed (${isSse ? 'SSE' : 'HTTP'}): ${e.message}`));
70
+ }
71
+ }
72
+ async function connectAndGetTools(_ctx, config) {
73
+ const connResult = await connectMcpClient(config);
74
+ if (!connResult.ok) {
75
+ return {
76
+ client: null,
77
+ mcpTools: [],
78
+ error: { error: 'Connection Failed', message: connResult.error.message }
79
+ };
80
+ }
81
+ const client = connResult.result;
82
+ try {
50
83
  const result = await client.listTools();
51
84
  const tools = (result.tools || []);
52
85
  return { client, mcpTools: tools };
53
86
  }
54
- catch (err) {
87
+ catch (e) {
88
+ // Don't forget to close if listing fails
89
+ await client.close();
55
90
  return {
56
91
  client: null,
57
92
  mcpTools: [],
58
- error: { error: 'Connection Failed', message: err.message || String(err) }
93
+ error: { error: 'List Tools Failed', message: e.message }
59
94
  };
60
95
  }
61
96
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-smart-browser-automation",
3
- "version": "1.6.27",
3
+ "version": "1.6.28",
4
4
  "description": "n8n node for AI-driven browser automation using MCP",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",