@fouradata/mcp 0.3.0 → 0.3.1

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 CHANGED
@@ -1,10 +1,10 @@
1
1
  # @fouradata/mcp
2
2
 
3
- [FourA Web Scraping API](https://foura.ai/) as three [Model Context Protocol](https://modelcontextprotocol.io) tools plus five built-in workflow prompts. Plug it into Claude Desktop, Claude Code, Cursor, Windsurf, or any other MCP client and fetch arbitrary public web pages, bypass anti-bot challenges, and render JavaScript-heavy sites without writing a line of integration code.
3
+ [FourA Web Scraping API](https://foura.ai/) as four [Model Context Protocol](https://modelcontextprotocol.io) tools plus six built-in workflow prompts. Plug it into Claude Desktop, Claude Code, Cursor, Windsurf, or any other MCP client and fetch arbitrary public web pages, bypass anti-bot challenges, and render JavaScript-heavy sites - without writing a line of integration code.
4
4
 
5
5
  Four tools, six prompts, one API key.
6
6
 
7
- ## Quick Start local stdio (recommended for Claude Desktop)
7
+ ## Quick Start - local stdio (recommended for Claude Desktop)
8
8
 
9
9
  Grab a key at [foura.ai/dashboard#api-keys](https://foura.ai/dashboard#api-keys) (one click, shown once on creation, format `pk_live_...`). Then drop this into your MCP client's config:
10
10
 
@@ -24,7 +24,7 @@ Grab a key at [foura.ai/dashboard#api-keys](https://foura.ai/dashboard#api-keys)
24
24
 
25
25
  > **Claude Desktop gotcha:** fully quit Claude Desktop (`Cmd+Q` on macOS) **before** editing the config file. If the app is still running, it will overwrite your edits with its in-memory config on exit.
26
26
 
27
- The npx command downloads the package on first launch (~10s) and runs it as a subprocess of your MCP client. No global install needed. Same JSON works in every major client just point it at the right file:
27
+ The npx command downloads the package on first launch (~10s) and runs it as a subprocess of your MCP client. No global install needed. Same JSON works in every major client - just point it at the right file:
28
28
 
29
29
  | Client | Where the config lives |
30
30
  |---|---|
@@ -37,7 +37,7 @@ The npx command downloads the package on first launch (~10s) and runs it as a su
37
37
 
38
38
  Restart the client and `foura_auto`, `foura_single`, `foura_proxy`, `foura_browser` show up in your tool list, plus six prompts under `/prompts`.
39
39
 
40
- ## Quick Start hosted (Streamable HTTP)
40
+ ## Quick Start - hosted (Streamable HTTP)
41
41
 
42
42
  For clients that support the Streamable HTTP transport (Cursor, Windsurf, VS Code, Claude Code with `--transport http`), point them at the hosted endpoint instead of running a local subprocess:
43
43
 
@@ -54,7 +54,7 @@ For clients that support the Streamable HTTP transport (Cursor, Windsurf, VS Cod
54
54
  }
55
55
  ```
56
56
 
57
- Current Claude Desktop builds reject the bare `url` form use the stdio config above for Claude Desktop, or bridge through `mcp-remote`:
57
+ Current Claude Desktop builds reject the bare `url` form - use the stdio config above for Claude Desktop, or bridge through `mcp-remote`:
58
58
 
59
59
  ```json
60
60
  {
@@ -69,15 +69,15 @@ Current Claude Desktop builds reject the bare `url` form — use the stdio confi
69
69
 
70
70
  ## The Tools
71
71
 
72
- `foura_auto` is the **default** give it a URL and it returns the content, picking the fetch method for you. The other three are the lower-level primitives it orchestrates; reach for them when you want explicit control.
72
+ `foura_auto` is the **default** - give it a URL and it returns the content, picking the fetch method for you. The other three are the lower-level primitives it orchestrates; reach for them when you want explicit control.
73
73
 
74
- All four are marked `readOnlyHint: true` and `openWorldHint: true` per the [MCP spec](https://modelcontextprotocol.io/specification/2025-06-18/server/tools) clients that auto-approve trusted read-only tools (Claude Desktop, Cursor in 2026) call them without a per-request confirmation modal.
74
+ All four are marked `readOnlyHint: true` and `openWorldHint: true` per the [MCP spec](https://modelcontextprotocol.io/specification/2025-06-18/server/tools) - clients that auto-approve trusted read-only tools (Claude Desktop, Cursor in 2026) call them without a per-request confirmation modal.
75
75
 
76
76
  Every response carries both human-readable text (`content`) and a typed `structuredContent` JSON object validated against the tool's `outputSchema`. Clients pass `structuredContent` to your LLM natively, skipping the re-tokenization tax on stringified JSON.
77
77
 
78
- ### `foura_auto` smart fetch (the default)
78
+ ### `foura_auto` - smart fetch (the default)
79
79
 
80
- Give a URL, get the content back. Use this first when you just want the page and don't want to choose a method. Internally it walks a cost-aware ladder a fast direct request, then a rotating proxy, then a full browser session escalating only as far as the target forces it, solving common bot challenges on the way, and cheaply replaying a warm session on repeat calls to the same host. It learns the right settings per host, so there are no `maxTries` / pool / retry knobs to tune.
80
+ Give a URL, get the content back. Use this first when you just want the page and don't want to choose a method. Internally it walks a cost-aware ladder - a fast direct request, then a rotating proxy, then a full browser session - escalating only as far as the target forces it, solving common bot challenges on the way, and cheaply replaying a warm session on repeat calls to the same host. It learns the right settings per host, so there are no `maxTries` / pool / retry knobs to tune.
81
81
 
82
82
  ```jsonc
83
83
  {
@@ -88,13 +88,13 @@ Give a URL, get the content back. Use this first when you just want the page and
88
88
  }
89
89
  ```
90
90
 
91
- The client surface is intentionally minimal: `url` (required), plus optional `method`, `headers`, `data`, `validate`, `returnSession` (default `true`), `forceProxy` (default `true`), `timeout_ms` (5000180000, default 120000), `ignoreProxies`.
91
+ The client surface is intentionally minimal: `url` (required), plus optional `method`, `headers`, `data`, `validate`, `returnSession` (default `true`), `forceProxy` (default `true`), `timeout_ms` (5000-180000, default 120000), `ignoreProxies`.
92
92
 
93
- `structuredContent` shape: `{status, headers, data, meta, session}`. `meta` is always present `{rung, solved, attempts, credits}` the trace of which rung delivered and what it cost. `session` (`{proxy, cookies, userAgent}`) is returned by default so you can replay the same session through `foura_single` / `foura_proxy` afterwards (pass `session.proxy` into their `proxy` field). Send `returnSession: false` to omit it. There is no `total_time` field on auto.
93
+ `structuredContent` shape: `{status, headers, data, meta, session}`. `meta` is always present - `{rung, solved, attempts, credits}` - the trace of which rung delivered and what it cost. `session` (`{proxy, cookies, userAgent}`) is returned by default so you can replay the same session through `foura_single` / `foura_proxy` afterwards (pass `session.proxy` into their `proxy` field). Send `returnSession: false` to omit it. There is no `total_time` field on auto.
94
94
 
95
- ### `foura_single` fast HTTP
95
+ ### `foura_single` - fast HTTP
96
96
 
97
- One HTTP request, response back. Typically 200ms2s. Use it for static pages, JSON APIs, server-rendered HTML the bread and butter of scraping. Set `unblocker: true` if the target is picky about wire-level signals.
97
+ One HTTP request, response back. Typically 200ms-2s. Use it for static pages, JSON APIs, server-rendered HTML - the bread and butter of scraping. Set `unblocker: true` if the target is picky about wire-level signals.
98
98
 
99
99
  ```jsonc
100
100
  {
@@ -104,11 +104,11 @@ One HTTP request, response back. Typically 200ms–2s. Use it for static pages,
104
104
  }
105
105
  ```
106
106
 
107
- Supports custom headers, a body, per-stage timeouts, redirect controls, JSON auto-parse, a binary-buffer mode, and built-in response validation (`validate.status.accept`, `validate.data.fail`, and so on). If `foura_single` comes back blocked status 403/429, captcha page, OR response headers `x-vercel-mitigated: challenge` / `cf-mitigated: challenge`, OR body title matches `Vercel Security Checkpoint` / `Just a moment` / `Attention Required` escalate to `foura_proxy` with `maxTries: 25-30` for these tier-1 WAFs. If the page also needs JavaScript to render, chain `foura_proxy`'s returned `proxy` ID into `foura_browser.proxy`.
107
+ Supports custom headers, a body, per-stage timeouts, redirect controls, JSON auto-parse, a binary-buffer mode, and built-in response validation (`validate.status.accept`, `validate.data.fail`, and so on). If `foura_single` comes back blocked - status 403/429, captcha page, OR response headers `x-vercel-mitigated: challenge` / `cf-mitigated: challenge`, OR body title matches `Vercel Security Checkpoint` / `Just a moment` / `Attention Required` - escalate to `foura_proxy` with `maxTries: 25-30` for these tier-1 WAFs. If the page also needs JavaScript to render, chain `foura_proxy`'s returned `proxy` ID into `foura_browser.proxy`.
108
108
 
109
109
  `structuredContent` shape: `{status, headers, data, total_time, ...}`.
110
110
 
111
- ### `foura_proxy` rotating proxies with retry
111
+ ### `foura_proxy` - rotating proxies with retry
112
112
 
113
113
  Same target shape as `foura_single`, but routed through a pool of proxies with automatic retry on failure. Per-host scoring picks the proxies most likely to succeed against this particular target, so you're not burning attempts on known-bad routes.
114
114
 
@@ -123,9 +123,9 @@ Same target shape as `foura_single`, but routed through a pool of proxies with a
123
123
  }
124
124
  ```
125
125
 
126
- Typical latency 15s. `structuredContent` adds `proxy` (the encoded ID of the proxy that succeeded pass it to `ignoreProxies` next time if it later goes bad) and `total` (outer timing including selection + retries). For tier-1 WAF challenges (Vercel Security Checkpoint, Cloudflare 'Just a moment', Akamai Bot Manager) use `maxTries: 25-30` the default 5 is sized for lightly-blocked sites. If still blocked after 30 attempts the gate is likely country / ASN allowlist (not solvable by rotation) pivot strategy. If the target needs JavaScript render, chain the returned `proxy` ID into `foura_browser.proxy` the browser then exits through the IP that already cleared the challenge for this target.
126
+ Typical latency 1-5s. `structuredContent` adds `proxy` (the encoded ID of the proxy that succeeded - pass it to `ignoreProxies` next time if it later goes bad) and `total` (outer timing including selection + retries). For tier-1 WAF challenges (Vercel Security Checkpoint, Cloudflare 'Just a moment', Akamai Bot Manager) use `maxTries: 25-30` - the default 5 is sized for lightly-blocked sites. If still blocked after 30 attempts the gate is likely country / ASN allowlist (not solvable by rotation) - pivot strategy. If the target needs JavaScript render, chain the returned `proxy` ID into `foura_browser.proxy` - the browser then exits through the IP that already cleared the challenge for this target.
127
127
 
128
- ### `foura_browser` full browser session
128
+ ### `foura_browser` - full browser session
129
129
 
130
130
  A real browser session. JavaScript runs, the DOM finishes rendering, cookies come back with the response. Use it when the page is a single-page app, when content lazy-loads after first paint, or when there's an anti-bot challenge that needs a real browser to clear.
131
131
 
@@ -137,13 +137,13 @@ A real browser session. JavaScript runs, the DOM finishes rendering, cookies com
137
137
  }
138
138
  ```
139
139
 
140
- Slowest of the three (210s) but the only tool that handles JavaScript end-to-end. `checkText` is a one-shot post-render validator (substring search on the rendered HTML AFTER navigation completes not a waiter, does not poll): if the substring is missing, the call fails with an error envelope. Useful when a page returns 200 but the actual content is missing. `unblocker` defaults to `true` the session actively solves an anti-bot / captcha challenge (Cloudflare Turnstile and similar) it meets along the way; set `unblocker: false` to render and return the page exactly as it loads, challenge page included.
140
+ Slowest of the lower-level tools (2-10s) but the only tool that handles JavaScript end-to-end. `checkText` is a one-shot post-render validator (substring search on the rendered HTML AFTER navigation completes - not a waiter, does not poll): if the substring is missing, the call fails with an error envelope. Useful when a page returns 200 but the actual content is missing. `unblocker` defaults to `true` - the session actively solves an anti-bot / captcha challenge (Cloudflare Turnstile and similar) it meets along the way; set `unblocker: false` to render and return the page exactly as it loads, challenge page included.
141
141
 
142
142
  `structuredContent` shape is intentionally different from single/proxy: `{status, headers (object, not array), body (not data), cookies (full browser cookie shape), userAgent}`.
143
143
 
144
144
  ## Built-in Prompts
145
145
 
146
- Five workflow templates surfaced under `/prompts` in your MCP client. They orchestrate one or more tools without you spelling out the steps.
146
+ Six workflow templates surfaced under `/prompts` in your MCP client. They orchestrate one or more tools without you spelling out the steps.
147
147
 
148
148
  | Prompt | Arguments | What it does |
149
149
  |---|---|---|
@@ -154,7 +154,7 @@ Five workflow templates surfaced under `/prompts` in your MCP client. They orche
154
154
  | `check_endpoint_health` | `url, expected_text?` | Single with strict validation → reachable/status/timing report |
155
155
  | `bulk_fetch_urls` | `urls` (comma-separated) | Parallel single → auto-fallback to proxy per URL → metadata only |
156
156
 
157
- Each prompt arrives as a templated user message your LLM executes with the right tools. They cost zero tokens at idle only invoked prompts enter the context window.
157
+ Each prompt arrives as a templated user message your LLM executes with the right tools. They cost zero tokens at idle - only invoked prompts enter the context window.
158
158
 
159
159
  Full recipe text + manual fallback prompts: [foura.ai/docs/mcp/recipes](https://foura.ai/docs/mcp/recipes). For the full error code list, see [foura.ai/docs/mcp/errors](https://foura.ai/docs/mcp/errors).
160
160
 
@@ -162,9 +162,9 @@ Full recipe text + manual fallback prompts: [foura.ai/docs/mcp/recipes](https://
162
162
 
163
163
  Your `Bearer` token (or the `FOURA_API_KEY` env var in stdio mode) forwards to the FourA API as `X-API-Key`. One key, all four tools.
164
164
 
165
- Keys are managed in the [dashboard](https://foura.ai/dashboard#api-keys) shown once on creation, rotate or deactivate any time. See [foura.ai/docs/getting-started/authentication](https://foura.ai/docs/getting-started/authentication) for the full key-management walkthrough.
165
+ Keys are managed in the [dashboard](https://foura.ai/dashboard#api-keys) - shown once on creation, rotate or deactivate any time. See [foura.ai/docs/getting-started/authentication](https://foura.ai/docs/getting-started/authentication) for the full key-management walkthrough.
166
166
 
167
- ## Error envelope typed contract for agent retries
167
+ ## Error envelope - typed contract for agent retries
168
168
 
169
169
  Every error (`isError: true`) carries a `structuredContent` envelope with at minimum these three fields:
170
170
 
@@ -180,27 +180,27 @@ Where the upstream returned a status, you also get `status` (HTTP code) and on r
180
180
 
181
181
  | `code` | When | Retry safe? |
182
182
  |---|---|---|
183
- | `ssrf_blocked` | Target IP in a private / reserved range (RFC 5735+6598+IPv6 reserved) | No change the URL |
184
- | `upstream_non_json` | Upstream returned malformed body | Maybe investigate |
185
- | `bad_request` (400) | Input shape rejected by FourA | No fix arguments |
186
- | `auth_failed` (401) | Key missing, invalid, or deactivated | No fix the key |
183
+ | `ssrf_blocked` | Target IP in a private / reserved range (RFC 5735+6598+IPv6 reserved) | No - change the URL |
184
+ | `upstream_non_json` | Upstream returned malformed body | Maybe - investigate |
185
+ | `bad_request` (400) | Input shape rejected by FourA | No - fix arguments |
186
+ | `auth_failed` (401) | Key missing, invalid, or deactivated | No - fix the key |
187
187
  | `forbidden` (403) | Authenticated but not allowed | No |
188
188
  | `not_found` (404) | Target / endpoint doesn't exist | No |
189
- | `rate_limited` (429) | RPM cap hit | Yes wait `retryAfter` |
190
- | `at_capacity` (503) | Concurrency cap hit | Yes wait `retryAfter` |
191
- | `service_disabled` (503) | Maintenance window | Yes wait `retryAfter` |
192
- | `service_unavailable` (503) | Generic 503 | Yes short backoff |
193
- | `upstream_error` (≥500) | Upstream 5xx | Yes exponential backoff |
189
+ | `rate_limited` (429) | RPM cap hit | Yes - wait `retryAfter` |
190
+ | `at_capacity` (503) | Concurrency cap hit | Yes - wait `retryAfter` |
191
+ | `service_disabled` (503) | Maintenance window | Yes - wait `retryAfter` |
192
+ | `service_unavailable` (503) | Generic 503 | Yes - short backoff |
193
+ | `upstream_error` (≥500) | Upstream 5xx | Yes - exponential backoff |
194
194
  | `upstream_client_error` (4xx) | Other 4xx | Usually no |
195
195
 
196
196
  LLM agents can read `code` directly for retry logic without parsing prose. Spec reference: [foura.ai/docs/api/errors](https://foura.ai/docs/api/errors).
197
197
 
198
- ## Combining the tools sticky exit IPs
198
+ ## Combining the tools - sticky exit IPs
199
199
 
200
- The three tools compose. `foura_proxy` returns the base36 ID of the exit it used. Pass that ID back into `foura_single.proxy` or `foura_browser.proxy` and the next call exits through the **same IP** same session, same fingerprint, same geo.
200
+ The lower-level tools compose. `foura_proxy` returns the base36 ID of the exit it used. Pass that ID back into `foura_single.proxy` or `foura_browser.proxy` and the next call exits through the **same IP** - same session, same fingerprint, same geo.
201
201
 
202
202
  ```jsonc
203
- // 1. Find a working exit for the target use maxTries:25-30 for tier-1 WAFs
203
+ // 1. Find a working exit for the target - use maxTries:25-30 for tier-1 WAFs
204
204
  const r = await foura_proxy({
205
205
  maxTries: 30,
206
206
  request: { method: "GET", url: "https://probe.example.com", unblocker: true }
@@ -210,17 +210,17 @@ const r = await foura_proxy({
210
210
  // 2. Reuse it for follow-up HTTP (cookies, multi-step flows)
211
211
  await foura_single({ method: "GET", url: "https://target/api", proxy: r.proxy });
212
212
 
213
- // 3. Or render JS through the same egress exits through the IP that already
213
+ // 3. Or render JS through the same egress - exits through the IP that already
214
214
  // cleared the challenge for this target, so the snapshot captures the real
215
215
  // post-challenge content instead of a challenge page.
216
216
  await foura_browser({ url: "https://target/spa", proxy: r.proxy });
217
217
  ```
218
218
 
219
- This chain is the canonical pattern for **tier-1 WAF + JavaScript-rendered targets** (Vercel Security Checkpoint, Cloudflare 'Just a moment', Akamai Bot Manager protecting SPAs). Calling `foura_browser` directly against a WAF target usually captures the challenge page the snapshot fires before the challenge's deferred reload completes. Solve via `foura_proxy` first, then chain.
219
+ This chain is the canonical pattern for **tier-1 WAF + JavaScript-rendered targets** (Vercel Security Checkpoint, Cloudflare 'Just a moment', Akamai Bot Manager protecting SPAs). Calling `foura_browser` directly against a WAF target usually captures the challenge page - the snapshot fires before the challenge's deferred reload completes. Solve via `foura_proxy` first, then chain.
220
220
 
221
221
  To rotate AWAY from a known-bad proxy on the next `foura_proxy` call, pass it as `ignoreProxies: ["4DZ3VE"]`. The `proxy` field on `foura_single` and `foura_browser` also accepts raw URLs (`http://host:port`, `socks5://...`) if you have your own list.
222
222
 
223
- ## Large responses `offload_large` (default: inline)
223
+ ## Large responses - `offload_large` (default: inline)
224
224
 
225
225
  By default (since v0.2.0), full response bodies are returned inline in `structuredContent` regardless of size. This works in every MCP client.
226
226
 
@@ -236,7 +236,7 @@ If your client supports MCP `resources/read` (and you want to save tokens on big
236
236
 
237
237
  | Client | `offload_large: true` |
238
238
  |---|---|
239
- | Claude Desktop | not yet leave default `false` |
239
+ | Claude Desktop | not yet - leave default `false` |
240
240
  | Claude Code, Cursor, Windsurf | supported |
241
241
  | VS Code MCP extension | supported |
242
242
 
@@ -251,7 +251,7 @@ Tenant-isolated: only the API key that stored a payload can read it back.
251
251
 
252
252
  ## Self-Hosting
253
253
 
254
- The MCP server runs in one container, statelessly each request brings its own key, so there's no session state, no sticky load balancing, nothing to coordinate. Scale horizontally behind any load balancer.
254
+ The MCP server runs in one container, statelessly - each request brings its own key, so there's no session state, no sticky load balancing, nothing to coordinate. Scale horizontally behind any load balancer.
255
255
 
256
256
  Configurable environment:
257
257
 
package/dist/http.js CHANGED
@@ -4,7 +4,7 @@ import { SUPPORTED_PROTOCOL_VERSIONS } from "@modelcontextprotocol/sdk/types.js"
4
4
  import { createServer } from "./server.js";
5
5
  import { withApiKey } from "./auth.js";
6
6
  const PORT = Number(process.env.PORT ?? 3076);
7
- const SERVER_VERSION = "0.3.0";
7
+ const SERVER_VERSION = "0.3.1";
8
8
  // Spec MUSTs covered in this file:
9
9
  // audit 1.1 — Origin + Host validation (CVE-2025-66414 DNS rebinding)
10
10
  // audit 1.2 — WWW-Authenticate on 401
package/dist/prompts.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  /**
3
- * MCP Prompts pre-written workflow templates the user can invoke from the
3
+ * MCP Prompts - pre-written workflow templates the user can invoke from the
4
4
  * MCP client UI (Claude Desktop / Cursor / etc) instead of figuring out the
5
5
  * tool orchestration themselves.
6
6
  *
7
- * Prompts are LAZY context they only enter the LLM's window when invoked,
7
+ * Prompts are LAZY context - they only enter the LLM's window when invoked,
8
8
  * unlike tool descriptions which are loaded on every turn. So we can be more
9
9
  * verbose here.
10
10
  */
package/dist/prompts.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { z } from "zod";
2
2
  /**
3
- * MCP Prompts pre-written workflow templates the user can invoke from the
3
+ * MCP Prompts - pre-written workflow templates the user can invoke from the
4
4
  * MCP client UI (Claude Desktop / Cursor / etc) instead of figuring out the
5
5
  * tool orchestration themselves.
6
6
  *
7
- * Prompts are LAZY context they only enter the LLM's window when invoked,
7
+ * Prompts are LAZY context - they only enter the LLM's window when invoked,
8
8
  * unlike tool descriptions which are loaded on every turn. So we can be more
9
9
  * verbose here.
10
10
  */
@@ -21,7 +21,7 @@ export function registerPrompts(server) {
21
21
  role: "user",
22
22
  content: {
23
23
  type: "text",
24
- text: `Fetch the product page at ${url} using the foura_browser tool most product pages are single-page apps and need JavaScript to render.\n\n` +
24
+ text: `Fetch the product page at ${url} using the foura_browser tool - most product pages are single-page apps and need JavaScript to render.\n\n` +
25
25
  `From the response body extract:\n` +
26
26
  `- product title\n` +
27
27
  `- price (with currency)\n` +
@@ -48,7 +48,7 @@ export function registerPrompts(server) {
48
48
  content: {
49
49
  type: "text",
50
50
  text: `Fetch ${url} using the foura_single tool with unblocker:true. Most news and blog sites are server-rendered, so HTTP is fastest (200ms-2s).\n\n` +
51
- `If foura_single returns a 403, captcha page, or empty content, retry the same URL with foura_proxy (maxTries:3) it routes through a rotating proxy pool.\n\n` +
51
+ `If foura_single returns a 403, captcha page, or empty content, retry the same URL with foura_proxy (maxTries:3) - it routes through a rotating proxy pool.\n\n` +
52
52
  `From the response, extract:\n` +
53
53
  `- headline (the main H1, not the page title bar)\n` +
54
54
  `- author byline (may be inside .author / [rel=author] / itemprop)\n` +
@@ -137,23 +137,23 @@ export function registerPrompts(server) {
137
137
  type: "text",
138
138
  text: `Parse the following comma-separated URLs and fetch each one concurrently using foura_single (unblocker:true).\n\n` +
139
139
  `URLs: ${urls}\n\n` +
140
- `For any URL that returns 403, captcha page, or empty body retry that single URL with foura_proxy (maxTries:3).\n\n` +
140
+ `For any URL that returns 403, captcha page, or empty body - retry that single URL with foura_proxy (maxTries:3).\n\n` +
141
141
  `Return a JSON array, one entry per URL in input order:\n` +
142
142
  `[{"url": "...", "status": 200, "success": true, "body_size_bytes": 0, "via": "single|proxy", "error": null}, ...]\n\n` +
143
- `Do NOT inline full response bodies in the output only metadata. If the caller needs body content, they should call foura_single individually.`,
143
+ `Do NOT inline full response bodies in the output - only metadata. If the caller needs body content, they should call foura_single individually.`,
144
144
  },
145
145
  },
146
146
  ],
147
147
  }));
148
148
  server.registerPrompt("smart_fetch", {
149
149
  title: "Fetch a URL the smart way (auto)",
150
- description: "Fetch any URL with foura_auto one call that picks the method (direct / proxy / browser), gets past common bot protection, and returns the content. Use when you just want the page and don't want to choose a tool.",
150
+ description: "Fetch any URL with foura_auto - one call that picks the method (direct / proxy / browser), gets past common bot protection, and returns the content. Use when you just want the page and don't want to choose a tool.",
151
151
  argsSchema: {
152
152
  url: z.string().describe("URL to fetch"),
153
153
  must_contain: z
154
154
  .string()
155
155
  .optional()
156
- .describe("Optional substring the real page must contain lets auto tell a real page from a challenge page on protected targets."),
156
+ .describe("Optional substring the real page must contain - lets auto tell a real page from a challenge page on protected targets."),
157
157
  extract: z
158
158
  .string()
159
159
  .optional()
@@ -165,7 +165,7 @@ export function registerPrompts(server) {
165
165
  role: "user",
166
166
  content: {
167
167
  type: "text",
168
- text: `Fetch ${url} using the foura_auto tool. It picks the fetch method for you (direct request, rotating proxy, or full browser) and gets past common bot protection automatically you do not need to choose between foura_single / foura_proxy / foura_browser.\n\n` +
168
+ text: `Fetch ${url} using the foura_auto tool. It picks the fetch method for you (direct request, rotating proxy, or full browser) and gets past common bot protection automatically - you do not need to choose between foura_single / foura_proxy / foura_browser.\n\n` +
169
169
  (must_contain
170
170
  ? `Pass validate.data.accept:["${must_contain}"] so auto keeps escalating until the real page (containing "${must_contain}") comes back, not a challenge page.\n\n`
171
171
  : `If the first response looks like a challenge / block page rather than real content, re-call with validate.data.accept:["<a string the real page must contain>"] so auto knows what success looks like.\n\n`) +
@@ -1,6 +1,6 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  /**
3
- * Resources offload large response bodies (>= THRESHOLD bytes) onto host
3
+ * Resources - offload large response bodies (>= THRESHOLD bytes) onto host
4
4
  * disk and return a MCP resource_link instead of inlining megabytes into the
5
5
  * LLM context.
6
6
  *
@@ -9,10 +9,10 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
9
  * which response field is the "large payload" (single/proxy use `data`,
10
10
  * browser uses `body`).
11
11
  *
12
- * Audit 1.5 tenant isolation. Payloads are stored under
12
+ * Audit 1.5 - tenant isolation. Payloads are stored under
13
13
  * `<PAYLOADS_DIR>/<keyhash>/<uuid>.{bin,meta.json}`, where `keyhash` is the
14
14
  * first 16 hex chars of sha256(apiKey). The resource handler validates the
15
- * caller's keyhash matches the storage path before serving any other
15
+ * caller's keyhash matches the storage path before serving - any other
16
16
  * tenant gets a `resource not found` (no leaking whether the UUID exists).
17
17
  */
18
18
  export declare const THRESHOLD_BYTES = 50000;
package/dist/resources.js CHANGED
@@ -5,7 +5,7 @@ import { tmpdir } from "node:os";
5
5
  import { ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
6
6
  import { getApiKey } from "./auth.js";
7
7
  /**
8
- * Resources offload large response bodies (>= THRESHOLD bytes) onto host
8
+ * Resources - offload large response bodies (>= THRESHOLD bytes) onto host
9
9
  * disk and return a MCP resource_link instead of inlining megabytes into the
10
10
  * LLM context.
11
11
  *
@@ -14,16 +14,16 @@ import { getApiKey } from "./auth.js";
14
14
  * which response field is the "large payload" (single/proxy use `data`,
15
15
  * browser uses `body`).
16
16
  *
17
- * Audit 1.5 tenant isolation. Payloads are stored under
17
+ * Audit 1.5 - tenant isolation. Payloads are stored under
18
18
  * `<PAYLOADS_DIR>/<keyhash>/<uuid>.{bin,meta.json}`, where `keyhash` is the
19
19
  * first 16 hex chars of sha256(apiKey). The resource handler validates the
20
- * caller's keyhash matches the storage path before serving any other
20
+ * caller's keyhash matches the storage path before serving - any other
21
21
  * tenant gets a `resource not found` (no leaking whether the UUID exists).
22
22
  */
23
23
  export const THRESHOLD_BYTES = 50_000;
24
24
  export const PAYLOADS_DIR = process.env.FOURA_MCP_PAYLOADS_DIR ?? path.join(tmpdir(), "foura-mcp-payloads");
25
25
  const URI_PREFIX = "foura-mcp://payload/";
26
- // sha256(apiKey).hex().slice(0, 16) gives 64 bits of namespacing entropy,
26
+ // sha256(apiKey).hex().slice(0, 16) - gives 64 bits of namespacing entropy,
27
27
  // short enough for a filesystem path component and unguessable for an
28
28
  // attacker who doesn't have the corresponding API key.
29
29
  export function hashApiKey(apiKey) {
@@ -100,7 +100,7 @@ export function registerResourceHandler(server) {
100
100
  throw new Error(`Payload not found: ${uuidStr}`);
101
101
  }
102
102
  const meta = JSON.parse(metaRaw);
103
- // Defense in depth even if filesystem permissions ever got bypassed,
103
+ // Defense in depth - even if filesystem permissions ever got bypassed,
104
104
  // the meta sidecar carries the keyhash; reject on mismatch.
105
105
  if (meta.keyhash !== keyhash) {
106
106
  throw new Error(`Payload not found: ${uuidStr}`);
package/dist/server.js CHANGED
@@ -8,7 +8,7 @@ import { registerPrompts } from "./prompts.js";
8
8
  export function createServer() {
9
9
  const server = new McpServer({
10
10
  name: "foura-mcp",
11
- version: "0.3.0",
11
+ version: "0.3.1",
12
12
  });
13
13
  registerSingleTool(server);
14
14
  registerProxyTool(server);
@@ -22,7 +22,7 @@ function extractContentTypeFromHeaderInfo(headers) {
22
22
  }
23
23
  // Derive a stable error code from upstream HTTP status + envelope.
24
24
  // LLM agents read `code` for retry / classify logic without parsing prose.
25
- // Same code set as the other three tools auto reuses them verbatim.
25
+ // Same code set as the other three tools - auto reuses them verbatim.
26
26
  function deriveCode(status, envelope) {
27
27
  if (status === 400)
28
28
  return "bad_request";
@@ -51,10 +51,10 @@ function deriveCode(status, envelope) {
51
51
  // /api/auto is registered on the gateway WITHOUT a trailing slash (the route is
52
52
  // an exact `POST /api/auto`), unlike /single/ /proxy/ /browser/ which keep the
53
53
  // slash. Hitting `/auto/` would miss the dedicated handler and fall through to
54
- // the transparent table so this URL must stay slash-less.
54
+ // the transparent table - so this URL must stay slash-less.
55
55
  const AUTO_API_URL = (process.env.FOURA_API_BASE ?? "https://api.foura.ai/api") + "/auto";
56
56
  // The client's success criteria, passed verbatim to auto's internal sub-calls.
57
- // Same DwValidate shape as foura_single / foura_proxy auto enforces it on
57
+ // Same DwValidate shape as foura_single / foura_proxy - auto enforces it on
58
58
  // every rung itself, so a rung that only superficially "succeeds" (e.g. a
59
59
  // challenge interstitial) is rejected and the ladder keeps climbing.
60
60
  const AutoValidateSchema = z
@@ -115,19 +115,19 @@ const AutoMetaSchema = z
115
115
  // the same session through foura_single / foura_proxy later.
116
116
  const AutoSessionSchema = z
117
117
  .object({
118
- proxy: z.string().optional().describe("Opaque base36 exit id of the session (e.g. `4DZ3VE`) pass to foura_single.proxy / foura_proxy.proxy to replay through the same exit. Never a raw IP."),
119
- cookies: z.unknown().optional().describe("Cookies accumulated by the winning session replay them on a follow-up request."),
120
- userAgent: z.string().optional().describe("User-Agent string the winning session used send the same one when replaying."),
118
+ proxy: z.string().optional().describe("Opaque base36 exit id of the session (e.g. `4DZ3VE`) - pass to foura_single.proxy / foura_proxy.proxy to replay through the same exit. Never a raw IP."),
119
+ cookies: z.unknown().optional().describe("Cookies accumulated by the winning session - replay them on a follow-up request."),
120
+ userAgent: z.string().optional().describe("User-Agent string the winning session used - send the same one when replaying."),
121
121
  })
122
122
  .catchall(z.unknown());
123
123
  const autoOutputShape = {
124
- // Success path single-shaped body so any client that renders foura_single
124
+ // Success path - single-shaped body so any client that renders foura_single
125
125
  // also renders auto. Note: NO `total_time` (auto does not surface it).
126
126
  status: z
127
127
  .number()
128
128
  .int()
129
129
  .optional()
130
- .describe("HTTP status code from the target on the rung that delivered the content. `0` indicates the whole ladder failed before any HTTP response check `error`."),
130
+ .describe("HTTP status code from the target on the rung that delivered the content. `0` indicates the whole ladder failed before any HTTP response - check `error`."),
131
131
  headers: z
132
132
  .union([z.array(HeaderInfoSchema), z.string(), z.record(z.string(), z.unknown())])
133
133
  .optional()
@@ -138,10 +138,10 @@ const autoOutputShape = {
138
138
  .describe("Decoded response body of the delivered page. String by default; object when the body parsed as JSON. Omitted when offloaded."),
139
139
  meta: AutoMetaSchema.optional().describe("Trace of what the ladder did: rung, solved, attempts, credits. Always present."),
140
140
  session: AutoSessionSchema.optional().describe("The {proxy, cookies, userAgent} triple of the winning session, for DIY replay through foura_single / foura_proxy. Present by default (send returnSession:false to omit)."),
141
- // Offload path MCP layer adds these when body >= 50KB AND offload_large=true
141
+ // Offload path - MCP layer adds these when body >= 50KB AND offload_large=true
142
142
  offloaded_resource_uri: z.string().optional().describe("foura-mcp://payload/<uuid>"),
143
143
  size_bytes: z.number().int().optional().describe("Total offloaded body size in bytes"),
144
- // Error path auto failure surfaces the failure status + message + attempts.
144
+ // Error path - auto failure surfaces the failure status + message + attempts.
145
145
  error: z.string().optional().describe("Human-readable error message when the ladder could not deliver the content within the budget."),
146
146
  attempts: z.number().optional().describe("Total sub-call attempts when the ladder failed (also present inside `meta`)."),
147
147
  service: z.enum(["single", "proxy", "browser", "api", "auto"]).optional(),
@@ -163,7 +163,7 @@ const autoInputShape = {
163
163
  url: z
164
164
  .string()
165
165
  .url()
166
- .describe("Target URL. Public hosts only private/reserved ranges (RFC 1918 10/8, 172.16/12, 192.168/16, loopback 127/8, link-local, IPv6 ULA fc00::/7, IPv6 loopback ::1, plus *.local mDNS) are refused with code `ssrf_blocked`. Example: https://example.com/page. Use {ts} anywhere in the URL to insert the current Unix timestamp for cache-bust."),
166
+ .describe("Target URL. Public hosts only - private/reserved ranges (RFC 1918 10/8, 172.16/12, 192.168/16, loopback 127/8, link-local, IPv6 ULA fc00::/7, IPv6 loopback ::1, plus *.local mDNS) are refused with code `ssrf_blocked`. Example: https://example.com/page. Use {ts} anywhere in the URL to insert the current Unix timestamp for cache-bust."),
167
167
  method: z
168
168
  .string()
169
169
  .min(1)
@@ -185,18 +185,18 @@ const autoInputShape = {
185
185
  forceProxy: z
186
186
  .boolean()
187
187
  .optional()
188
- .describe("Always reach the target through a rotating proxy, never from FourA's own egress. Default true (the target never sees FourA's origin IP). Send false to allow the cheaper direct path but note some trust-gated defenses actually resolve more easily from the direct egress, so forcing a proxy can make those targets harder (more attempts / credits)."),
188
+ .describe("Always reach the target through a rotating proxy, never from FourA's own egress. Default true (the target never sees FourA's origin IP). Send false to allow the cheaper direct path - but note some trust-gated defenses actually resolve more easily from the direct egress, so forcing a proxy can make those targets harder (more attempts / credits)."),
189
189
  timeout_ms: z
190
190
  .number()
191
191
  .int()
192
192
  .min(5_000)
193
193
  .max(180_000)
194
194
  .optional()
195
- .describe("Total time budget in ms for the WHOLE operation auto fires several internal attempts and they must all fit inside this. Default 120000, max 180000. Auto portions the budget across its attempts; it does not hand the whole budget to one attempt."),
195
+ .describe("Total time budget in ms for the WHOLE operation - auto fires several internal attempts and they must all fit inside this. Default 120000, max 180000. Auto portions the budget across its attempts; it does not hand the whole budget to one attempt."),
196
196
  ignoreProxies: z
197
197
  .array(z.string())
198
198
  .optional()
199
- .describe("Exits to AVOID base36 proxy ids (like \"4DZ3VE\") or proxy URLs. Auto skips a warm session on one of these and tells its internal proxy search to avoid them too. Use to rotate away from an exit that just got blocked."),
199
+ .describe("Exits to AVOID - base36 proxy ids (like \"4DZ3VE\") or proxy URLs. Auto skips a warm session on one of these and tells its internal proxy search to avoid them too. Use to rotate away from an exit that just got blocked."),
200
200
  followRedirects: z
201
201
  .number()
202
202
  .int()
@@ -204,7 +204,7 @@ const autoInputShape = {
204
204
  .max(20)
205
205
  .optional()
206
206
  .describe("Follow up to N redirects on the cheap (direct / proxy) rungs so a 301/302 lands on the real content instead of being returned as-is. Default 5; 0 = don't follow. The browser rung follows redirects natively."),
207
- // Bug 3 parity opt-in offload. Default false → response inline regardless of
207
+ // Bug 3 parity - opt-in offload. Default false → response inline regardless of
208
208
  // size so clients that can't read resource_link blocks still get usable output.
209
209
  offload_large: z
210
210
  .boolean()
@@ -226,7 +226,7 @@ fn) {
226
226
  const issues = parsed.error.issues.slice(0, 5).map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
227
227
  return {
228
228
  isError: true,
229
- content: [{ type: "text", text: `FourA ${service} upstream response failed schema: ${issues}` }],
229
+ content: [{ type: "text", text: `FourA ${service} - upstream response failed schema: ${issues}` }],
230
230
  structuredContent: {
231
231
  service,
232
232
  code: "output_validation_failed",
@@ -241,7 +241,7 @@ fn) {
241
241
  const msg = e instanceof Error ? e.message : String(e);
242
242
  return {
243
243
  isError: true,
244
- content: [{ type: "text", text: `FourA ${service} internal error: ${msg}` }],
244
+ content: [{ type: "text", text: `FourA ${service} - internal error: ${msg}` }],
245
245
  structuredContent: {
246
246
  service,
247
247
  code: "output_validation_failed",
@@ -252,10 +252,10 @@ fn) {
252
252
  }
253
253
  export function registerAutoTool(server) {
254
254
  server.registerTool("foura_auto", {
255
- title: "FourA auto (smart fetch, picks the method for you)",
255
+ title: "FourA - auto (smart fetch, picks the method for you)",
256
256
  description: "Give a URL, get the content back. The default first choice for any page when you just want " +
257
- "the data and don't want to decide how to fetch it. Internally it walks a cost-aware ladder a " +
258
- "fast direct request first, then a rotating proxy, then a full browser session escalating only " +
257
+ "the data and don't want to decide how to fetch it. Internally it walks a cost-aware ladder - a " +
258
+ "fast direct request first, then a rotating proxy, then a full browser session - escalating only " +
259
259
  "as far as the target forces it, solving common bot challenges (Cloudflare, and similar) on the " +
260
260
  "way, and cheaply replaying a warm session on repeat calls to the same host. It learns the right " +
261
261
  "settings per host on its own, so there are no maxTries / pool / retry knobs to tune. " +
@@ -296,7 +296,7 @@ export function registerAutoTool(server) {
296
296
  headers: {
297
297
  "X-API-Key": getApiKey(),
298
298
  "Content-Type": "application/json",
299
- "User-Agent": "foura-mcp/0.3.0 (auto)",
299
+ "User-Agent": "foura-mcp/0.3.1 (auto)",
300
300
  },
301
301
  body: JSON.stringify(upstreamBody),
302
302
  headersTimeout: 200_000,
@@ -313,7 +313,7 @@ export function registerAutoTool(server) {
313
313
  content: [
314
314
  {
315
315
  type: "text",
316
- text: `FourA auto non-JSON response (${res.statusCode}): ${text.slice(0, 200)}`,
316
+ text: `FourA auto - non-JSON response (${res.statusCode}): ${text.slice(0, 200)}`,
317
317
  },
318
318
  ],
319
319
  structuredContent: {
@@ -324,7 +324,7 @@ export function registerAutoTool(server) {
324
324
  },
325
325
  };
326
326
  }
327
- // Gateway-level error (rate limit / auth / capacity) auto itself ALWAYS
327
+ // Gateway-level error (rate limit / auth / capacity) - auto itself ALWAYS
328
328
  // replies transport-200, so a non-2xx transport status is the gateway
329
329
  // rejecting before auto ran. Surface it like the other tools do.
330
330
  if (res.statusCode < 200 || res.statusCode >= 300) {
@@ -354,7 +354,7 @@ export function registerAutoTool(server) {
354
354
  content: [
355
355
  {
356
356
  type: "text",
357
- text: `FourA auto could not deliver content (status ${innerStatus}): ${parsedObj.error}`,
357
+ text: `FourA auto - could not deliver content (status ${innerStatus}): ${parsedObj.error}`,
358
358
  },
359
359
  ],
360
360
  structuredContent: {
@@ -53,7 +53,7 @@ fn) {
53
53
  const issues = parsed.error.issues.slice(0, 5).map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
54
54
  return {
55
55
  isError: true,
56
- content: [{ type: "text", text: `FourA ${service} upstream response failed schema: ${issues}` }],
56
+ content: [{ type: "text", text: `FourA ${service} - upstream response failed schema: ${issues}` }],
57
57
  structuredContent: {
58
58
  service,
59
59
  code: "output_validation_failed",
@@ -68,7 +68,7 @@ fn) {
68
68
  const msg = e instanceof Error ? e.message : String(e);
69
69
  return {
70
70
  isError: true,
71
- content: [{ type: "text", text: `FourA ${service} internal error: ${msg}` }],
71
+ content: [{ type: "text", text: `FourA ${service} - internal error: ${msg}` }],
72
72
  structuredContent: {
73
73
  service,
74
74
  code: "output_validation_failed",
@@ -83,8 +83,8 @@ const BrowserCookieInputSchema = z.object({
83
83
  value: z.string(),
84
84
  domain: z.string().optional(),
85
85
  });
86
- // Full CDP cookie object captured from Chrome via Network.getAllCookies after navigation.
87
- // Permissive Protocol.Network.Cookie has more fields than we model, and CDP versions vary.
86
+ // Full CDP cookie object - captured from Chrome via Network.getAllCookies after navigation.
87
+ // Permissive - Protocol.Network.Cookie has more fields than we model, and CDP versions vary.
88
88
  const CdpCookieSchema = z
89
89
  .object({
90
90
  name: z.string(),
@@ -100,10 +100,10 @@ const CdpCookieSchema = z
100
100
  })
101
101
  .catchall(z.unknown());
102
102
  const browserOutputShape = {
103
- // Success dwstack browser DwResponse (packages/browser/src/schema.ts:19-26)
104
- status: z.number().int().optional().describe("HTTP status code from the target page. `0` indicates the navigation failed before any HTTP response (DNS / connection refused / timeout) check the `error` field for the underlying reason."),
103
+ // Success - dwstack browser DwResponse (packages/browser/src/schema.ts:19-26)
104
+ status: z.number().int().optional().describe("HTTP status code from the target page. `0` indicates the navigation failed before any HTTP response (DNS / connection refused / timeout) - check the `error` field for the underlying reason."),
105
105
  // CDP headers (Protocol.Network.Headers) are a Record but values can be
106
- // string OR string[] in some CDP versions. Bug 11 fix permissive.
106
+ // string OR string[] in some CDP versions. Bug 11 fix - permissive.
107
107
  headers: z
108
108
  .record(z.string(), z.unknown())
109
109
  .optional()
@@ -115,7 +115,7 @@ const browserOutputShape = {
115
115
  .describe("Fully-rendered page content. String HTML when content-type is HTML; object when the page returned JSON and it was auto-parsed. Field is named `body`, not `data`. Omitted when offloaded."),
116
116
  cookies: z.array(CdpCookieSchema).optional().describe("Full cookie objects collected after navigation (name, value, domain, path, expires, httpOnly, secure, session, sameSite, …)"),
117
117
  userAgent: z.string().optional().describe("The User-Agent the browser session presented"),
118
- // Offload path MCP layer adds these when body >= 50KB AND offload_large=true
118
+ // Offload path - MCP layer adds these when body >= 50KB AND offload_large=true
119
119
  offloaded_resource_uri: z.string().optional().describe("foura-mcp://payload/<uuid>"),
120
120
  size_bytes: z.number().int().optional().describe("Total offloaded body size in bytes"),
121
121
  // Error path
@@ -131,7 +131,7 @@ const browserOutputShape = {
131
131
  code: z.string().optional().describe("Stable error code for retry classification. One of: ssrf_blocked, upstream_non_json, output_validation_failed, bad_request (400), auth_failed (401), forbidden (403), not_found (404), rate_limited (429), at_capacity (503), service_disabled (503), service_unavailable (503), upstream_error (>=500), upstream_client_error (other 4xx), upstream_unknown (defensive)."),
132
132
  };
133
133
  const browserInputShape = {
134
- url: z.string().url().describe("Target URL to load in a full browser session. Public hosts only private/reserved ranges (RFC 1918 + loopback + link-local + IPv6 ULA/loopback + *.local mDNS) are refused with code `ssrf_blocked`. Example: https://shop.example.com/product/123 or any single-page-app URL."),
134
+ url: z.string().url().describe("Target URL to load in a full browser session. Public hosts only - private/reserved ranges (RFC 1918 + loopback + link-local + IPv6 ULA/loopback + *.local mDNS) are refused with code `ssrf_blocked`. Example: https://shop.example.com/product/123 or any single-page-app URL."),
135
135
  headers: z
136
136
  .record(z.string(), z.string())
137
137
  .optional()
@@ -145,7 +145,7 @@ const browserInputShape = {
145
145
  .string()
146
146
  .optional()
147
147
  .describe("Optional proxy. Three forms: (1) URL `http://user:pass@host:port` or `socks5://host:port`; " +
148
- "(2) base36 ID from foura_proxy (e.g. `4DZ3VE`) same pool exit IP; (3) omit → fixed container egress."),
148
+ "(2) base36 ID from foura_proxy (e.g. `4DZ3VE`) - same pool exit IP; (3) omit → fixed container egress."),
149
149
  timeout_ms: z
150
150
  .number()
151
151
  .int()
@@ -161,12 +161,12 @@ const browserInputShape = {
161
161
  checkText: z
162
162
  .string()
163
163
  .optional()
164
- .describe("One-shot post-render validator substring search on the rendered HTML AFTER navigation completes. NOT a waiter: does not poll, does not block until the substring appears. If the substring is missing, the tool returns an error envelope. Use to catch silent failures like a 200 response that captured a challenge page or empty SPA shell. Example: \"add to cart\" for product pages."),
164
+ .describe("One-shot post-render validator - substring search on the rendered HTML AFTER navigation completes. NOT a waiter: does not poll, does not block until the substring appears. If the substring is missing, the tool returns an error envelope. Use to catch silent failures like a 200 response that captured a challenge page or empty SPA shell. Example: \"add to cart\" for product pages."),
165
165
  unblocker: z
166
166
  .boolean()
167
167
  .optional()
168
- .describe("Actively SOLVE an anti-bot / captcha challenge (Cloudflare Turnstile and similar) encountered during navigation, instead of just rendering it. Default true. Set false to render and return the page exactly as it loads including a challenge page without attempting to solve; cheaper when you already know the target is open or you want the raw challenge page."),
169
- // Bug 3 fix opt-in offload, default false (inline).
168
+ .describe("Actively SOLVE an anti-bot / captcha challenge (Cloudflare Turnstile and similar) encountered during navigation, instead of just rendering it. Default true. Set false to render and return the page exactly as it loads - including a challenge page - without attempting to solve; cheaper when you already know the target is open or you want the raw challenge page."),
169
+ // Bug 3 fix - opt-in offload, default false (inline).
170
170
  offload_large: z
171
171
  .boolean()
172
172
  .optional()
@@ -174,15 +174,15 @@ const browserInputShape = {
174
174
  };
175
175
  export function registerBrowserTool(server) {
176
176
  server.registerTool("foura_browser", {
177
- title: "FourA full browser navigation",
178
- description: "Load a URL in a real browser session. JS runs, DOM renders, cookies come back. 210s. " +
177
+ title: "FourA - full browser navigation",
178
+ description: "Load a URL in a real browser session. JS runs, DOM renders, cookies come back. 2-10s. " +
179
179
  "Use for SPAs, lazy-loaded content, or JS anti-bot challenges (Cloudflare Turnstile etc.). " +
180
180
  "Prefer foura_single for static HTML; foura_proxy for static pages where your IP is blocked. " +
181
181
  "To rotate the browser's exit IP: call foura_proxy first, pass its returned `proxy` ID into " +
182
- "this tool's `proxy` field the browser exits through that pool IP. Default (no `proxy`) " +
182
+ "this tool's `proxy` field - the browser exits through that pool IP. Default (no `proxy`) " +
183
183
  "is one fixed container egress. If the target is behind a tier-1 WAF challenge (Vercel / " +
184
184
  "Cloudflare / Akamai), calling this tool directly will usually still capture the challenge " +
185
- "page rather than the post-challenge content the snapshot fires before the challenge's " +
185
+ "page rather than the post-challenge content - the snapshot fires before the challenge's " +
186
186
  "deferred reload completes. Correct chain: call foura_proxy first with maxTries:25-30 against " +
187
187
  "the same URL → take the returned proxy base36 ID → pass it into this tool's `proxy` field. " +
188
188
  "The browser then exits through the IP that already cleared the challenge for this target. " +
@@ -216,7 +216,7 @@ export function registerBrowserTool(server) {
216
216
  headers: {
217
217
  "X-API-Key": getApiKey(),
218
218
  "Content-Type": "application/json",
219
- "User-Agent": "foura-mcp/0.3.0 (browser)",
219
+ "User-Agent": "foura-mcp/0.3.1 (browser)",
220
220
  },
221
221
  body: JSON.stringify(upstreamBody),
222
222
  });
@@ -231,7 +231,7 @@ export function registerBrowserTool(server) {
231
231
  content: [
232
232
  {
233
233
  type: "text",
234
- text: `FourA browser non-JSON response (${res.statusCode}): ${text.slice(0, 200)}`,
234
+ text: `FourA browser - non-JSON response (${res.statusCode}): ${text.slice(0, 200)}`,
235
235
  },
236
236
  ],
237
237
  structuredContent: {
@@ -258,7 +258,7 @@ export function registerBrowserTool(server) {
258
258
  };
259
259
  }
260
260
  const parsedObj = parsed;
261
- // Bug 8 fix browser request.ts also rejects with DwResponseError on
261
+ // Bug 8 fix - browser request.ts also rejects with DwResponseError on
262
262
  // page failure (timeout, navigation error, checkStatus mismatch).
263
263
  // Service forwards as 200 + {error, ...} via dwRequestToExpressRes.
264
264
  if (typeof parsedObj.error === "string" && parsedObj.error.length > 0) {
@@ -268,7 +268,7 @@ export function registerBrowserTool(server) {
268
268
  content: [
269
269
  {
270
270
  type: "text",
271
- text: `FourA browser page failure (status ${innerStatus}): ${parsedObj.error}`,
271
+ text: `FourA browser - page failure (status ${innerStatus}): ${parsedObj.error}`,
272
272
  },
273
273
  ],
274
274
  structuredContent: {
@@ -58,7 +58,7 @@ fn) {
58
58
  const issues = parsed.error.issues.slice(0, 5).map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
59
59
  return {
60
60
  isError: true,
61
- content: [{ type: "text", text: `FourA ${service} upstream response failed schema: ${issues}` }],
61
+ content: [{ type: "text", text: `FourA ${service} - upstream response failed schema: ${issues}` }],
62
62
  structuredContent: {
63
63
  service,
64
64
  code: "output_validation_failed",
@@ -73,7 +73,7 @@ fn) {
73
73
  const msg = e instanceof Error ? e.message : String(e);
74
74
  return {
75
75
  isError: true,
76
- content: [{ type: "text", text: `FourA ${service} internal error: ${msg}` }],
76
+ content: [{ type: "text", text: `FourA ${service} - internal error: ${msg}` }],
77
77
  structuredContent: {
78
78
  service,
79
79
  code: "output_validation_failed",
@@ -111,7 +111,7 @@ const ProxyValidateSchema = z
111
111
  .optional(),
112
112
  })
113
113
  .optional();
114
- // Inner DwRequest matches dwstack types/src/single.ts DwRequestSchema
114
+ // Inner DwRequest - matches dwstack types/src/single.ts DwRequestSchema
115
115
  // (which is what /api/proxy/ inner.request validates against). Method is
116
116
  // permissive z.string() not enum (Bug 9). All optionals match upstream.
117
117
  const ProxyInnerRequestSchema = z
@@ -123,7 +123,7 @@ const ProxyInnerRequestSchema = z
123
123
  url: z
124
124
  .string()
125
125
  .url()
126
- .describe("Target URL the proxy should fetch. Public hosts only private/reserved ranges (RFC 1918 + loopback + link-local + IPv6 ULA/loopback + *.local mDNS) are refused with code `ssrf_blocked`. Example: https://shop.example.com/pricing for blocked sites. {ts} placeholder is replaced with current Unix timestamp."),
126
+ .describe("Target URL the proxy should fetch. Public hosts only - private/reserved ranges (RFC 1918 + loopback + link-local + IPv6 ULA/loopback + *.local mDNS) are refused with code `ssrf_blocked`. Example: https://shop.example.com/pricing for blocked sites. {ts} placeholder is replaced with current Unix timestamp."),
127
127
  headers: z
128
128
  .array(z.tuple([z.string(), z.string()]))
129
129
  .optional()
@@ -131,7 +131,7 @@ const ProxyInnerRequestSchema = z
131
131
  unblocker: z
132
132
  .boolean()
133
133
  .optional()
134
- .describe("Inject realistic browser headers (User-Agent, Sec-Ch-Ua, Accept-Encoding, …) and make the request look like it's coming from a real browser at the wire level. Default false STRONGLY recommended on proxy paths since most sites that need a proxy also have wire-level anti-bot (Cloudflare, Akamai, PerimeterX, Datadome). Cheap to leave on for production scrapes."),
134
+ .describe("Inject realistic browser headers (User-Agent, Sec-Ch-Ua, Accept-Encoding, …) and make the request look like it's coming from a real browser at the wire level. Default false - STRONGLY recommended on proxy paths since most sites that need a proxy also have wire-level anti-bot (Cloudflare, Akamai, PerimeterX, Datadome). Cheap to leave on for production scrapes."),
135
135
  data: z
136
136
  .union([z.string(), z.record(z.string(), z.unknown())])
137
137
  .optional()
@@ -147,8 +147,8 @@ const ProxyInnerRequestSchema = z
147
147
  validate: ProxyValidateSchema,
148
148
  })
149
149
  .describe("The inner HTTP request to send through each proxy attempt. Validation rules here determine when a proxy is treated as failed and retried.");
150
- // HeaderInfo[] same as single. Multi-value headers (Set-Cookie, Link)
151
- // come as string OR string[] from node-libcurl Bug 1 fix.
150
+ // HeaderInfo[] - same as single. Multi-value headers (Set-Cookie, Link)
151
+ // come as string OR string[] from node-libcurl - Bug 1 fix.
152
152
  const ProxyHeaderInfoSchema = z
153
153
  .object({
154
154
  result: z
@@ -161,17 +161,17 @@ const ProxyHeaderInfoSchema = z
161
161
  })
162
162
  .catchall(z.union([z.string(), z.array(z.string())]));
163
163
  const proxyOutputShape = {
164
- // Success PrResponse = DwResponse + {proxy, total}. The backend type
164
+ // Success - PrResponse = DwResponse + {proxy, total}. The backend type
165
165
  // also has an optional `proxyId` (number), but the api gateway encodes it
166
166
  // into the `proxy` string and strips it from the response. We don't
167
- // surface it in outputSchema zod silently drops any stray copy.
168
- status: z.number().int().optional().describe("HTTP status code from the target (from the succeeding proxy attempt). `0` indicates every attempt failed before any HTTP response (DNS / connection refused / timeout) check the `error` field for the underlying reason."),
167
+ // surface it in outputSchema - zod silently drops any stray copy.
168
+ status: z.number().int().optional().describe("HTTP status code from the target (from the succeeding proxy attempt). `0` indicates every attempt failed before any HTTP response (DNS / connection refused / timeout) - check the `error` field for the underlying reason."),
169
169
  headers: z
170
170
  .union([z.array(ProxyHeaderInfoSchema), z.string(), z.record(z.string(), z.unknown())])
171
171
  .optional()
172
172
  .describe("Response headers per redirect hop, as an array of objects. Each entry has `result.{version, code, reason}` plus arbitrary header-name keys whose values are strings (or arrays of strings for multi-value headers like Set-Cookie / Link)."),
173
173
  data: z.unknown().optional().describe("Decoded response body. Omitted when offloaded."),
174
- // total_time can be string | number | null per dwstack types Bug 6 fix.
174
+ // total_time can be string | number | null per dwstack types - Bug 6 fix.
175
175
  total_time: z
176
176
  .union([z.number(), z.string(), z.null()])
177
177
  .optional()
@@ -186,10 +186,10 @@ const proxyOutputShape = {
186
186
  .number()
187
187
  .optional()
188
188
  .describe("Outer total time in seconds (proxy selection + retries + the successful inner attempt). Float."),
189
- // Offload path MCP layer adds these when body >= 50KB AND offload_large=true
189
+ // Offload path - MCP layer adds these when body >= 50KB AND offload_large=true
190
190
  offloaded_resource_uri: z.string().optional().describe("foura-mcp://payload/<uuid>"),
191
191
  size_bytes: z.number().int().optional().describe("Total offloaded body size in bytes"),
192
- // Error path includes PrResponseError shape: {error, request, total} (no status, no headers, no data)
192
+ // Error path - includes PrResponseError shape: {error, request, total} (no status, no headers, no data)
193
193
  error: z.string().optional().describe("Human-readable error message"),
194
194
  service: z.enum(["single", "proxy", "browser", "api"]).optional(),
195
195
  retryAfter: z.number().optional(),
@@ -204,7 +204,7 @@ const proxyOutputShape = {
204
204
  };
205
205
  const proxyInputShape = {
206
206
  request: ProxyInnerRequestSchema,
207
- // PrRequestSchema.timeout_ms is `.positive()` in upstream 0 is invalid here
207
+ // PrRequestSchema.timeout_ms is `.positive()` in upstream - 0 is invalid here
208
208
  // (different from single's .min(0)).
209
209
  maxTries: z
210
210
  .number()
@@ -212,7 +212,7 @@ const proxyInputShape = {
212
212
  .min(1)
213
213
  .max(90)
214
214
  .optional()
215
- .describe("Maximum proxy rotation attempts before giving up (default 5, max 90). Default 5 is sized for lightly-blocked sites. Raise to 25-30 for tier-1 WAF challenges (Vercel Security Checkpoint, Cloudflare 'Just a moment', Akamai Bot Manager) most rotations on these targets need this range. If still blocked after 30 attempts, the gate is likely country / ASN allowlist (not solvable by rotation) pivot strategy instead of climbing to 60."),
215
+ .describe("Maximum proxy rotation attempts before giving up (default 5, max 90). Default 5 is sized for lightly-blocked sites. Raise to 25-30 for tier-1 WAF challenges (Vercel Security Checkpoint, Cloudflare 'Just a moment', Akamai Bot Manager) - most rotations on these targets need this range. If still blocked after 30 attempts, the gate is likely country / ASN allowlist (not solvable by rotation) - pivot strategy instead of climbing to 60."),
216
216
  timeout_ms: z
217
217
  .number()
218
218
  .int()
@@ -224,7 +224,7 @@ const proxyInputShape = {
224
224
  .array(z.string())
225
225
  .optional()
226
226
  .describe("Encoded proxy IDs (base36 strings like \"4DZ3VE\") OR proxy URLs to exclude from rotation. Both forms are accepted."),
227
- // Bug 3 fix opt-in offload, default false (inline).
227
+ // Bug 3 fix - opt-in offload, default false (inline).
228
228
  offload_large: z
229
229
  .boolean()
230
230
  .optional()
@@ -232,18 +232,18 @@ const proxyInputShape = {
232
232
  };
233
233
  export function registerProxyTool(server) {
234
234
  server.registerTool("foura_proxy", {
235
- title: "FourA HTTP request via rotating proxies",
235
+ title: "FourA - HTTP request via rotating proxies",
236
236
  description: "Route an HTTP request through FourA's proxy pool with automatic retry across multiple proxies. " +
237
237
  "Per-host proxy rating picks proxies most likely to succeed for the target. Use when foura_single " +
238
- "returns 403, captcha, or geo-blocked content. Typical latency 15s. The response includes the " +
239
- "encoded proxy ID that succeeded ('proxy' field) reuse it in foura_single.proxy or " +
238
+ "returns 403, captcha, or geo-blocked content. Typical latency 1-5s. The response includes the " +
239
+ "encoded proxy ID that succeeded ('proxy' field) - reuse it in foura_single.proxy or " +
240
240
  "foura_browser.proxy to pin follow-up requests to the same exit IP, or pass it in ignoreProxies " +
241
241
  "to skip this exit on the next rotation. Escalate to foura_browser if all proxies fail or the " +
242
242
  "page needs JavaScript rendering. When the trigger is a tier-1 WAF challenge (Vercel Security " +
243
- "Checkpoint, Cloudflare 'Just a moment', Akamai Bot Manager), set maxTries to 25-30 the default " +
243
+ "Checkpoint, Cloudflare 'Just a moment', Akamai Bot Manager), set maxTries to 25-30 - the default " +
244
244
  "5 will usually be too low for these targets. Distinguish from country / ASN allowlist denials " +
245
245
  "(country-licensed bookmakers, government sites): there rotation never helps regardless of " +
246
- "maxTries after ~30 failed attempts on the same block, stop climbing and pivot strategy instead.",
246
+ "maxTries - after ~30 failed attempts on the same block, stop climbing and pivot strategy instead.",
247
247
  inputSchema: proxyInputShape,
248
248
  outputSchema: proxyOutputShape,
249
249
  annotations: {
@@ -271,7 +271,7 @@ export function registerProxyTool(server) {
271
271
  headers: {
272
272
  "X-API-Key": getApiKey(),
273
273
  "Content-Type": "application/json",
274
- "User-Agent": "foura-mcp/0.3.0 (proxy)",
274
+ "User-Agent": "foura-mcp/0.3.1 (proxy)",
275
275
  },
276
276
  body: JSON.stringify(upstreamBody),
277
277
  });
@@ -286,7 +286,7 @@ export function registerProxyTool(server) {
286
286
  content: [
287
287
  {
288
288
  type: "text",
289
- text: `FourA proxy non-JSON response (${res.statusCode}): ${text.slice(0, 200)}`,
289
+ text: `FourA proxy - non-JSON response (${res.statusCode}): ${text.slice(0, 200)}`,
290
290
  },
291
291
  ],
292
292
  structuredContent: {
@@ -313,7 +313,7 @@ export function registerProxyTool(server) {
313
313
  };
314
314
  }
315
315
  const parsedObj = parsed;
316
- // Bug 8 fix all-proxies-fail returns HTTP 200 + PrResponseError shape
316
+ // Bug 8 fix - all-proxies-fail returns HTTP 200 + PrResponseError shape
317
317
  // {error, request, total}. dwstack proxy/src/api/request.ts:43
318
318
  // forwards without overriding the response status. Without this check,
319
319
  // foura-mcp would silently return "success" with a missing-data body.
@@ -324,7 +324,7 @@ export function registerProxyTool(server) {
324
324
  content: [
325
325
  {
326
326
  type: "text",
327
- text: `FourA proxy all attempts failed: ${parsedObj.error}`,
327
+ text: `FourA proxy - all attempts failed: ${parsedObj.error}`,
328
328
  },
329
329
  ],
330
330
  structuredContent: {
@@ -79,10 +79,10 @@ const SingleValidateSchema = z
79
79
  .optional(),
80
80
  })
81
81
  .optional();
82
- // HeaderInfo[] one entry per response in the redirect chain. `result` holds
82
+ // HeaderInfo[] - one entry per response in the redirect chain. `result` holds
83
83
  // the HTTP status line; all other keys are response header name → value pairs.
84
84
  // Multi-value headers (Set-Cookie, Link, WWW-Authenticate, etc.) come as
85
- // `string | string[]` from node-libcurl HeaderInfo Bug 1 fix.
85
+ // `string | string[]` from node-libcurl HeaderInfo - Bug 1 fix.
86
86
  const HeaderInfoSchema = z
87
87
  .object({
88
88
  result: z
@@ -95,9 +95,9 @@ const HeaderInfoSchema = z
95
95
  })
96
96
  .catchall(z.union([z.string(), z.array(z.string())]));
97
97
  const singleOutputShape = {
98
- // Success path matches dwstack DwResponse (packages/types/src/single.ts:47-57)
99
- status: z.number().int().optional().describe("HTTP status code from the target. `0` indicates the request failed before any HTTP response (DNS failure, connection refused, timeout) check the `error` field for the underlying reason."),
100
- // `Buffer | HeaderInfo[]` Buffer when raw mode is requested upstream
98
+ // Success path - matches dwstack DwResponse (packages/types/src/single.ts:47-57)
99
+ status: z.number().int().optional().describe("HTTP status code from the target. `0` indicates the request failed before any HTTP response (DNS failure, connection refused, timeout) - check the `error` field for the underlying reason."),
100
+ // `Buffer | HeaderInfo[]` - Buffer when raw mode is requested upstream
101
101
  // (we don't expose that mode but accept it permissively). Array entries
102
102
  // hold the libcurl HeaderInfo shape with multi-value header support.
103
103
  headers: z
@@ -107,16 +107,16 @@ const singleOutputShape = {
107
107
  data: z
108
108
  .unknown()
109
109
  .optional()
110
- .describe("Decoded response body. String by default; object when tryJsonData=true and the body parsed as JSON; serialized Buffer JSON shape (`{type:\"Buffer\", data:[byte, ...]}`, bytes 0-255) when returnBuffer=true reconstruct with `Buffer.from(data.data)` in Node, `new Uint8Array(data.data)` elsewhere. Omitted when offloaded."),
111
- // total_time can be string | number | null per dwstack types/src/single.ts:52 Bug 6 fix.
110
+ .describe("Decoded response body. String by default; object when tryJsonData=true and the body parsed as JSON; serialized Buffer JSON shape (`{type:\"Buffer\", data:[byte, ...]}`, bytes 0-255) when returnBuffer=true - reconstruct with `Buffer.from(data.data)` in Node, `new Uint8Array(data.data)` elsewhere. Omitted when offloaded."),
111
+ // total_time can be string | number | null per dwstack types/src/single.ts:52 - Bug 6 fix.
112
112
  total_time: z
113
113
  .union([z.number(), z.string(), z.null()])
114
114
  .optional()
115
115
  .describe("Wall-clock request duration in seconds. Number when present; string in some variants; null when the request never started."),
116
- // Offload path MCP layer adds these when body >= 50KB AND offload_large=true
116
+ // Offload path - MCP layer adds these when body >= 50KB AND offload_large=true
117
117
  offloaded_resource_uri: z.string().optional().describe("foura-mcp://payload/<uuid>"),
118
118
  size_bytes: z.number().int().optional().describe("Total offloaded body size in bytes"),
119
- // Error path common envelope across all FourA services (see foura.ai/docs/api/errors)
119
+ // Error path - common envelope across all FourA services (see foura.ai/docs/api/errors)
120
120
  error: z.string().optional().describe("Human-readable error message"),
121
121
  service: z.enum(["single", "proxy", "browser", "api"]).optional(),
122
122
  retryAfter: z.number().optional().describe("Seconds to wait before retrying (429/503)"),
@@ -131,14 +131,14 @@ const singleOutputShape = {
131
131
  code: z.string().optional().describe("Stable error code for retry classification. One of: ssrf_blocked, upstream_non_json, output_validation_failed, bad_request (400), auth_failed (401), forbidden (403), not_found (404), rate_limited (429), at_capacity (503), service_disabled (503), service_unavailable (503), upstream_error (>=500), upstream_client_error (other 4xx), upstream_unknown (defensive)."),
132
132
  };
133
133
  const singleInputShape = {
134
- // method is z.string() in upstream (packages/types/src/single.ts:29) Bug 9.
134
+ // method is z.string() in upstream (packages/types/src/single.ts:29) - Bug 9.
135
135
  // We don't restrict to a closed enum; libcurl accepts arbitrary verbs
136
136
  // (PROPFIND, MKCOL, REPORT, etc.) used by WebDAV / OData / GraphQL clients.
137
137
  method: z
138
138
  .string()
139
139
  .min(1)
140
140
  .describe("HTTP method (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, or any WebDAV verb like PROPFIND/MKCOL)"),
141
- url: z.string().url().describe("Target URL. Public hosts only private/reserved ranges (RFC 1918 10/8, 172.16/12, 192.168/16, loopback 127/8, link-local, IPv6 ULA fc00::/7, IPv6 loopback ::1, plus *.local mDNS) are refused with code `ssrf_blocked`. Example: https://example.com/page or https://api.example.com/v1/users. Use {ts} anywhere in the URL to insert current Unix timestamp for cache-bust."),
141
+ url: z.string().url().describe("Target URL. Public hosts only - private/reserved ranges (RFC 1918 10/8, 172.16/12, 192.168/16, loopback 127/8, link-local, IPv6 ULA fc00::/7, IPv6 loopback ::1, plus *.local mDNS) are refused with code `ssrf_blocked`. Example: https://example.com/page or https://api.example.com/v1/users. Use {ts} anywhere in the URL to insert current Unix timestamp for cache-bust."),
142
142
  headers: z
143
143
  .array(z.tuple([z.string(), z.string()]))
144
144
  .optional()
@@ -146,7 +146,7 @@ const singleInputShape = {
146
146
  unblocker: z
147
147
  .boolean()
148
148
  .optional()
149
- .describe("Inject realistic browser headers (User-Agent, Sec-Ch-Ua, Accept-Encoding, …) and make the request look like it's coming from a real browser at the wire level. Default false set true for any target with anti-bot or WAF (Cloudflare, Akamai, PerimeterX, Datadome). Cheap to leave on for production scrapes."),
149
+ .describe("Inject realistic browser headers (User-Agent, Sec-Ch-Ua, Accept-Encoding, …) and make the request look like it's coming from a real browser at the wire level. Default false - set true for any target with anti-bot or WAF (Cloudflare, Akamai, PerimeterX, Datadome). Cheap to leave on for production scrapes."),
150
150
  data: z
151
151
  .union([z.string(), z.record(z.string(), z.unknown())])
152
152
  .optional()
@@ -155,9 +155,9 @@ const singleInputShape = {
155
155
  .string()
156
156
  .optional()
157
157
  .describe("Optional proxy. Two forms: (1) URL `http://host:port` or `socks5://host:port`; " +
158
- "(2) base36 ID from foura_proxy (e.g. `4DZ3VE`) same exit IP. For rotation, use foura_proxy."),
158
+ "(2) base36 ID from foura_proxy (e.g. `4DZ3VE`) - same exit IP. For rotation, use foura_proxy."),
159
159
  // Note: `proxyId` (number) is intentionally NOT exposed. The backend zod
160
- // schema declares it, but no code path reads it gateway only resolves
160
+ // schema declares it, but no code path reads it - gateway only resolves
161
161
  // base36 IDs from the `proxy` string field, and libcurl never reads
162
162
  // request.proxyId. Exposing the dead field would silently no-op.
163
163
  timeout_ms: z.number().int().min(0).max(120_000).optional().describe("Overall request timeout in ms (max 120000, default 15000)"),
@@ -172,10 +172,10 @@ const singleInputShape = {
172
172
  .max(20)
173
173
  .optional()
174
174
  .describe("Max number of redirects to follow (0-20). Omit to disable redirect following."),
175
- tryJsonData: z.boolean().optional().describe("If true, attempt JSON.parse on the response body. On success, `data` is the parsed value (typically object or array). On parse failure, `data` silently stays as the original string no error, no warning. Set false (or omit) when you need to detect parse failures explicitly."),
175
+ tryJsonData: z.boolean().optional().describe("If true, attempt JSON.parse on the response body. On success, `data` is the parsed value (typically object or array). On parse failure, `data` silently stays as the original string - no error, no warning. Set false (or omit) when you need to detect parse failures explicitly."),
176
176
  returnBuffer: z.boolean().optional().describe("Return raw bytes as a serialized Buffer JSON shape (`{type:\"Buffer\", data:[byte, ...]}`, bytes 0-255) instead of decoded string. Use for binary responses (images, protobuf). Reconstruct: `Buffer.from(data.data)` in Node, `new Uint8Array(data.data)` elsewhere."),
177
177
  validate: SingleValidateSchema,
178
- // Bug 3 fix opt-in offload. Default false → response inline regardless
178
+ // Bug 3 fix - opt-in offload. Default false → response inline regardless
179
179
  // of size so Claude Desktop (which can't read resource_link content blocks)
180
180
  // gets usable output on real product pages.
181
181
  offload_large: z
@@ -183,10 +183,10 @@ const singleInputShape = {
183
183
  .optional()
184
184
  .describe("If true, response bodies >= 50KB are written to disk and returned as a resource_link instead of inlined. Saves token context but requires a client that supports `resources/read`. Default false."),
185
185
  };
186
- // Bug 2 fix convert any handler-level crash OR output-validation failure
186
+ // Bug 2 fix - convert any handler-level crash OR output-validation failure
187
187
  // into the documented {service, code, error} envelope. Two guards:
188
- // 1. Outer try/catch around handler catches throws (network, JSON, etc).
189
- // 2. Output validation against outputSchema BEFORE returning catches
188
+ // 1. Outer try/catch around handler - catches throws (network, JSON, etc).
189
+ // 2. Output validation against outputSchema BEFORE returning - catches
190
190
  // shape mismatches that would otherwise bubble up as MCP -32602 errors
191
191
  // without a structuredContent envelope.
192
192
  // Return type is `any` so the strict CallToolResult union from the SDK
@@ -204,7 +204,7 @@ fn) {
204
204
  const issues = parsed.error.issues.slice(0, 5).map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
205
205
  return {
206
206
  isError: true,
207
- content: [{ type: "text", text: `FourA ${service} upstream response failed schema: ${issues}` }],
207
+ content: [{ type: "text", text: `FourA ${service} - upstream response failed schema: ${issues}` }],
208
208
  structuredContent: {
209
209
  service,
210
210
  code: "output_validation_failed",
@@ -219,7 +219,7 @@ fn) {
219
219
  const msg = e instanceof Error ? e.message : String(e);
220
220
  return {
221
221
  isError: true,
222
- content: [{ type: "text", text: `FourA ${service} internal error: ${msg}` }],
222
+ content: [{ type: "text", text: `FourA ${service} - internal error: ${msg}` }],
223
223
  structuredContent: {
224
224
  service,
225
225
  code: "output_validation_failed",
@@ -230,10 +230,10 @@ fn) {
230
230
  }
231
231
  export function registerSingleTool(server) {
232
232
  server.registerTool("foura_single", {
233
- title: "FourA single HTTP request",
234
- description: "Send one HTTP request and return the response. Fastest of the three tools (typically 200ms2s). " +
233
+ title: "FourA - single HTTP request",
234
+ description: "Send one HTTP request and return the response. Fastest of the four tools (typically 200ms-2s). " +
235
235
  "Use for static pages, JSON APIs, server-rendered HTML. Set unblocker:true if the target has " +
236
- "wire-level anti-bot protection. Switch to foura_proxy if you get blocked status 403, status 429, " +
236
+ "wire-level anti-bot protection. Switch to foura_proxy if you get blocked - status 403, status 429, " +
237
237
  "captcha page, OR response carries `x-vercel-mitigated: challenge` / `cf-mitigated: challenge` " +
238
238
  "headers, OR body title matches `Vercel Security Checkpoint` / `Just a moment` / `Attention Required` / " +
239
239
  "`We're verifying your browser`. For these tier-1 WAF challenges call foura_proxy with maxTries:25-30 " +
@@ -267,7 +267,7 @@ export function registerSingleTool(server) {
267
267
  headers: {
268
268
  "X-API-Key": getApiKey(),
269
269
  "Content-Type": "application/json",
270
- "User-Agent": "foura-mcp/0.3.0 (single)",
270
+ "User-Agent": "foura-mcp/0.3.1 (single)",
271
271
  },
272
272
  body: JSON.stringify(upstreamBody),
273
273
  });
@@ -282,7 +282,7 @@ export function registerSingleTool(server) {
282
282
  content: [
283
283
  {
284
284
  type: "text",
285
- text: `FourA single non-JSON response (${res.statusCode}): ${text.slice(0, 200)}`,
285
+ text: `FourA single - non-JSON response (${res.statusCode}): ${text.slice(0, 200)}`,
286
286
  },
287
287
  ],
288
288
  structuredContent: {
@@ -309,7 +309,7 @@ export function registerSingleTool(server) {
309
309
  };
310
310
  }
311
311
  const parsedObj = parsed;
312
- // Bug 8 fix dwstack returns HTTP 200 + {error, status, headers, data}
312
+ // Bug 8 fix - dwstack returns HTTP 200 + {error, status, headers, data}
313
313
  // when libcurl itself failed (DNS, refused, timeout) or when validate.*
314
314
  // rejected the response. The api gateway forwards the inner status
315
315
  // verbatim. Surface this as a structured error instead of a fake success.
@@ -320,7 +320,7 @@ export function registerSingleTool(server) {
320
320
  content: [
321
321
  {
322
322
  type: "text",
323
- text: `FourA single upstream failure (status ${innerStatus}): ${parsedObj.error}`,
323
+ text: `FourA single - upstream failure (status ${innerStatus}): ${parsedObj.error}`,
324
324
  },
325
325
  ],
326
326
  structuredContent: {
@@ -340,7 +340,7 @@ export function registerSingleTool(server) {
340
340
  const statusLabel = parsedObj.status ?? "?";
341
341
  const timeLabel = parsedObj.total_time !== undefined && parsedObj.total_time !== null
342
342
  ? `${parsedObj.total_time}s`
343
- : "";
343
+ : "-";
344
344
  const shouldOffload = offload_large === true
345
345
  && bodyStr
346
346
  && Buffer.byteLength(bodyStr, "utf8") >= THRESHOLD_BYTES;
@@ -1 +1 @@
1
- {"version":3,"file":"single.js","sourceRoot":"","sources":["../../src/tools/single.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"single.js","sourceRoot":"","sources":["../../src/tools/single.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEhE,SAAS,gCAAgC,CAAC,OAAgB;IACxD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAA4B,CAAC,CAAC;YAC7D,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;gBAC7B,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,cAAc,EAAE,CAAC;oBACvC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ;wBAAE,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC;gBAC5E,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,mEAAmE;AACnE,2EAA2E;AAC3E,SAAS,UAAU,CAAC,MAAc,EAAE,QAAiC;IACnE,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,aAAa,CAAC;IACzC,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,aAAa,CAAC;IACzC,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,WAAW,CAAC;IACvC,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,WAAW,CAAC;IACvC,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,cAAc,CAAC;IAC1C,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACnB,IAAI,QAAQ,CAAC,OAAO;YAAE,OAAO,aAAa,CAAC;QAC3C,MAAM,GAAG,GAAG,OAAO,QAAQ,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACrE,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC;YAAE,OAAO,kBAAkB,CAAC;QACtE,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IACD,IAAI,MAAM,IAAI,GAAG;QAAE,OAAO,gBAAgB,CAAC;IAC3C,IAAI,MAAM,IAAI,GAAG;QAAE,OAAO,uBAAuB,CAAC;IAClD,yEAAyE;IACzE,uEAAuE;IACvE,kCAAkC;IAClC,OAAO,kBAAkB,CAAC;AAC5B,CAAC;AAED,MAAM,cAAc,GAClB,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,0BAA0B,CAAC,GAAG,UAAU,CAAC;AAE1E,MAAM,oBAAoB,GAAG,CAAC;KAC3B,MAAM,CAAC;IACN,MAAM,EAAE,CAAC;SACN,MAAM,CAAC;QACN,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;QAC9F,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;KAC7F,CAAC;SACD,QAAQ,EAAE;IACb,OAAO,EAAE,CAAC;SACP,MAAM,CAAC;QACN,MAAM,EAAE,CAAC;aACN,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;aAC9B,QAAQ,EAAE;aACV,QAAQ,CAAC,sQAAsQ,CAAC;QACnR,IAAI,EAAE,CAAC;aACJ,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;aAC9B,QAAQ,EAAE;aACV,QAAQ,CAAC,2PAA2P,CAAC;KACzQ,CAAC;SACD,QAAQ,EAAE;IACb,IAAI,EAAE,CAAC;SACJ,MAAM,CAAC;QACN,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC;QAC5F,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+CAA+C,CAAC;KAC/F,CAAC;SACD,QAAQ,EAAE;CACd,CAAC;KACD,QAAQ,EAAE,CAAC;AAEd,8EAA8E;AAC9E,+EAA+E;AAC/E,yEAAyE;AACzE,gEAAgE;AAChE,MAAM,gBAAgB,GAAG,CAAC;KACvB,MAAM,CAAC;IACN,MAAM,EAAE,CAAC;SACN,MAAM,CAAC;QACN,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC9B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;QACjC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC9B,CAAC;SACD,QAAQ,EAAE;CACd,CAAC;KACD,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAExD,MAAM,iBAAiB,GAAG;IACxB,iFAAiF;IACjF,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6LAA6L,CAAC;IAC3O,uEAAuE;IACvE,wEAAwE;IACxE,qEAAqE;IACrE,OAAO,EAAE,CAAC;SACP,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;SACjF,QAAQ,EAAE;SACV,QAAQ,CAAC,oRAAoR,CAAC;IACjS,IAAI,EAAE,CAAC;SACJ,OAAO,EAAE;SACT,QAAQ,EAAE;SACV,QAAQ,CAAC,kUAAkU,CAAC;IAC/U,2FAA2F;IAC3F,UAAU,EAAE,CAAC;SACV,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;SACzC,QAAQ,EAAE;SACV,QAAQ,CAAC,4HAA4H,CAAC;IACzI,+EAA+E;IAC/E,sBAAsB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC;IACpF,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;IACtF,wFAAwF;IACxF,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;IACrE,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE;IACjE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC;IACvF,OAAO,EAAE,CAAC;SACP,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC;SAC1E,QAAQ,EAAE;SACV,QAAQ,CAAC,sCAAsC,CAAC;IACnD,MAAM,EAAE,CAAC;SACN,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC;SAChF,QAAQ,EAAE;SACV,QAAQ,CAAC,kCAAkC,CAAC;IAC/C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2WAA2W,CAAC;CAClZ,CAAC;AAEF,MAAM,gBAAgB,GAAG;IACvB,8EAA8E;IAC9E,sEAAsE;IACtE,4EAA4E;IAC5E,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,oGAAoG,CAAC;IACjH,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,gXAAgX,CAAC;IAChZ,OAAO,EAAE,CAAC;SACP,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;SACxC,QAAQ,EAAE;SACV,QAAQ,CAAC,oIAAoI,CAAC;IACjJ,SAAS,EAAE,CAAC;SACT,OAAO,EAAE;SACT,QAAQ,EAAE;SACV,QAAQ,CAAC,mTAAmT,CAAC;IAChU,IAAI,EAAE,CAAC;SACJ,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;SACtD,QAAQ,EAAE;SACV,QAAQ,CAAC,mHAAmH,CAAC;IAChI,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,iFAAiF;QACjF,+FAA+F,CAChG;IACH,yEAAyE;IACzE,wEAAwE;IACxE,oEAAoE;IACpE,iEAAiE;IACjE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2DAA2D,CAAC;IACjI,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE;IACnE,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE;IAClE,0BAA0B,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE;IAC3E,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAClE,eAAe,EAAE,CAAC;SACf,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,EAAE,CAAC;SACP,QAAQ,EAAE;SACV,QAAQ,CAAC,+EAA+E,CAAC;IAC5F,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qRAAqR,CAAC;IACnU,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uQAAuQ,CAAC;IACtT,QAAQ,EAAE,oBAAoB;IAC9B,yEAAyE;IACzE,4EAA4E;IAC5E,4CAA4C;IAC5C,aAAa,EAAE,CAAC;SACb,OAAO,EAAE;SACT,QAAQ,EAAE;SACV,QAAQ,CAAC,mMAAmM,CAAC;CACjN,CAAC;AAEF,2EAA2E;AAC3E,mEAAmE;AACnE,6EAA6E;AAC7E,yEAAyE;AACzE,4EAA4E;AAC5E,6CAA6C;AAC7C,uEAAuE;AACvE,oDAAoD;AACpD,KAAK,UAAU,YAAY,CACzB,OAAuC;AACvC,8DAA8D;AAC9D,YAA8B;AAC9B,8DAA8D;AAC9D,EAAsB;IAGtB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,EAAE,EAAE,CAAC;QAC1B,IAAI,MAAM,EAAE,iBAAiB,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;YAChE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACvD,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,KAAK,CAAC,CAAC,OAAO,EAAE,CAChD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACb,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,OAAO,uCAAuC,MAAM,EAAE,EAAE,CAAC;oBAClG,iBAAiB,EAAE;wBACjB,OAAO;wBACP,IAAI,EAAE,0BAA0B;wBAChC,KAAK,EAAE,wDAAwD,MAAM,EAAE;qBACxE;iBACF,CAAC;YACJ,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvD,OAAO;YACL,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,OAAO,sBAAsB,GAAG,EAAE,EAAE,CAAC;YAC9E,iBAAiB,EAAE;gBACjB,OAAO;gBACP,IAAI,EAAE,0BAA0B;gBAChC,KAAK,EAAE,qDAAqD,GAAG,EAAE;aAClE;SACF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAiB;IAClD,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,KAAK,EAAE,6BAA6B;QACpC,WAAW,EACT,iGAAiG;YACjG,8FAA8F;YAC9F,qGAAqG;YACrG,gGAAgG;YAChG,yGAAyG;YACzG,uGAAuG;YACvG,mGAAmG;YACnG,kEAAkE;QACpE,WAAW,EAAE,gBAAgB;QAC7B,YAAY,EAAE,iBAAiB;QAC/B,WAAW,EAAE;YACX,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,KAAK;YACtB,aAAa,EAAE,IAAI;SACpB;KACF,EACD,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,KAAK,IAAI,EAAE;QAC9E,IAAI,CAAC;YACH,MAAM,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,YAAY,gBAAgB,EAAE,CAAC;gBAClC,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;oBAC5C,iBAAiB,EAAE,EAAE,OAAO,EAAE,QAAiB,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE;iBAC1F,CAAC;YACJ,CAAC;YACD,MAAM,CAAC,CAAC;QACV,CAAC;QAED,0DAA0D;QAC1D,MAAM,EAAE,aAAa,EAAE,GAAG,YAAY,EAAE,GAAG,KAAK,CAAC;QAEjD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE;YACxC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,WAAW,EAAE,SAAS,EAAE;gBACxB,cAAc,EAAE,kBAAkB;gBAClC,YAAY,EAAE,0BAA0B;aACzC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC;SACnC,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,qCAAqC,GAAG,CAAC,UAAU,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;qBACpF;iBACF;gBACD,iBAAiB,EAAE;oBACjB,OAAO,EAAE,QAAiB;oBAC1B,IAAI,EAAE,mBAAmB;oBACzB,MAAM,EAAE,GAAG,CAAC,UAAU;oBACtB,KAAK,EAAE,+BAA+B,GAAG,CAAC,UAAU,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;iBAC/E;aACF,CAAC;QACJ,CAAC;QAED,IAAI,GAAG,CAAC,UAAU,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,EAAE,CAAC;YAClD,MAAM,CAAC,GAAG,MAAiC,CAAC;YAC5C,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;YACjE,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACrF,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,GAAG,CAAC,UAAU,KAAK,MAAM,GAAG,QAAQ,EAAE,EAAE,CAAC;gBAC/F,iBAAiB,EAAE;oBACjB,GAAG,CAAC;oBACJ,OAAO,EAAE,QAAiB;oBAC1B,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;oBACnC,MAAM,EAAE,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU;iBACjE;aACF,CAAC;QACJ,CAAC;QAED,MAAM,SAAS,GAAG,MAAuG,CAAC;QAE1H,wEAAwE;QACxE,wEAAwE;QACxE,mEAAmE;QACnE,0EAA0E;QAC1E,IAAI,OAAO,SAAS,CAAC,KAAK,KAAK,QAAQ,IAAI,SAAS,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtE,MAAM,WAAW,GAAG,OAAO,SAAS,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAChF,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,2CAA2C,WAAW,MAAM,SAAS,CAAC,KAAK,EAAE;qBACpF;iBACF;gBACD,iBAAiB,EAAE;oBACjB,GAAI,SAAqC;oBACzC,OAAO,EAAE,QAAiB;oBAC1B,IAAI,EAAE,UAAU,CAAC,WAAW,EAAE,SAAoC,CAAC;oBACnE,MAAM,EAAE,WAAW;iBACpB;aACF,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC;QAC5B,IAAI,OAAO,GAAkB,IAAI,CAAC;QAClC,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,GAAG,IAAI,CAAC;aACxC,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAE1E,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,IAAI,GAAG,CAAC;QAC5C,MAAM,SAAS,GAAG,SAAS,CAAC,UAAU,KAAK,SAAS,IAAI,SAAS,CAAC,UAAU,KAAK,IAAI;YACnF,CAAC,CAAC,GAAG,SAAS,CAAC,UAAU,GAAG;YAC5B,CAAC,CAAC,GAAG,CAAC;QAER,MAAM,aAAa,GAAG,aAAa,KAAK,IAAI;eACvC,OAAO;eACP,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,eAAe,CAAC;QAE3D,IAAI,aAAa,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,EAAE,GAAG,gCAAgC,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,YAAY,CAAC;YAC/E,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,EAAE,EAAE,eAAe,CAAC,CAAC;YAChE,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC/C,OAAO;gBACL,OAAO,EAAE;oBACP,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,WAAW,gBAAgB,MAAM,SAAS,SAAS,EAAE,EAAE;oBAChF,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE;iBACzF;gBACD,iBAAiB,EAAE;oBACjB,MAAM,EAAE,SAAS,CAAC,MAAM;oBACxB,OAAO,EAAE,SAAS,CAAC,OAAO;oBAC1B,UAAU,EAAE,SAAS,CAAC,UAAgD;oBACtE,sBAAsB,EAAE,MAAM,CAAC,GAAG;oBAClC,UAAU,EAAE,MAAM,CAAC,IAAI;iBACxB;aACF,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QACtF,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,WAAW,SAAS,MAAM,SAAS,SAAS,EAAE,EAAE,CAAC;YACpF,iBAAiB,EAAE,SAAoC;SACxD,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,0DAA0D;AAC1D,MAAM,CAAC,MAAM,MAAM,GAAG,EAAE,UAAU,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fouradata/mcp",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "MCP server for the FourA web scraping API — three tools for HTTP requests, rotating proxies, and full browser sessions, usable from any MCP client.",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",