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
|
|
8
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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 (
|
|
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: '
|
|
93
|
+
error: { error: 'List Tools Failed', message: e.message }
|
|
59
94
|
};
|
|
60
95
|
}
|
|
61
96
|
}
|