@browserless.io/mcp 1.6.0 → 1.6.2
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.
- package/README.md +1 -5
- package/package.json +7 -5
- package/build/src/lib/amplitude.d.ts +0 -11
- package/build/src/lib/amplitude.js +0 -65
- package/build/src/lib/schema-fields.d.ts +0 -10
- package/build/src/lib/schema-fields.js +0 -27
- package/build/src/lib/supabase-token-patch.d.ts +0 -6
- package/build/src/lib/supabase-token-patch.js +0 -33
- package/patches/mcp-proxy+6.4.0.patch +0 -31
package/README.md
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
# Browserless MCP Server
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
[](https://lobehub.com/mcp/browserless-browserless-mcp)
|
|
6
|
-
|
|
7
|
-
</div>
|
|
3
|
+
[](https://lobehub.com/mcp/browserless-browserless-mcp)
|
|
8
4
|
|
|
9
5
|
MCP (Model Context Protocol) server for [Browserless.io](https://browserless.io) — expose the Browserless smart scraper API to LLM clients like Claude Desktop, Cursor, VS Code, and Windsurf.
|
|
10
6
|
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@browserless.io/mcp",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"description": "MCP (Model Context Protocol) server for the Browserless.io browser automation platform",
|
|
5
5
|
"author": "browserless.io",
|
|
6
6
|
"license": "SSPL-1.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/browserless/browserless-mcp.git"
|
|
10
|
+
},
|
|
7
11
|
"type": "module",
|
|
8
12
|
"main": "./build/src/index.js",
|
|
9
13
|
"types": "./build/src/index.d.ts",
|
|
@@ -45,7 +49,6 @@
|
|
|
45
49
|
"build/src/**/*.d.ts",
|
|
46
50
|
"build/src/**/*.md",
|
|
47
51
|
"bin/*",
|
|
48
|
-
"patches/**",
|
|
49
52
|
"README.md"
|
|
50
53
|
],
|
|
51
54
|
"scripts": {
|
|
@@ -59,7 +62,6 @@
|
|
|
59
62
|
"coverage": "npm run build && c8 --reporter=text --reporter=html --reporter=lcov --check-coverage --lines 80 --branches 70 --functions 80 mocha",
|
|
60
63
|
"start": "node build/src/index.js",
|
|
61
64
|
"start:http": "TRANSPORT=httpStream PORT=8080 node build/src/index.js",
|
|
62
|
-
"postinstall": "patch-package",
|
|
63
65
|
"prepack": "npm run build",
|
|
64
66
|
"prepare": "husky",
|
|
65
67
|
"prettier": "prettier '{src,test,bin,.github}/**/*.{js,ts,json,yml,yaml,md}' --log-level error --write"
|
|
@@ -74,7 +76,6 @@
|
|
|
74
76
|
"@aws-sdk/client-sqs": "^3.1053.0",
|
|
75
77
|
"fastmcp": "^4.0.0",
|
|
76
78
|
"ioredis": "^5.10.1",
|
|
77
|
-
"patch-package": "^8.0.1",
|
|
78
79
|
"ws": "^8.21.0",
|
|
79
80
|
"zod": "^4.4.3"
|
|
80
81
|
},
|
|
@@ -105,7 +106,8 @@
|
|
|
105
106
|
"mocha": {
|
|
106
107
|
"diff": "^8.0.3",
|
|
107
108
|
"minimatch": "^10.2.1",
|
|
108
|
-
"glob": "^11.0.0"
|
|
109
|
+
"glob": "^11.0.0",
|
|
110
|
+
"serialize-javascript": "^7.0.5"
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
113
|
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export declare class AmplitudeHelper {
|
|
2
|
-
private sqsClient?;
|
|
3
|
-
private queueUrl?;
|
|
4
|
-
private initialized;
|
|
5
|
-
private enabled;
|
|
6
|
-
constructor(enabled: boolean, queueUrl?: string, region?: string);
|
|
7
|
-
initialize(queueUrl: string, region: string): void;
|
|
8
|
-
send(eventName: string, sessionId: number, properties: Record<string, unknown> & {
|
|
9
|
-
token: string;
|
|
10
|
-
}): Promise<boolean>;
|
|
11
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { SQSClient, SendMessageBatchCommand, } from '@aws-sdk/client-sqs';
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
3
|
-
export class AmplitudeHelper {
|
|
4
|
-
sqsClient;
|
|
5
|
-
queueUrl;
|
|
6
|
-
initialized = false;
|
|
7
|
-
enabled;
|
|
8
|
-
constructor(enabled, queueUrl, region) {
|
|
9
|
-
this.enabled = enabled;
|
|
10
|
-
if (!this.enabled) {
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
if (queueUrl && region) {
|
|
14
|
-
this.initialize(queueUrl, region);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
initialize(queueUrl, region) {
|
|
18
|
-
if (!this.enabled || this.initialized) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
this.queueUrl = queueUrl;
|
|
22
|
-
this.sqsClient = new SQSClient({ region });
|
|
23
|
-
this.initialized = true;
|
|
24
|
-
}
|
|
25
|
-
async send(eventName, sessionId, properties) {
|
|
26
|
-
if (!this.enabled ||
|
|
27
|
-
!this.initialized ||
|
|
28
|
-
!this.sqsClient ||
|
|
29
|
-
!this.queueUrl) {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
const event = {
|
|
33
|
-
event_type: eventName,
|
|
34
|
-
session_id: sessionId,
|
|
35
|
-
time: Date.now(),
|
|
36
|
-
event_properties: properties,
|
|
37
|
-
};
|
|
38
|
-
const entry = {
|
|
39
|
-
Id: randomUUID(),
|
|
40
|
-
MessageBody: JSON.stringify(event),
|
|
41
|
-
};
|
|
42
|
-
let retries = 3;
|
|
43
|
-
while (retries-- > 0) {
|
|
44
|
-
try {
|
|
45
|
-
const command = new SendMessageBatchCommand({
|
|
46
|
-
QueueUrl: this.queueUrl,
|
|
47
|
-
Entries: [entry],
|
|
48
|
-
});
|
|
49
|
-
const data = await this.sqsClient.send(command);
|
|
50
|
-
if (data.Failed?.length) {
|
|
51
|
-
if (retries === 0)
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
if (retries === 0)
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
/**
|
|
3
|
-
* Build the schema for an optional profile field. The NUL refinement protects
|
|
4
|
-
* the session-key separator used in agent-client.ts — a profile name
|
|
5
|
-
* containing NUL could otherwise collide with another key.
|
|
6
|
-
*
|
|
7
|
-
* Dependency-clean (zod only) so it can be shared by the server tools and the
|
|
8
|
-
* published `@browserless.io/mcp/schemas` surface without pulling in fastmcp.
|
|
9
|
-
*/
|
|
10
|
-
export declare function profileField(whenLoaded: string, extra?: string): z.ZodOptional<z.ZodString>;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
// NUL is the session-key separator (KEY_SEP) in agent-client.ts. Computed via
|
|
3
|
-
// fromCharCode so the literal control character never appears in source.
|
|
4
|
-
const NUL = String.fromCharCode(0);
|
|
5
|
-
/**
|
|
6
|
-
* Build the schema for an optional profile field. The NUL refinement protects
|
|
7
|
-
* the session-key separator used in agent-client.ts — a profile name
|
|
8
|
-
* containing NUL could otherwise collide with another key.
|
|
9
|
-
*
|
|
10
|
-
* Dependency-clean (zod only) so it can be shared by the server tools and the
|
|
11
|
-
* published `@browserless.io/mcp/schemas` surface without pulling in fastmcp.
|
|
12
|
-
*/
|
|
13
|
-
export function profileField(whenLoaded, extra = '') {
|
|
14
|
-
const description = `Optional name of an authentication profile to hydrate into the browser ${whenLoaded}. ` +
|
|
15
|
-
"The profile's cookies, localStorage, and IndexedDB are restored into the session before the request runs. " +
|
|
16
|
-
'The profile must already exist for the API token in use — create one with Browserless.saveProfile in a live agent session first.' +
|
|
17
|
-
extra;
|
|
18
|
-
return z
|
|
19
|
-
.string()
|
|
20
|
-
.trim()
|
|
21
|
-
.min(1)
|
|
22
|
-
.refine((v) => !v.includes(NUL), {
|
|
23
|
-
message: 'profile must not contain NUL characters',
|
|
24
|
-
})
|
|
25
|
-
.optional()
|
|
26
|
-
.describe(description);
|
|
27
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Patch `globalThis.fetch` to extend `expires_in` on Supabase OAuth token
|
|
3
|
-
* responses so clients don't thrash refresh against the ~60s default. Global
|
|
4
|
-
* because FastMCP's OAuthProxy has no fetch hook; matched origin/path-exact.
|
|
5
|
-
*/
|
|
6
|
-
export declare function installSupabaseTokenTtlPatch(supabaseUrl: string, ttlSeconds: number): void;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Patch `globalThis.fetch` to extend `expires_in` on Supabase OAuth token
|
|
3
|
-
* responses so clients don't thrash refresh against the ~60s default. Global
|
|
4
|
-
* because FastMCP's OAuthProxy has no fetch hook; matched origin/path-exact.
|
|
5
|
-
*/
|
|
6
|
-
export function installSupabaseTokenTtlPatch(supabaseUrl, ttlSeconds) {
|
|
7
|
-
const supabaseOrigin = new URL(supabaseUrl).origin;
|
|
8
|
-
const TOKEN_PATHNAME = '/auth/v1/oauth/token';
|
|
9
|
-
const originalFetch = globalThis.fetch;
|
|
10
|
-
globalThis.fetch = async (...args) => {
|
|
11
|
-
const response = await originalFetch(...args);
|
|
12
|
-
const url = typeof args[0] === 'string'
|
|
13
|
-
? args[0]
|
|
14
|
-
: args[0] instanceof URL
|
|
15
|
-
? args[0].toString()
|
|
16
|
-
: args[0].url;
|
|
17
|
-
const reqUrl = new URL(url);
|
|
18
|
-
if (!response.ok ||
|
|
19
|
-
reqUrl.origin !== supabaseOrigin ||
|
|
20
|
-
reqUrl.pathname !== TOKEN_PATHNAME) {
|
|
21
|
-
return response;
|
|
22
|
-
}
|
|
23
|
-
const body = (await response.json());
|
|
24
|
-
if (typeof body.expires_in === 'number' && body.expires_in < ttlSeconds) {
|
|
25
|
-
body.expires_in = ttlSeconds;
|
|
26
|
-
}
|
|
27
|
-
return new Response(JSON.stringify(body), {
|
|
28
|
-
status: response.status,
|
|
29
|
-
statusText: response.statusText,
|
|
30
|
-
headers: response.headers,
|
|
31
|
-
});
|
|
32
|
-
};
|
|
33
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
diff --git a/node_modules/mcp-proxy/dist/stdio-CyOk7u4Q.mjs b/node_modules/mcp-proxy/dist/stdio-CyOk7u4Q.mjs
|
|
2
|
-
index bfd727e..3350845 100644
|
|
3
|
-
--- a/node_modules/mcp-proxy/dist/stdio-CyOk7u4Q.mjs
|
|
4
|
-
+++ b/node_modules/mcp-proxy/dist/stdio-CyOk7u4Q.mjs
|
|
5
|
-
@@ -15479,6 +15479,10 @@ const handleStreamRequest = async ({ activeTransports, authenticate, authMiddlew
|
|
6
|
-
const sessionId = req.headers["mcp-session-id"];
|
|
7
|
-
const activeTransport = sessionId ? activeTransports[sessionId] : void 0;
|
|
8
|
-
if (!sessionId) {
|
|
9
|
-
+ if (stateless) {
|
|
10
|
-
+ res.writeHead(405, { Allow: "POST" }).end("Method Not Allowed");
|
|
11
|
-
+ return true;
|
|
12
|
-
+ }
|
|
13
|
-
res.writeHead(400).end("No sessionId");
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
diff --git a/node_modules/mcp-proxy/src/startHTTPServer.ts b/node_modules/mcp-proxy/src/startHTTPServer.ts
|
|
17
|
-
index 80e8cb7..ac051a1 100644
|
|
18
|
-
--- a/node_modules/mcp-proxy/src/startHTTPServer.ts
|
|
19
|
-
+++ b/node_modules/mcp-proxy/src/startHTTPServer.ts
|
|
20
|
-
@@ -686,6 +686,11 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
21
|
-
| undefined = sessionId ? activeTransports[sessionId] : undefined;
|
|
22
|
-
|
|
23
|
-
if (!sessionId) {
|
|
24
|
-
+ if (stateless) {
|
|
25
|
-
+ res.writeHead(405, { Allow: "POST" }).end("Method Not Allowed");
|
|
26
|
-
+
|
|
27
|
-
+ return true;
|
|
28
|
-
+ }
|
|
29
|
-
res.writeHead(400).end("No sessionId");
|
|
30
|
-
|
|
31
|
-
return true;
|