@page2ai/mcp 0.5.1 → 0.5.3
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/CHANGELOG.md +41 -0
- package/PRIVACY.md +6 -2
- package/README.md +40 -2
- package/dist/bridge/protocol.d.ts +7 -1
- package/dist/bridge/protocol.d.ts.map +1 -1
- package/dist/bridge/protocol.js +10 -0
- package/dist/bridge/protocol.js.map +1 -1
- package/dist/bridge/server.d.ts +6 -0
- package/dist/bridge/server.d.ts.map +1 -1
- package/dist/bridge/server.js +39 -11
- package/dist/bridge/server.js.map +1 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/tools/page-to-markdown.d.ts +18 -0
- package/dist/tools/page-to-markdown.d.ts.map +1 -1
- package/dist/tools/page-to-markdown.js +120 -31
- package/dist/tools/page-to-markdown.js.map +1 -1
- package/manifest.json +2 -2
- package/package.json +1 -1
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,47 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
|
|
|
5
5
|
project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html); while the
|
|
6
6
|
major version is `0`, a minor bump may carry breaking changes.
|
|
7
7
|
|
|
8
|
+
## 0.5.3 - 2026-10-23
|
|
9
|
+
|
|
10
|
+
### Security
|
|
11
|
+
|
|
12
|
+
- **Agent Bridge no longer accepts every Firefox extension.** 0.5.0 to 0.5.2 accepted any `moz-extension://` origin, because Firefox gives each install a random one; any Firefox extension could then hold the bridge, read the URLs the agent asks for and write what the agent reads. A Firefox install is now accepted only by its exact origin listed in `PAGE2AI_BRIDGE_ORIGINS`, and the server's log names the variable when it refuses one.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **The server exits when its client closes stdin.** Since 0.5.0 the bridge's listening socket kept the process running after that (0.5.2 was still running 6 s later), holding port 3025, so the client's next instance started without the bridge. CI now checks it (`scripts/stdin-exit.mjs`).
|
|
17
|
+
- **An empty `html` is an error.** With a `url`, `html: ""` fetched the URL instead and a blank `html` converted to an empty page; both now answer `Invalid input: html: empty`.
|
|
18
|
+
- **`url` must be http(s).** zod's `url()` accepted `javascript:`, `file:` and `data:` addresses; the fetch refused them, but as the base of `html` they resolved links and filled `source`.
|
|
19
|
+
- **Bridge failures are classified by their protocol code**, not by words in the message: only `TIMEOUT` ends the call without the server's own fetch. 0.5.2 matched "timed out", so a tab that failed to load was not retried by fetch.
|
|
20
|
+
- **A bridge result without Markdown** is refused (`BAD_RESULT`) and answered by the server's fetch instead of returning an empty text.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **Markdown made from caller-supplied HTML says so**: its frontmatter adds `provenance: "HTML supplied by the caller; the source URL was not fetched"`, so it does not pass for a fetch of `source`.
|
|
25
|
+
- **Through Agent Bridge, a redirect to another host is reported** in a second text block naming the page the browser ended on (a sign-in page, most often).
|
|
26
|
+
- PRIVACY.md describes Agent Bridge (pages open in the user's own browser; content stays on the machine and goes to the MCP client); SECURITY.md supports 0.5.x and states what the bridge's origin check does and does not stop.
|
|
27
|
+
|
|
28
|
+
### Hosted endpoint (remote 0.1.3)
|
|
29
|
+
|
|
30
|
+
- `html` with a `url` is refused instead of being dropped (the reviewed contract has no `html`, and parsing stripped it, so the caller got a fetch of `url`). The production smoke now also pins the whole `inputSchema` JSON, `title` and `annotations` as served on 2026-10-23.
|
|
31
|
+
|
|
32
|
+
## 0.5.2 - 2026-10-23
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- **`url` and `html` together**: the HTML is converted and the URL is its address, never fetched; relative links and image sources resolve against it, and it fills the frontmatter's `source`. Up to 0.5.1 the pair was refused, so links in HTML from a browser tool stayed relative.
|
|
37
|
+
- **The tool description is accurate again.** 0.5.0 dropped the sentences saying the server reads only the page (no Markdown copy of a docs page) and why tab groups are split, and said a connected extension converts every page; it now says when Agent Bridge is used and that a page the extension declines is fetched by the server. Through the bridge the extension's own output settings apply, and `include_images` / `include_frontmatter` say so.
|
|
38
|
+
- **The hosted endpoint keeps the tool contract reviewed for the ChatGPT plugin**: `pageToMarkdownHostedDefinition` and `pageToMarkdownHostedInputSchema` are the 0.4.1 definition (a URL only, no Agent Bridge, which a hosted server never reaches). 0.5.0 had changed what ChatGPT runs by changing the shared definition; a test now pins the hosted one.
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- **A refusing extension no longer breaks `page_to_markdown`.** With the Agent Bridge connected, any extension error was returned as the tool's answer. Now a refusal (no site access, a sign-in page, a non-web URL) or a failed tab falls back to the server's own fetch, as with no extension, and the answer says why; a bridge timeout is still an error, so the caller's time is not spent twice.
|
|
43
|
+
- **A request no longer waits for its timeout when the extension disconnects**: it fails at once with `EXTENSION_DISCONNECTED`.
|
|
44
|
+
|
|
45
|
+
### Security
|
|
46
|
+
|
|
47
|
+
- **The bridge accepts Page2AI only**: its Chrome Web Store origin, any `moz-extension://` (Firefox gives each install a random UUID), and the exact origins in `PAGE2AI_BRIDGE_ORIGINS` (an unpacked build). Before, any installed extension could open the connection without a permission, read every URL the agent asked for and write what the agent read.
|
|
48
|
+
|
|
8
49
|
## 0.5.1 - 2026-10-22
|
|
9
50
|
|
|
10
51
|
### Fixed
|
package/PRIVACY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Page2AI MCP Server — Privacy Policy
|
|
2
2
|
|
|
3
|
-
**Last updated:** 2026-10-
|
|
3
|
+
**Last updated:** 2026-10-23
|
|
4
4
|
**Applies to:** `@page2ai/mcp` npm package v0.1.0+
|
|
5
5
|
|
|
6
6
|
## What data this MCP server collects
|
|
@@ -31,7 +31,11 @@ The MCP client (Claude Desktop, Cursor, etc.) that invokes `@page2ai/mcp` may lo
|
|
|
31
31
|
|
|
32
32
|
## Authentication
|
|
33
33
|
|
|
34
|
-
`@page2ai/mcp` has no authentication mechanism because it has no user accounts or state.
|
|
34
|
+
`@page2ai/mcp` has no authentication mechanism because it has no user accounts or state. By itself it cannot access content behind login walls or authenticated sessions: it fetches without cookies.
|
|
35
|
+
|
|
36
|
+
## Agent Bridge (since 0.5.0)
|
|
37
|
+
|
|
38
|
+
The server also listens on `ws://127.0.0.1:3025`, on this computer only, for the Page2AI browser extension. When you turn on Agent Bridge in the extension, a page the agent asks for opens in a background tab of your own browser, with your cookies, so its Markdown can include content you are signed in to; that Markdown goes from the extension to this server over the local connection and from here to your MCP client, like any other result. Nothing goes to Page2AI's author or to any server of theirs. Turning the switch off ends it and removes the extension's site access.
|
|
35
39
|
|
|
36
40
|
## Source code and audit
|
|
37
41
|
|
package/README.md
CHANGED
|
@@ -160,7 +160,30 @@ command per client and a verification step.
|
|
|
160
160
|
|
|
161
161
|
| Tool | Description | Read-only | Example |
|
|
162
162
|
|------|-------------|-----------|---------|
|
|
163
|
-
| `page_to_markdown` | Fetch a web page URL and convert to clean Markdown | ✅ | `page_to_markdown(url="https://docs.anthropic.com/en/api/messages")` |
|
|
163
|
+
| `page_to_markdown` | Fetch a web page URL, or take its HTML, and convert to clean Markdown | ✅ | `page_to_markdown(url="https://docs.anthropic.com/en/api/messages")` |
|
|
164
|
+
|
|
165
|
+
### Pages behind a sign-in or rendered by JavaScript
|
|
166
|
+
|
|
167
|
+
The server fetches without cookies and runs no page scripts. For a page that needs either:
|
|
168
|
+
|
|
169
|
+
- **Pass the HTML you already have** (from Playwright, Chrome DevTools MCP or another browser
|
|
170
|
+
tool) as `html`, with the page's `url`: the HTML is converted, the URL is never fetched, and
|
|
171
|
+
relative links resolve against it. The frontmatter then carries `provenance: "HTML supplied by
|
|
172
|
+
the caller; the source URL was not fetched"`. An empty `html` is an error, not a fetch of `url`.
|
|
173
|
+
- **Or turn on Agent Bridge** in the Page2AI extension (1.6.0 or later): popup → *Agent Bridge*
|
|
174
|
+
switch → allow access to websites. While the extension is connected, `url` pages open in a
|
|
175
|
+
background tab of your own browser, with its cookies and scripts, and come back as the
|
|
176
|
+
extension's Markdown (its own output settings apply, not `include_images` /
|
|
177
|
+
`include_frontmatter`). The extension refuses sign-in providers' pages and anything that is
|
|
178
|
+
not http(s); a page it refuses is fetched by the server as usual, with a second text block
|
|
179
|
+
saying why; when the browser ends on another host (a sign-in redirect), a second text block
|
|
180
|
+
names the page it got. The bridge listens on `ws://127.0.0.1:3025` only. In Chrome it accepts
|
|
181
|
+
the Page2AI extension alone (its Web Store ID). Firefox gives each install a random origin, so
|
|
182
|
+
a Firefox install is accepted only by its exact origin listed in `PAGE2AI_BRIDGE_ORIGINS`
|
|
183
|
+
(Firefox shows it at `about:debugging#/runtime/this-firefox` as the extension's Internal UUID:
|
|
184
|
+
`moz-extension://<that UUID>`); 0.5.0 to 0.5.2 accepted any Firefox extension. The origin check
|
|
185
|
+
stops web pages and other browser extensions; another program on the same computer can claim
|
|
186
|
+
any origin, so the bridge is no stronger than your user account.
|
|
164
187
|
|
|
165
188
|
## Example prompts
|
|
166
189
|
|
|
@@ -219,11 +242,22 @@ No setup required — every argument has a sensible default. `page_to_markdown`
|
|
|
219
242
|
|
|
220
243
|
| Argument | Type | Default | Effect |
|
|
221
244
|
|---|---|---|---|
|
|
222
|
-
| `url` | string
|
|
245
|
+
| `url` | string | — | Absolute http(s) URL to convert. Private ranges, loopback and cloud metadata endpoints are blocked (SSRF guard). With `html`, not fetched: the base for relative links |
|
|
246
|
+
| `html` | string (≤ 10 MB) | — | The page's HTML, converted instead of a fetch. `url` or `html` is required |
|
|
223
247
|
| `include_images` | boolean | `true` | Keep `` image references in the output |
|
|
224
248
|
| `include_frontmatter` | boolean | `true` | Prepend the YAML front matter block |
|
|
225
249
|
| `timeout_ms` | integer | `15000` | Abort the fetch after N ms (1000–60000) |
|
|
226
250
|
|
|
251
|
+
The hosted endpoint takes `url` only (the contract its ChatGPT plugin was reviewed with) and
|
|
252
|
+
caps `timeout_ms` at 20000.
|
|
253
|
+
|
|
254
|
+
Environment variables of the local server, both optional:
|
|
255
|
+
|
|
256
|
+
| Variable | Default | Effect |
|
|
257
|
+
|---|---|---|
|
|
258
|
+
| `PAGE2AI_BRIDGE_PORT` | `3025` | Port of the Agent Bridge on 127.0.0.1. If it is taken (another running instance), the server starts without the bridge |
|
|
259
|
+
| `PAGE2AI_BRIDGE_ORIGINS` | — | Exact extension origins the bridge accepts besides Page2AI's Web Store ID, comma-separated: a Firefox install's `moz-extension://<uuid>`, an unpacked build's `chrome-extension://<id>` |
|
|
260
|
+
|
|
227
261
|
Planned: a `profile` argument (`auto | docs | marketing | research | dashboard | wordpress-marketing`).
|
|
228
262
|
|
|
229
263
|
## Privacy
|
|
@@ -233,6 +267,10 @@ beyond the URLs you explicitly provide. Note that your AI client (Claude, ChatGP
|
|
|
233
267
|
still sends the extracted Markdown to its own model provider under that client's privacy
|
|
234
268
|
policy — that part is outside this server's control. See [PRIVACY.md](./PRIVACY.md) for details.
|
|
235
269
|
|
|
270
|
+
With Agent Bridge on, the extension sends this server the URL's Markdown as your browser
|
|
271
|
+
renders it, signed-in content included, over a WebSocket on 127.0.0.1; nothing else leaves
|
|
272
|
+
the machine, but the Markdown then goes to your AI client like any other result.
|
|
273
|
+
|
|
236
274
|
The optional **hosted endpoint** is different by nature: URLs you ask it to convert are
|
|
237
275
|
fetched from a server operated by the author (deployed on Vercel), and Vercel keeps its
|
|
238
276
|
standard operational logs. No accounts, no tracking. If that trade-off matters for your
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export declare const BRIDGE_PROTOCOL = "page2ai-bridge/1";
|
|
2
2
|
export declare const DEFAULT_BRIDGE_PORT = 3025;
|
|
3
|
+
/** A bridge failure with the protocol's code (TIMEOUT, PERMISSION_REQUIRED, ...).
|
|
4
|
+
* Callers decide by `code`; the message is for people and may change. */
|
|
5
|
+
export declare class BridgeError extends Error {
|
|
6
|
+
readonly code: string;
|
|
7
|
+
constructor(code: string, message: string);
|
|
8
|
+
}
|
|
3
9
|
export interface HelloMessage {
|
|
4
10
|
type: 'hello';
|
|
5
11
|
protocol: typeof BRIDGE_PROTOCOL;
|
|
@@ -24,7 +30,7 @@ export interface ConvertErrorMessage {
|
|
|
24
30
|
type: 'error';
|
|
25
31
|
id: string;
|
|
26
32
|
token: string;
|
|
27
|
-
code: 'TAB_LOAD_FAILED' | 'EXTRACTION_FAILED' | 'TIMEOUT' | 'RESTRICTED_URL';
|
|
33
|
+
code: 'TAB_LOAD_FAILED' | 'EXTRACTION_FAILED' | 'TIMEOUT' | 'RESTRICTED_URL' | 'PERMISSION_REQUIRED';
|
|
28
34
|
message: string;
|
|
29
35
|
}
|
|
30
36
|
export type ExtensionMessage = HelloMessage | PingMessage | ConvertResultMessage | ConvertErrorMessage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/bridge/protocol.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,eAAe,qBAAqB,CAAC;AAClD,eAAO,MAAM,mBAAmB,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/bridge/protocol.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,eAAe,qBAAqB,CAAC;AAClD,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAExC;0EAC0E;AAC1E,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1C;AAID,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,OAAO,eAAe,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IAGd,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,SAAS,GAAG,gBAAgB,GAAG,qBAAqB,CAAC;IACrG,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,WAAW,GAAG,oBAAoB,GAAG,mBAAmB,CAAC;AAIvG,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,OAAO,eAAe,CAAC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,SAAS,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE;QACP,aAAa,EAAE,OAAO,CAAC;QACvB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,kBAAkB,GAAG,qBAAqB,GAAG,WAAW,CAAC;AAItG,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,OAAO,CAAC;IACvB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
package/dist/bridge/protocol.js
CHANGED
|
@@ -3,4 +3,14 @@
|
|
|
3
3
|
// Security model: BrowserTools MCP v2.0 (post GHSA-xvrv-w8pg-f25f).
|
|
4
4
|
export const BRIDGE_PROTOCOL = 'page2ai-bridge/1';
|
|
5
5
|
export const DEFAULT_BRIDGE_PORT = 3025;
|
|
6
|
+
/** A bridge failure with the protocol's code (TIMEOUT, PERMISSION_REQUIRED, ...).
|
|
7
|
+
* Callers decide by `code`; the message is for people and may change. */
|
|
8
|
+
export class BridgeError extends Error {
|
|
9
|
+
code;
|
|
10
|
+
constructor(code, message) {
|
|
11
|
+
super(`${code}: ${message}`);
|
|
12
|
+
this.name = 'BridgeError';
|
|
13
|
+
this.code = code;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
6
16
|
//# sourceMappingURL=protocol.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/bridge/protocol.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,wDAAwD;AACxD,oEAAoE;AAEpE,MAAM,CAAC,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAClD,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC"}
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/bridge/protocol.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,wDAAwD;AACxD,oEAAoE;AAEpE,MAAM,CAAC,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAClD,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAExC;0EAC0E;AAC1E,MAAM,OAAO,WAAY,SAAQ,KAAK;IAC3B,IAAI,CAAS;IAEtB,YAAY,IAAY,EAAE,OAAe;QACvC,KAAK,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
|
package/dist/bridge/server.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { type BridgeConvertOptions, type BridgeConvertResult } from './protocol.js';
|
|
2
|
+
/** Page2AI's Chrome Web Store ID: Chrome, Edge and the other Chromium browsers
|
|
3
|
+
* that install from the store send this Origin. */
|
|
4
|
+
export declare const PAGE2AI_CHROME_ORIGIN = "chrome-extension://dlpaaijcnbbmlfeohlphjpnbbcnomnno";
|
|
2
5
|
export declare class BridgeServer {
|
|
3
6
|
private httpServer;
|
|
4
7
|
private wss;
|
|
@@ -9,10 +12,13 @@ export declare class BridgeServer {
|
|
|
9
12
|
private readonly port;
|
|
10
13
|
private readonly serverVersion;
|
|
11
14
|
private readonly log;
|
|
15
|
+
private readonly extraOrigins;
|
|
12
16
|
constructor(opts: {
|
|
13
17
|
port?: number;
|
|
14
18
|
serverVersion: string;
|
|
15
19
|
log: (msg: string) => void;
|
|
20
|
+
/** Exact origins accepted beside the store's, e.g. an unpacked build's. */
|
|
21
|
+
extraOrigins?: readonly string[];
|
|
16
22
|
});
|
|
17
23
|
isConnected(): boolean;
|
|
18
24
|
start(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/bridge/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/bridge/server.ts"],"names":[],"mappings":"AAcA,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EAMzB,MAAM,eAAe,CAAC;AAEvB;oDACoD;AACpD,eAAO,MAAM,qBAAqB,wDAAwD,CAAC;AAQ3F,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,GAAG,CAAgC;IAC3C,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAwB;IAC5C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;gBAErC,IAAI,EAAE;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC3B,2EAA2E;QAC3E,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KAClC;IAOD,WAAW,IAAI,OAAO;IAIhB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAqDtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBrB,OAAO,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA4BvE,OAAO,CAAC,aAAa;IA2BrB,OAAO,CAAC,gBAAgB;CAqHzB"}
|
package/dist/bridge/server.js
CHANGED
|
@@ -4,13 +4,17 @@
|
|
|
4
4
|
//
|
|
5
5
|
// Security model (BrowserTools MCP v2.0, post GHSA-xvrv-w8pg-f25f):
|
|
6
6
|
// 1. Bind 127.0.0.1 only (never 0.0.0.0)
|
|
7
|
-
// 2. Origin validation:
|
|
7
|
+
// 2. Origin validation: Page2AI's Chrome Web Store origin, and the exact origins
|
|
8
|
+
// in PAGE2AI_BRIDGE_ORIGINS (a Firefox install, an unpacked build)
|
|
8
9
|
// 3. Host header: must be 127.0.0.1:PORT or localhost:PORT
|
|
9
10
|
// 4. Per-session token: generated on auth, required on every message
|
|
10
11
|
import { createServer } from 'node:http';
|
|
11
12
|
import { randomBytes } from 'node:crypto';
|
|
12
13
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
13
|
-
import { BRIDGE_PROTOCOL, DEFAULT_BRIDGE_PORT, } from './protocol.js';
|
|
14
|
+
import { BRIDGE_PROTOCOL, BridgeError, DEFAULT_BRIDGE_PORT, } from './protocol.js';
|
|
15
|
+
/** Page2AI's Chrome Web Store ID: Chrome, Edge and the other Chromium browsers
|
|
16
|
+
* that install from the store send this Origin. */
|
|
17
|
+
export const PAGE2AI_CHROME_ORIGIN = 'chrome-extension://dlpaaijcnbbmlfeohlphjpnbbcnomnno';
|
|
14
18
|
export class BridgeServer {
|
|
15
19
|
httpServer = null;
|
|
16
20
|
wss = null;
|
|
@@ -21,10 +25,12 @@ export class BridgeServer {
|
|
|
21
25
|
port;
|
|
22
26
|
serverVersion;
|
|
23
27
|
log;
|
|
28
|
+
extraOrigins;
|
|
24
29
|
constructor(opts) {
|
|
25
30
|
this.port = opts.port ?? DEFAULT_BRIDGE_PORT;
|
|
26
31
|
this.serverVersion = opts.serverVersion;
|
|
27
32
|
this.log = opts.log;
|
|
33
|
+
this.extraOrigins = opts.extraOrigins ?? [];
|
|
28
34
|
}
|
|
29
35
|
isConnected() {
|
|
30
36
|
return this.client !== null && this.client.readyState === WebSocket.OPEN;
|
|
@@ -80,7 +86,7 @@ export class BridgeServer {
|
|
|
80
86
|
async stop() {
|
|
81
87
|
for (const [id, req] of this.pending) {
|
|
82
88
|
clearTimeout(req.timer);
|
|
83
|
-
req.reject(new
|
|
89
|
+
req.reject(new BridgeError('SHUTTING_DOWN', 'the bridge is shutting down'));
|
|
84
90
|
this.pending.delete(id);
|
|
85
91
|
}
|
|
86
92
|
this.client?.close();
|
|
@@ -99,7 +105,7 @@ export class BridgeServer {
|
|
|
99
105
|
}
|
|
100
106
|
async convert(opts) {
|
|
101
107
|
if (!this.isConnected()) {
|
|
102
|
-
throw new
|
|
108
|
+
throw new BridgeError('NOT_CONNECTED', 'the extension is not connected');
|
|
103
109
|
}
|
|
104
110
|
const id = `req_${Date.now()}_${++this.requestCounter}`;
|
|
105
111
|
const msg = {
|
|
@@ -115,16 +121,26 @@ export class BridgeServer {
|
|
|
115
121
|
return new Promise((resolve, reject) => {
|
|
116
122
|
const timer = setTimeout(() => {
|
|
117
123
|
this.pending.delete(id);
|
|
118
|
-
reject(new
|
|
124
|
+
reject(new BridgeError('TIMEOUT', `Bridge request timed out after ${opts.timeoutMs}ms`));
|
|
119
125
|
}, opts.timeoutMs + 5000);
|
|
120
126
|
this.pending.set(id, { resolve, reject, timer });
|
|
121
127
|
this.client.send(JSON.stringify(msg));
|
|
122
128
|
});
|
|
123
129
|
}
|
|
124
130
|
verifyUpgrade(req) {
|
|
131
|
+
// Page2AI only. Any installed extension may open a WebSocket to localhost
|
|
132
|
+
// without a permission, and the one holding this single connection reads
|
|
133
|
+
// every URL the agent asks for and writes what the agent then reads.
|
|
134
|
+
// Firefox gives each install a random moz-extension:// UUID, so a scheme
|
|
135
|
+
// check would admit every Firefox extension (0.5.0-0.5.2 did): a Firefox
|
|
136
|
+
// install is admitted only by its exact origin in PAGE2AI_BRIDGE_ORIGINS,
|
|
137
|
+
// which the Page2AI popup shows there.
|
|
125
138
|
const origin = req.headers.origin ?? '';
|
|
126
|
-
if (
|
|
127
|
-
|
|
139
|
+
if (origin !== PAGE2AI_CHROME_ORIGIN && !this.extraOrigins.includes(origin)) {
|
|
140
|
+
const hint = origin.startsWith('moz-extension://')
|
|
141
|
+
? `; if this is Page2AI in Firefox, add PAGE2AI_BRIDGE_ORIGINS=${origin} to this server's environment`
|
|
142
|
+
: '';
|
|
143
|
+
this.log(`bridge: rejected Origin "${origin}"${hint}`);
|
|
128
144
|
return false;
|
|
129
145
|
}
|
|
130
146
|
const host = req.headers.host ?? '';
|
|
@@ -201,11 +217,17 @@ export class BridgeServer {
|
|
|
201
217
|
if (pending) {
|
|
202
218
|
clearTimeout(pending.timer);
|
|
203
219
|
this.pending.delete(msg.id);
|
|
220
|
+
// The agent gets this text as the page: a result without it is an
|
|
221
|
+
// extension fault, answered like any other refusal.
|
|
222
|
+
if (typeof msg.markdown !== 'string') {
|
|
223
|
+
pending.reject(new BridgeError('BAD_RESULT', 'the extension sent a result without Markdown'));
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
204
226
|
pending.resolve({
|
|
205
227
|
markdown: msg.markdown,
|
|
206
|
-
title: msg.title,
|
|
207
|
-
url: msg.url,
|
|
208
|
-
charCount: msg.
|
|
228
|
+
title: typeof msg.title === 'string' ? msg.title : '',
|
|
229
|
+
url: typeof msg.url === 'string' ? msg.url : '',
|
|
230
|
+
charCount: msg.markdown.length,
|
|
209
231
|
});
|
|
210
232
|
}
|
|
211
233
|
return;
|
|
@@ -215,7 +237,7 @@ export class BridgeServer {
|
|
|
215
237
|
if (pending) {
|
|
216
238
|
clearTimeout(pending.timer);
|
|
217
239
|
this.pending.delete(msg.id);
|
|
218
|
-
pending.reject(new
|
|
240
|
+
pending.reject(new BridgeError(String(msg.code), String(msg.message)));
|
|
219
241
|
}
|
|
220
242
|
return;
|
|
221
243
|
}
|
|
@@ -226,6 +248,12 @@ export class BridgeServer {
|
|
|
226
248
|
this.client = null;
|
|
227
249
|
this.sessionToken = null;
|
|
228
250
|
this.log('bridge: extension disconnected');
|
|
251
|
+
// Its requests can no longer be answered: fail them now, not at their timeout.
|
|
252
|
+
for (const [id, req] of this.pending) {
|
|
253
|
+
clearTimeout(req.timer);
|
|
254
|
+
req.reject(new BridgeError('EXTENSION_DISCONNECTED', 'the extension closed the connection'));
|
|
255
|
+
this.pending.delete(id);
|
|
256
|
+
}
|
|
229
257
|
}
|
|
230
258
|
});
|
|
231
259
|
ws.on('error', (err) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/bridge/server.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,0EAA0E;AAC1E,+DAA+D;AAC/D,EAAE;AACF,oEAAoE;AACpE,2CAA2C;AAC3C
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/bridge/server.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -89,7 +89,11 @@ const isServerless = !!(process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NA
|
|
|
89
89
|
let bridge = null;
|
|
90
90
|
if (!isServerless) {
|
|
91
91
|
const bridgePort = parseInt(process.env.PAGE2AI_BRIDGE_PORT ?? '', 10) || DEFAULT_BRIDGE_PORT;
|
|
92
|
-
|
|
92
|
+
// An unpacked or self-built extension has its own ID: list its exact origin(s),
|
|
93
|
+
// comma-separated, e.g. PAGE2AI_BRIDGE_ORIGINS=chrome-extension://<id>.
|
|
94
|
+
const extraOrigins = (process.env.PAGE2AI_BRIDGE_ORIGINS ?? '')
|
|
95
|
+
.split(',').map((s) => s.trim()).filter(Boolean);
|
|
96
|
+
bridge = new BridgeServer({ port: bridgePort, serverVersion: SERVER_VERSION, log, extraOrigins });
|
|
93
97
|
setBridge(bridge);
|
|
94
98
|
bridge.start().then(() => log(`bridge ready on ws://127.0.0.1:${bridgePort}`), (err) => {
|
|
95
99
|
log(`bridge failed to start: ${err instanceof Error ? err.message : String(err)} (the server continues without it)`);
|
|
@@ -98,7 +102,11 @@ if (!isServerless) {
|
|
|
98
102
|
});
|
|
99
103
|
}
|
|
100
104
|
log(`server ${SERVER_VERSION} ready on stdio (tools: page_to_markdown)`);
|
|
105
|
+
let shuttingDown = false;
|
|
101
106
|
const shutdown = (signal) => {
|
|
107
|
+
if (shuttingDown)
|
|
108
|
+
return;
|
|
109
|
+
shuttingDown = true;
|
|
102
110
|
log(`${signal} received, closing`);
|
|
103
111
|
const bridgeStop = bridge ? bridge.stop() : Promise.resolve();
|
|
104
112
|
Promise.all([handle.close(), bridgeStop]).then(() => process.exit(0), (err) => {
|
|
@@ -108,4 +116,9 @@ const shutdown = (signal) => {
|
|
|
108
116
|
};
|
|
109
117
|
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
110
118
|
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
119
|
+
// A client ends a stdio session by closing stdin (the MCP spec's first shutdown step).
|
|
120
|
+
// The bridge's listening socket would keep this process alive after that, holding the
|
|
121
|
+
// port against the client's next instance: 0.5.0-0.5.2 were still running 6 s later.
|
|
122
|
+
process.stdin.once('end', () => shutdown('stdin closed'));
|
|
123
|
+
process.stdin.once('close', () => shutdown('stdin closed'));
|
|
111
124
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,yCAAyC;AACzC,+EAA+E;AAC/E,4EAA4E;AAC5E,2EAA2E;AAC3E,EAAE;AACF,8EAA8E;AAC9E,6EAA6E;AAC7E,gFAAgF;AAChF,+EAA+E;AAC/E,8EAA8E;AAC9E,2EAA2E;AAC3E,qEAAqE;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxG,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,WAAW,GAAG,aAAa,CAAC;AAElC,mEAAmE;AACnE,EAAE;AACF,mFAAmF;AACnF,8FAA8F;AAC9F,2FAA2F;AAC3F,4FAA4F;AAC5F,EAAE;AACF,2FAA2F;AAC3F,0FAA0F;AAC1F,oDAAoD;AACpD,MAAM,cAAc,GAAG,CAAC,GAAW,EAAE;IACnC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3E,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,eAAe,CAAC;IAC9E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,eAAe,CAAC;IACzB,CAAC;AACH,CAAC,CAAC,EAAE,CAAC;AAEL,+EAA+E;AAC/E,2EAA2E;AAC3E,MAAM,GAAG,GAAG,CAAC,GAAW,EAAQ,EAAE;IAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,WAAW,KAAK,GAAG,IAAI,CAAC,CAAC;AACpD,CAAC,CAAC;AAEF,iFAAiF;AACjF,0EAA0E;AAC1E,MAAM,WAAW,GAAG,GAAW,EAAE;IAC/B,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,cAAc;QACvB,KAAK,EAAE,SAAS;QAChB,WAAW,EACT,4EAA4E;YAC5E,qFAAqF;YACrF,+BAA+B;QACjC,UAAU,EAAE,4CAA4C;KACzD,EACD;QACE,YAAY,EAAE;YACZ,KAAK,EAAE,EAAE;SACV;KACF,CACF,CAAC;IAEF,MAAM,CAAC,iBAAiB,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QAClD,KAAK,EAAE,CAAC,wBAAwB,CAAC;KAClC,CAAC,CAAC,CAAC;IAEJ,MAAM,CAAC,iBAAiB,CAAC,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACvD,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;QACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC;QAChD,IAAI,QAAQ,KAAK,kBAAkB,EAAE,CAAC;YACpC,OAAO,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QACxC,CAAC;QACD,OAAO;YACL,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,iBAAiB,QAAQ,EAAE,EAAE,CAAC;SACxE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,EAAE;IACrC,2EAA2E;IAC3E,6EAA6E;IAC7E,MAAM,EAAE,OAAO;IACf,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,oBAAoB,GAAG,CAAC,OAAO,EAAE,CAAC;CACzD,CAAC,CAAC;AAEH,uEAAuE;AACvE,+EAA+E;AAC/E,yEAAyE;AACzE,gEAAgE;AAChE,MAAM,YAAY,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;AACpF,IAAI,MAAM,GAAwB,IAAI,CAAC;AAEvC,IAAI,CAAC,YAAY,EAAE,CAAC;IAClB,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,mBAAmB,CAAC;IAC9F,MAAM,GAAG,IAAI,YAAY,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -54,6 +54,24 @@ export declare const pageToMarkdownDefinition: {
|
|
|
54
54
|
destructiveHint: boolean;
|
|
55
55
|
};
|
|
56
56
|
};
|
|
57
|
+
export declare const pageToMarkdownHostedInputSchema: z.ZodObject<{
|
|
58
|
+
url: z.ZodString;
|
|
59
|
+
include_images: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
60
|
+
include_frontmatter: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
61
|
+
timeout_ms: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
62
|
+
}, z.core.$strip>;
|
|
63
|
+
export declare const pageToMarkdownHostedDefinition: {
|
|
64
|
+
name: string;
|
|
65
|
+
title: string;
|
|
66
|
+
description: string;
|
|
67
|
+
annotations: {
|
|
68
|
+
readOnlyHint: boolean;
|
|
69
|
+
openWorldHint: boolean;
|
|
70
|
+
idempotentHint: boolean;
|
|
71
|
+
destructiveHint: boolean;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
57
74
|
export declare const setBridge: (bridge: BridgeServer | null) => void;
|
|
58
75
|
export declare const handlePageToMarkdown: (rawInput: unknown) => Promise<PageToMarkdownResult>;
|
|
76
|
+
export declare const markSuppliedHtml: (markdown: string) => string;
|
|
59
77
|
//# sourceMappingURL=page-to-markdown.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-to-markdown.d.ts","sourceRoot":"","sources":["../../src/tools/page-to-markdown.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"page-to-markdown.d.ts","sourceRoot":"","sources":["../../src/tools/page-to-markdown.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAWxD,eAAO,MAAM,yBAAyB;;;;;;iBAcpC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAG5E,MAAM,MAAM,oBAAoB,GAAG,cAAc,CAAC;AAElD,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6DpC,CAAC;AAOF,eAAO,MAAM,+BAA+B;;;;;iBAa1C,CAAC;AAEH,eAAO,MAAM,8BAA8B;;;;;;;;;;CAiB1C,CAAC;AAWF,eAAO,MAAM,SAAS,GAAI,QAAQ,YAAY,GAAG,IAAI,KAAG,IAEvD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,UAAU,OAAO,KAChB,OAAO,CAAC,oBAAoB,CAsG9B,CAAC;AAoBF,eAAO,MAAM,gBAAgB,GAAI,UAAU,MAAM,KAAG,MAKnD,CAAC"}
|
|
@@ -5,54 +5,63 @@
|
|
|
5
5
|
// `### Tab: {label}` sections for docs sites).
|
|
6
6
|
import { fetchAndConvert, htmlToMarkdown, SsrfBlockedError, FetchProtectionError } from '@page2ai/core';
|
|
7
7
|
import { z } from 'zod';
|
|
8
|
+
import { BridgeError } from '../bridge/protocol.js';
|
|
9
|
+
const URL_TEXT = 'Absolute HTTP or HTTPS URL of the web page to convert. Blocks private ranges, loopback, and cloud metadata endpoints. ' +
|
|
10
|
+
'Given with `html`, it is not fetched: it names the page the HTML came from, and relative links resolve against it.';
|
|
11
|
+
const HTML_TEXT = 'Raw HTML of the page to convert instead of fetching it, e.g. from a browser tool. ' +
|
|
12
|
+
'Pass the page\'s `url` with it so relative links resolve. Either `url` or `html` is required.';
|
|
13
|
+
const BRIDGE_SETTINGS_TEXT = ' Through Agent Bridge the extension\'s own output settings apply instead.';
|
|
8
14
|
export const pageToMarkdownInputSchema = z.object({
|
|
9
|
-
url: z.string().url().optional().describe(
|
|
10
|
-
html: z.string().max(10_000_000).optional().describe(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
include_images: z.boolean().optional().default(true).describe('Include image references () in the output. Default: true.'),
|
|
14
|
-
include_frontmatter: z.boolean().optional().default(true).describe('Prepend a YAML frontmatter block with title, source URL, capture timestamp, language, description, and canonical URL. Default: true.'),
|
|
15
|
+
url: z.string().url().optional().describe(URL_TEXT),
|
|
16
|
+
html: z.string().max(10_000_000).optional().describe(HTML_TEXT),
|
|
17
|
+
include_images: z.boolean().optional().default(true).describe('Include image references () in the output. Default: true.' + BRIDGE_SETTINGS_TEXT),
|
|
18
|
+
include_frontmatter: z.boolean().optional().default(true).describe('Prepend a YAML frontmatter block with title, source URL, capture timestamp, language, description, and canonical URL. Default: true.' + BRIDGE_SETTINGS_TEXT),
|
|
15
19
|
timeout_ms: z.number().int().min(1000).max(60000).optional().default(15000).describe('Abort the fetch after N milliseconds. Range: 1000-60000. Default: 15000.'),
|
|
16
|
-
}).refine((data) => data.url || data.html, {
|
|
17
|
-
message: 'Provide
|
|
18
|
-
}).refine((data) => !(data.url && data.html), {
|
|
19
|
-
message: 'Provide either `url` or `html`, not both.',
|
|
20
|
+
}).refine((data) => data.url !== undefined || data.html !== undefined, {
|
|
21
|
+
message: 'Provide `url`, `html`, or both.',
|
|
20
22
|
});
|
|
21
23
|
export const pageToMarkdownDefinition = {
|
|
22
24
|
name: 'page_to_markdown',
|
|
23
25
|
title: 'Convert Page to Markdown',
|
|
24
|
-
description: '
|
|
25
|
-
'Provide a `url` to fetch, or `html` if you already have the page HTML from a browser tool. ' +
|
|
26
|
-
'When the Page2AI browser extension is connected via Agent Bridge, the extension converts ' +
|
|
27
|
-
'the page in the user\'s real browser (with cookies and JavaScript) and returns extension-quality Markdown. ' +
|
|
28
|
-
'Without the bridge, `url` fetches without cookies and runs no page scripts, so a page behind ' +
|
|
29
|
-
'a sign-in comes back as its login page; read those through a browser tool and pass the HTML here. ' +
|
|
26
|
+
description: 'Fetch a web page URL and convert it to clean Markdown optimized for LLM context. ' +
|
|
30
27
|
'Preserves headings, code blocks (with language hints), links, and tables; keeps the ' +
|
|
31
28
|
'main content and drops navigation, headers, footers, and sidebars. ' +
|
|
32
|
-
'
|
|
33
|
-
'
|
|
29
|
+
'It fetches without cookies and runs no page scripts, so a page behind a sign-in or one ' +
|
|
30
|
+
'rendered only by JavaScript comes back as its login page or with no content. For those, ' +
|
|
31
|
+
'pass the page HTML you already have from a browser tool as `html` (with its `url`, relative ' +
|
|
32
|
+
'links resolve and nothing is fetched), or ask the user to turn on Agent Bridge in the ' +
|
|
33
|
+
'Page2AI browser extension: while it is connected, `url` pages open in the user\'s own ' +
|
|
34
|
+
'browser with its cookies and scripts, and a page the extension declines is fetched by the ' +
|
|
35
|
+
'server, with a note saying why. ' +
|
|
36
|
+
'It requests only the URL given, as a browser opens a page, so docs sites return the page ' +
|
|
37
|
+
'itself, not a Markdown copy of it; a URL that names a Markdown or text file (a raw GitHub ' +
|
|
38
|
+
'README.md, an llms.txt) comes back as written. ' +
|
|
39
|
+
'Discovers tab groups statically and emits each panel as a `### Tab: {label}` section ' +
|
|
40
|
+
'instead of concatenating (prevents Python+TypeScript examples merging into one broken block). ' +
|
|
41
|
+
'Zero external API calls: pages are parsed locally (linkedom) or in the user\'s browser.',
|
|
34
42
|
inputSchema: {
|
|
35
43
|
type: 'object',
|
|
36
44
|
properties: {
|
|
37
45
|
url: {
|
|
38
46
|
type: 'string',
|
|
39
47
|
format: 'uri',
|
|
40
|
-
description:
|
|
48
|
+
description: URL_TEXT,
|
|
41
49
|
},
|
|
42
50
|
html: {
|
|
43
51
|
type: 'string',
|
|
44
52
|
maxLength: 10_000_000,
|
|
45
|
-
description:
|
|
53
|
+
description: HTML_TEXT,
|
|
46
54
|
},
|
|
47
55
|
include_images: {
|
|
48
56
|
type: 'boolean',
|
|
49
57
|
default: true,
|
|
50
|
-
description: 'Include image references () in the output.',
|
|
58
|
+
description: 'Include image references () in the output.' + BRIDGE_SETTINGS_TEXT,
|
|
51
59
|
},
|
|
52
60
|
include_frontmatter: {
|
|
53
61
|
type: 'boolean',
|
|
54
62
|
default: true,
|
|
55
|
-
description: 'Prepend a YAML frontmatter block with title, source URL, capture timestamp, language, description, and canonical URL.'
|
|
63
|
+
description: 'Prepend a YAML frontmatter block with title, source URL, capture timestamp, language, description, and canonical URL.' +
|
|
64
|
+
BRIDGE_SETTINGS_TEXT,
|
|
56
65
|
},
|
|
57
66
|
timeout_ms: {
|
|
58
67
|
type: 'integer',
|
|
@@ -71,6 +80,34 @@ export const pageToMarkdownDefinition = {
|
|
|
71
80
|
destructiveHint: false,
|
|
72
81
|
},
|
|
73
82
|
};
|
|
83
|
+
// The hosted endpoint (remote/) serves the contract OpenAI reviewed for the ChatGPT plugin
|
|
84
|
+
// (approved 2026-09-11). OpenAI rescans the live server and, after its automated checks,
|
|
85
|
+
// replaces a reviewed tool definition with the changed one, so editing these strings changes
|
|
86
|
+
// what ChatGPT runs. They stay the 0.4.1 definition: a URL only (no `html`) and no Agent
|
|
87
|
+
// Bridge, which a hosted server can never reach. tests/hosted-contract.test.ts pins them.
|
|
88
|
+
export const pageToMarkdownHostedInputSchema = z.object({
|
|
89
|
+
url: z.string().url().describe('Absolute HTTP or HTTPS URL of the web page to convert. Blocks private ranges, loopback, and cloud metadata endpoints.'),
|
|
90
|
+
include_images: z.boolean().optional().default(true).describe('Include image references () in the output. Default: true.'),
|
|
91
|
+
include_frontmatter: z.boolean().optional().default(true).describe('Prepend a YAML frontmatter block with title, source URL, capture timestamp, language, description, and canonical URL. Default: true.'),
|
|
92
|
+
timeout_ms: z.number().int().min(1000).max(60000).optional().default(15000).describe('Abort the fetch after N milliseconds. Range: 1000-60000. Default: 15000.'),
|
|
93
|
+
});
|
|
94
|
+
export const pageToMarkdownHostedDefinition = {
|
|
95
|
+
name: pageToMarkdownDefinition.name,
|
|
96
|
+
title: pageToMarkdownDefinition.title,
|
|
97
|
+
description: 'Fetch a web page URL and convert it to clean Markdown optimized for LLM context. ' +
|
|
98
|
+
'Preserves headings, code blocks (with language hints), links, and tables; keeps the ' +
|
|
99
|
+
'main content and drops navigation, headers, footers, and sidebars. ' +
|
|
100
|
+
'It fetches without cookies and runs no page scripts, so a page behind a sign-in or one ' +
|
|
101
|
+
'rendered only by JavaScript comes back as its login page or with no content; read those ' +
|
|
102
|
+
'through a browser tool instead. ' +
|
|
103
|
+
'It requests only the URL given, as a browser opens a page, so docs sites return the page ' +
|
|
104
|
+
'itself, not a Markdown copy of it; a URL that names a Markdown or text file (a raw GitHub ' +
|
|
105
|
+
'README.md, an llms.txt) comes back as written. ' +
|
|
106
|
+
'Discovers tab groups statically and emits each panel as a `### Tab: {label}` section ' +
|
|
107
|
+
'instead of concatenating (prevents Python+TypeScript examples merging into one broken block). ' +
|
|
108
|
+
'Zero external API calls — parses locally via linkedom.',
|
|
109
|
+
annotations: pageToMarkdownDefinition.annotations,
|
|
110
|
+
};
|
|
74
111
|
// Wraps a plain-text error into an MCP tool result with isError:true so the
|
|
75
112
|
// LLM sees the error message instead of a JSON-RPC failure.
|
|
76
113
|
const errorResult = (message) => ({
|
|
@@ -93,8 +130,19 @@ export const handlePageToMarkdown = async (rawInput) => {
|
|
|
93
130
|
: String(e);
|
|
94
131
|
return errorResult(`Invalid input: ${msg}`);
|
|
95
132
|
}
|
|
133
|
+
// zod's url() takes any scheme (javascript:, file:, data:). The fetch would refuse
|
|
134
|
+
// them, but as the base of `html` one would resolve links and fill `source` unchecked.
|
|
135
|
+
if (input.url !== undefined && !/^https?:$/.test(new URL(input.url).protocol)) {
|
|
136
|
+
return errorResult('Invalid input: url: only http:// and https:// addresses are accepted');
|
|
137
|
+
}
|
|
96
138
|
// --- Path 1: raw HTML provided → convert locally via htmlToMarkdown (mcp#1) ---
|
|
97
|
-
|
|
139
|
+
// A `url` given with it is the page's address, never fetched: relative links resolve
|
|
140
|
+
// against it and it fills the frontmatter's `source`. An empty `html` is an error, not
|
|
141
|
+
// a reason to fetch `url` instead: the caller chose the HTML route (0.5.2 fetched on "").
|
|
142
|
+
if (input.html !== undefined) {
|
|
143
|
+
if (input.html.trim() === '') {
|
|
144
|
+
return errorResult('Invalid input: html: empty. Send the page\'s HTML, or leave html out to fetch url.');
|
|
145
|
+
}
|
|
98
146
|
try {
|
|
99
147
|
const result = htmlToMarkdown(input.html, {
|
|
100
148
|
baseUrl: input.url,
|
|
@@ -102,7 +150,7 @@ export const handlePageToMarkdown = async (rawInput) => {
|
|
|
102
150
|
includeFrontmatter: input.include_frontmatter,
|
|
103
151
|
});
|
|
104
152
|
return {
|
|
105
|
-
content: [{ type: 'text', text: result.markdown }],
|
|
153
|
+
content: [{ type: 'text', text: markSuppliedHtml(result.markdown) }],
|
|
106
154
|
};
|
|
107
155
|
}
|
|
108
156
|
catch (e) {
|
|
@@ -113,6 +161,12 @@ export const handlePageToMarkdown = async (rawInput) => {
|
|
|
113
161
|
// url is guaranteed present here by the schema refinement (url || html required).
|
|
114
162
|
const url = input.url;
|
|
115
163
|
// --- Path 2: extension bridge connected → route through the user's browser ---
|
|
164
|
+
// A refusal (no site access, a sensitive or non-web URL) or a failed tab falls
|
|
165
|
+
// through to the server's own fetch, as if no extension were connected, with a
|
|
166
|
+
// note saying why; a timeout does not, since the fetch would spend the caller's
|
|
167
|
+
// time a second time. The protocol's code decides, never the message's wording
|
|
168
|
+
// (0.5.2 matched /timed out/, so a tab-load failure worded so was not retried).
|
|
169
|
+
let bridgeNote = null;
|
|
116
170
|
if (bridgeRef?.isConnected()) {
|
|
117
171
|
try {
|
|
118
172
|
const result = await bridgeRef.convert({
|
|
@@ -121,13 +175,22 @@ export const handlePageToMarkdown = async (rawInput) => {
|
|
|
121
175
|
includeFrontmatter: input.include_frontmatter,
|
|
122
176
|
timeoutMs: input.timeout_ms,
|
|
123
177
|
});
|
|
178
|
+
// A redirect to another host (a sign-in page, most often) is still a success
|
|
179
|
+
// for the browser; the agent is told which page it got.
|
|
180
|
+
const landed = hostOf(result.url);
|
|
181
|
+
const redirectNote = landed && landed !== hostOf(url)
|
|
182
|
+
? [{ type: 'text', text: `Opened in the browser, the page redirected to ${result.url}; the Markdown is of that page.` }]
|
|
183
|
+
: [];
|
|
124
184
|
return {
|
|
125
|
-
content: [{ type: 'text', text: result.markdown }],
|
|
185
|
+
content: [{ type: 'text', text: result.markdown }, ...redirectNote],
|
|
126
186
|
};
|
|
127
187
|
}
|
|
128
188
|
catch (e) {
|
|
129
189
|
const msg = e instanceof Error ? e.message : String(e);
|
|
130
|
-
|
|
190
|
+
if (e instanceof BridgeError && e.code === 'TIMEOUT')
|
|
191
|
+
return errorResult(`Bridge conversion failed: ${msg}`);
|
|
192
|
+
process.stderr.write(`[page2ai-mcp] bridge: ${msg}; fetching without the browser\n`);
|
|
193
|
+
bridgeNote = `Fetched without the browser: the Page2AI extension answered ${msg}`;
|
|
131
194
|
}
|
|
132
195
|
}
|
|
133
196
|
// --- Path 3: fetch + linkedom (current behavior) ---
|
|
@@ -138,21 +201,47 @@ export const handlePageToMarkdown = async (rawInput) => {
|
|
|
138
201
|
includeFrontmatter: input.include_frontmatter,
|
|
139
202
|
});
|
|
140
203
|
return {
|
|
141
|
-
content: [
|
|
204
|
+
content: [
|
|
205
|
+
{ type: 'text', text: result.markdown },
|
|
206
|
+
...(bridgeNote ? [{ type: 'text', text: bridgeNote }] : []),
|
|
207
|
+
],
|
|
142
208
|
};
|
|
143
209
|
}
|
|
144
210
|
catch (e) {
|
|
145
211
|
if (e instanceof SsrfBlockedError) {
|
|
146
|
-
return
|
|
212
|
+
return fetchError(`URL blocked by SSRF guard: ${e.message}`, bridgeNote);
|
|
147
213
|
}
|
|
148
214
|
if (e instanceof FetchProtectionError) {
|
|
149
|
-
return
|
|
215
|
+
return fetchError(`Fetch failed: ${e.message}`, bridgeNote);
|
|
150
216
|
}
|
|
151
217
|
if (e instanceof Error && e.name === 'AbortError') {
|
|
152
|
-
return
|
|
218
|
+
return fetchError(`Fetch timed out after ${input.timeout_ms}ms: ${url}`, bridgeNote);
|
|
153
219
|
}
|
|
154
220
|
const msg = e instanceof Error ? e.message : String(e);
|
|
155
|
-
return
|
|
221
|
+
return fetchError(`Unexpected error: ${msg}`, bridgeNote);
|
|
156
222
|
}
|
|
157
223
|
};
|
|
224
|
+
// A fetch error after a bridge refusal names both, so the agent can tell the
|
|
225
|
+
// user what to change in the extension.
|
|
226
|
+
const fetchError = (message, bridgeNote) => errorResult(bridgeNote ? `${message}\n${bridgeNote}` : message);
|
|
227
|
+
const hostOf = (url) => {
|
|
228
|
+
try {
|
|
229
|
+
return new URL(url).hostname.replace(/\.$/, '').toLowerCase();
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return '';
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
// Markdown made from the caller's HTML names `url` as its source without having fetched
|
|
236
|
+
// it, so the frontmatter says where the content came from: a copy saved beside fetched
|
|
237
|
+
// pages must not pass for one (any caller can pair any HTML with any URL).
|
|
238
|
+
const PROVENANCE_LINE = 'provenance: "HTML supplied by the caller; the source URL was not fetched"';
|
|
239
|
+
export const markSuppliedHtml = (markdown) => {
|
|
240
|
+
if (!markdown.startsWith('---\n'))
|
|
241
|
+
return markdown;
|
|
242
|
+
const end = markdown.indexOf('\n---', 4);
|
|
243
|
+
if (end < 0)
|
|
244
|
+
return markdown;
|
|
245
|
+
return `${markdown.slice(0, end)}\n${PROVENANCE_LINE}${markdown.slice(end)}`;
|
|
246
|
+
};
|
|
158
247
|
//# sourceMappingURL=page-to-markdown.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-to-markdown.js","sourceRoot":"","sources":["../../src/tools/page-to-markdown.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,+EAA+E;AAC/E,+EAA+E;AAC/E,qEAAqE;AACrE,+CAA+C;AAE/C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAExG,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"page-to-markdown.js","sourceRoot":"","sources":["../../src/tools/page-to-markdown.ts"],"names":[],"mappings":"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"}
|
package/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": "0.3",
|
|
3
3
|
"name": "page2ai-mcp",
|
|
4
4
|
"display_name": "Page2AI — Web to Markdown",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.3",
|
|
6
6
|
"description": "Convert any web page to clean Markdown for LLM context",
|
|
7
7
|
"long_description": "Page2AI MCP converts a web page URL to Markdown for AI context windows. It keeps the page's main content (its <main> or dominant <article>), preserves code blocks with language hints, converts tables and lists, and drops navigation, headers, footers and sidebars. The only network requests go to the URL you pass it and the addresses it redirects to, asked for as a browser opens a page, so docs platforms return the page itself rather than a Markdown copy of it: pure Node + linkedom, no external services, no telemetry.\n\nAgent Bridge: when the Page2AI browser extension is connected, the tool routes through the user's real browser — cookies, JavaScript rendering, extension-quality extraction — so pages behind a sign-in and JS-rendered content are within reach. Without the bridge, a page behind a sign-in or rendered only by JavaScript is out of the tool's reach; pass its HTML directly via the `html` parameter.\n\nCompanion to the Page2AI browser extension (https://github.com/igorsaevets/page2ai-extension), which converts the live page in your own browser, including pages behind a sign-in. Built on the open-source @page2ai/core library.",
|
|
8
8
|
"author": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"tools": [
|
|
45
45
|
{
|
|
46
46
|
"name": "page_to_markdown",
|
|
47
|
-
"description": "Fetch a web page URL and convert it to clean Markdown. Preserves headings, code blocks, links, and tables; keeps the main content and drops navigation, headers, footers, and sidebars.
|
|
47
|
+
"description": "Fetch a web page URL, or take HTML you already have, and convert it to clean Markdown. Preserves headings, code blocks, links, and tables; keeps the main content and drops navigation, headers, footers, and sidebars. A fetch carries no cookies and runs no page scripts; with Agent Bridge on in the Page2AI browser extension, URLs open in your own browser instead."
|
|
48
48
|
}
|
|
49
49
|
],
|
|
50
50
|
"compatibility": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@page2ai/mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"mcpName": "io.github.igorsaevets/page2ai-mcp",
|
|
5
5
|
"description": "MCP server that converts any web page URL to clean Markdown for LLM context. Processing is local and SSRF-guarded; no external APIs, no telemetry.",
|
|
6
6
|
"type": "module",
|
package/server.json
CHANGED
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
10
|
"websiteUrl": "https://github.com/igorsaevets/page2ai-mcp#readme",
|
|
11
|
-
"version": "0.5.
|
|
11
|
+
"version": "0.5.3",
|
|
12
12
|
"packages": [
|
|
13
13
|
{
|
|
14
14
|
"registryType": "npm",
|
|
15
15
|
"identifier": "@page2ai/mcp",
|
|
16
|
-
"version": "0.5.
|
|
16
|
+
"version": "0.5.3",
|
|
17
17
|
"transport": {
|
|
18
18
|
"type": "stdio"
|
|
19
19
|
}
|