tinyfish-mcp-lite 0.0.0-stage → 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TinyFish
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,327 @@
1
- # Temporary Holding Version
1
+ # @tiny-fish/mcp
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm version](https://img.shields.io/npm/v/@tiny-fish/mcp)](https://www.npmjs.com/package/@tiny-fish/mcp)
4
+
5
+ > **⚠️ You are on `tinyfish-mcp-lite`** — a fork that exposes **only the two
6
+ > free hosted tools** (`search`, `fetch_content`) and hides the 26 paid ones,
7
+ > so they neither burn client context tokens (~72% smaller `tools/list`) nor
8
+ > can ever be invoked through this proxy. **Start here:
9
+ > [Using this fork](#using-this-fork-tinyfish-mcp-lite)** · fork guide:
10
+ > [FORK.md](FORK.md). Everything below this section is inherited from
11
+ > upstream and describes the unfiltered behavior unless noted.
12
+
13
+ TinyFish MCP server: web search, page fetching and extraction, browser
14
+ automation and page monitoring for Claude, Cursor, VS Code and any MCP
15
+ client. Search and Fetch are free.
16
+
17
+ > **Using Claude or ChatGPT in the browser or desktop app?** Skip the setup and add the official
18
+ > TinyFish plugin for **[Claude](https://claude.ai/directory/tinyfish)** or **[ChatGPT](https://chatgpt.com/plugins/plugin_asdk_app_695325bae7348191b58ae9349a963d22)** in one click. It bundles
19
+ > these tools with ready-made skills and safety rules.
20
+
21
+ This package is a transparent reverse proxy that exposes a local
22
+ Streamable-HTTP MCP endpoint at `http://127.0.0.1:3711/mcp` and forwards every
23
+ request to the hosted TinyFish MCP server at `https://agent.tinyfish.ai/mcp`.
24
+
25
+ The proxy defines no tools and no schemas of its own. `tools/list`,
26
+ `tools/call`, `resources/*`, errors, and the `run_web_automation` SSE progress
27
+ stream all come from the hosted server: streaming (SSE) responses are relayed
28
+ byte-verbatim, and non-streaming JSON responses are relayed content-identical
29
+ (parsed and re-serialized, deep-equal to upstream). What the hosted server
30
+ says is what your client sees.
31
+
32
+ ## Using this fork (tinyfish-mcp-lite)
33
+
34
+ Everything below about install, API key, and client configuration applies to
35
+ this fork too — with exactly these differences:
36
+
37
+ - **Only the free tools are exposed.** `tools/list` returns exactly
38
+ `search` and `fetch_content` (their schemas verbatim from upstream). All
39
+ other tools are hidden.
40
+ - **Paid tools cannot be invoked.** A `tools/call` naming a hidden tool is
41
+ answered locally with the same error the hosted server itself produces for
42
+ an unknown tool (HTTP 400, JSON-RPC `-32602 "Unknown tool: <name>"`) — the
43
+ request never leaves your machine, so it can never bill. Notification-
44
+ shaped calls and JSON-RPC batches carrying hidden tools are blocked the
45
+ same way.
46
+ - **One extra env var, `TINYFISH_TOOLS`** (default `search,fetch_content`):
47
+ a comma-separated allowlist. Set `TINYFISH_TOOLS=*` to restore the full
48
+ upstream catalog (28 tools, unfiltered). Invalid values abort startup.
49
+ - **A second startup log line** states the active filter, e.g.
50
+ `tinyfish-mcp [info] tool filter: exposing [search, fetch_content] — set TINYFISH_TOOLS=* to expose every upstream tool`.
51
+
52
+ ### Install (this fork)
53
+
54
+ The npm package name below belongs to **upstream** — installing it gets the
55
+ unfiltered server. To use this fork, install it from GitHub (requires
56
+ Node.js >= 22):
57
+
58
+ ```sh
59
+ # one-liner (the fork's prepare script builds dist/ during install)
60
+ npm install -g github:ByronFinn/tinyfish-mcp-lite
61
+ TINYFISH_API_KEY=tf_... tinyfish-mcp
62
+
63
+ # or from a clone
64
+ git clone https://github.com/ByronFinn/tinyfish-mcp-lite.git
65
+ cd tinyfish-mcp-lite
66
+ npm ci && npm run build
67
+ TINYFISH_API_KEY=tf_... node dist/index.js
68
+ ```
69
+
70
+ > **npm ≥ 11.17 with the install-script policy enabled** (an `allow-scripts`
71
+ > entry in your `~/.npmrc`): git-based installs (`npx github:…`,
72
+ > `npm install -g github:…`) fail with `EALLOWSCRIPTS` — a known npm quirk
73
+ > where the policy reaches the inner git-dep build via the environment. Use
74
+ > the clone route instead, then `npm install -g .` from the clone if you
75
+ > want the `tinyfish-mcp` command on your PATH.
76
+
77
+ Then point your MCP client at `http://127.0.0.1:3711/mcp` exactly as
78
+ described in [Client configuration](#client-configuration) below.
79
+
80
+ Why a fork at all: the hosted server's paid tools (automation, browsers,
81
+ monitors…) are unusable without wallet funds, but their schemas still land
82
+ in every client session. If you only ever use the free search/fetch, this
83
+ fork keeps the client context lean and makes accidental paid calls
84
+ impossible. Fork maintenance and upstream-sync details: [FORK.md](FORK.md).
85
+
86
+ ## Tools
87
+
88
+ The hosted server exposes 28 tools. You can use them to search the web, fetch and
89
+ extract pages, automate browsers, run remote Chrome sessions (CDP), keep
90
+ signed-in Browser Context Profiles and schedule page and topic Monitors.
91
+ (In this fork only the `Search & Fetch` row is advertised — see
92
+ [Using this fork](#using-this-fork-tinyfish-mcp-lite).)
93
+
94
+ | Category | Tools |
95
+ |---|---|
96
+ | Search & Fetch | `search`, `fetch_content` |
97
+ | Web automation | `run_web_automation`, `run_web_automation_async`, `get_run`, `list_runs`, `cancel_run`, `batch_status`, `batch_cancel` |
98
+ | Browser sessions | `create_browser_session`, `list_browser_sessions`, `close_browser_session` |
99
+ | Browser Context Profiles | `create_profile`, `list_profiles`, `start_profile_setup_session`, `save_profile_setup_session`, `cancel_profile_setup_session` |
100
+ | Monitors | `create_monitor`, `get_monitor`, `list_monitors`, `run_monitor`, `pause_monitor`, `resume_monitor`, `cancel_monitor` |
101
+ | Account & usage | `get_wallet`, `get_search_usage`, `list_fetch_usage`, `guide_next_step` |
102
+
103
+ - [Tool reference](https://github.com/tinyfish-io/tinyfish-mcp-server/blob/main/docs/tools.md): every tool, verbatim, with parameters
104
+ - [Examples](https://github.com/tinyfish-io/tinyfish-mcp-server/blob/main/docs/examples.md): common workflows and prompts
105
+ - [MCP Integration guide](https://docs.tinyfish.ai/mcp-integration): client setup, auth, rates, troubleshooting
106
+ - Full documentation: [docs.tinyfish.ai](https://docs.tinyfish.ai)
107
+
108
+ ## Use the hosted server first
109
+
110
+ If your MCP client supports remote Streamable-HTTP servers (Claude Code,
111
+ Claude Desktop connectors, Cursor, VS Code, and most modern clients do),
112
+ connect it **directly** to the hosted server — no install, no local process:
113
+
114
+ ```text
115
+ https://agent.tinyfish.ai/mcp
116
+ ```
117
+
118
+ Use this package instead when:
119
+
120
+ - your client only talks to local MCP servers, or
121
+ - you want to authenticate with a **TinyFish API key** from your environment
122
+ instead of the hosted server's OAuth flow (CI, headless machines, scripts).
123
+
124
+ ## Install
125
+
126
+ ```sh
127
+ npm install -g @tiny-fish/mcp
128
+ ```
129
+
130
+ Or run it without installing:
131
+
132
+ ```sh
133
+ npx @tiny-fish/mcp
134
+ ```
135
+
136
+ Requires **Node.js >= 22**. (This is a deliberate deviation from the TinyFish
137
+ CLI's `>=24` requirement — nothing here needs Node 24.)
138
+
139
+ ## API key
140
+
141
+ The server reads `TINYFISH_API_KEY` from its environment at startup and sends
142
+ it upstream as `X-API-Key` on every call. Get a key at
143
+ [https://agent.tinyfish.ai](https://agent.tinyfish.ai).
144
+
145
+ ```sh
146
+ export TINYFISH_API_KEY=tf_...
147
+ tinyfish-mcp
148
+ ```
149
+
150
+ On success it prints one line to stderr:
151
+
152
+ ```text
153
+ tinyfish-mcp [info] listening on http://127.0.0.1:3711 — upstream https://agent.tinyfish.ai/mcp — v0.1.0
154
+ ```
155
+
156
+ The key is never logged and never echoed back to clients.
157
+
158
+ ## Client configuration
159
+
160
+ Start `tinyfish-mcp` (e.g. in a terminal, or under your process manager of
161
+ choice), then point your client at `http://127.0.0.1:3711/mcp`.
162
+
163
+ ### Claude Code
164
+
165
+ ```sh
166
+ claude mcp add --transport http tinyfish http://127.0.0.1:3711/mcp
167
+ ```
168
+
169
+ ### Claude Desktop
170
+
171
+ Settings → Connectors → Add custom connector, with URL
172
+ `http://127.0.0.1:3711/mcp`. On versions whose
173
+ `claude_desktop_config.json` supports URL-based servers:
174
+
175
+ ```json
176
+ {
177
+ "mcpServers": {
178
+ "tinyfish": {
179
+ "url": "http://127.0.0.1:3711/mcp"
180
+ }
181
+ }
182
+ }
183
+ ```
184
+
185
+ Note: Claude Desktop can also use the hosted `https://agent.tinyfish.ai/mcp`
186
+ directly as a custom connector — prefer that unless you need API-key auth.
187
+
188
+ ### Cursor
189
+
190
+ `~/.cursor/mcp.json` (or `.cursor/mcp.json` in a project):
191
+
192
+ ```json
193
+ {
194
+ "mcpServers": {
195
+ "tinyfish": {
196
+ "url": "http://127.0.0.1:3711/mcp"
197
+ }
198
+ }
199
+ }
200
+ ```
201
+
202
+ ### VS Code
203
+
204
+ `.vscode/mcp.json`:
205
+
206
+ ```json
207
+ {
208
+ "servers": {
209
+ "tinyfish": {
210
+ "type": "http",
211
+ "url": "http://127.0.0.1:3711/mcp"
212
+ }
213
+ }
214
+ }
215
+ ```
216
+
217
+ ### Any other client
218
+
219
+ Configure a Streamable-HTTP (remote/URL) MCP server with:
220
+
221
+ ```json
222
+ { "url": "http://127.0.0.1:3711/mcp" }
223
+ ```
224
+
225
+ The endpoint accepts `POST /mcp` only (matching the hosted server, which has
226
+ no GET SSE channel and no DELETE session teardown). `GET /healthz` returns
227
+ `200 ok` for debugging.
228
+
229
+ ## Environment variables
230
+
231
+ | Variable | Default | Description |
232
+ |---|---|---|
233
+ | `TINYFISH_API_KEY` | (required) | TinyFish API key, sent upstream as `X-API-Key`. The server refuses to start without it. |
234
+ | `PORT` | `3711` | Local listen port (integer 1-65535). No auto-increment: if the port is busy the server exits with an error. |
235
+ | `TINYFISH_UPSTREAM_URL` | `https://agent.tinyfish.ai/mcp` | Upstream MCP URL. Must be `https:`; `http:` is allowed only for `127.0.0.1`/`localhost` (local testing). |
236
+ | `TINYFISH_TOOLS` | `search,fetch_content` | **Fork only:** comma-separated allowlist of upstream tools to expose; `*` disables filtering and exposes the full upstream catalog. Invalid values abort startup. |
237
+
238
+ The proxy also sends attribution headers on every upstream call:
239
+ `X-TF-Request-Origin: tinyfish-mcp`, `X-TF-Client-Name: tinyfish-mcp`, and
240
+ `X-TF-Client-Version: <package version>`.
241
+
242
+ ## Security & trust model
243
+
244
+ This is a loopback-only server with a deliberate, documented trust boundary:
245
+
246
+ - **Loopback bind, always.** The server binds the `127.0.0.1` literal and this
247
+ is not configurable — it can never listen on `0.0.0.0` or a LAN interface.
248
+ - **Origin validation.** Requests carrying an `Origin` header are rejected
249
+ with `403` unless the origin is `http(s)://127.0.0.1` or
250
+ `http(s)://localhost` (any port). This blocks the DNS-rebinding attack the
251
+ MCP spec calls out for local HTTP servers. Requests without an `Origin`
252
+ header (curl, MCP SDKs, inspectors) are allowed.
253
+ - **Server-holds-key.** The process reads `TINYFISH_API_KEY` from its env;
254
+ clients send no credential on the local hop. This is the simplest model,
255
+ but it means **any local process that can reach `127.0.0.1:<port>` can use
256
+ your key and drive automations** (which can spend TinyFish credits). The
257
+ loopback bind and Origin check are the mitigations; treat the port as a
258
+ local trust boundary on a machine you trust.
259
+
260
+ ## Troubleshooting
261
+
262
+ **"Port 3711 is already in use"** — another process holds the port. Stop it,
263
+ or set `PORT` to a free port and update your client config to match.
264
+
265
+ **HTTP 502 with JSON-RPC error `-32001`** ("Upstream rejected the request …
266
+ check that TINYFISH_API_KEY is set to a valid TinyFish API key") — the hosted
267
+ server rejected your key. Verify `TINYFISH_API_KEY` is set in the environment
268
+ of the `tinyfish-mcp` process (not just your shell) and that the key is valid
269
+ at [https://agent.tinyfish.ai](https://agent.tinyfish.ai). The error's `data`
270
+ carries the upstream status and (truncated) body for diagnosis.
271
+
272
+ **HTTP 502 with JSON-RPC error `-32000`** ("cannot reach …") — the upstream
273
+ server is unreachable: check your network/proxy/VPN; the hosted server may
274
+ also be temporarily down. If you overrode `TINYFISH_UPSTREAM_URL`, check it.
275
+ If a streamed `run_web_automation` call fails mid-stream you get the same
276
+ `-32000` as the final SSE frame, with a "the run may still be executing"
277
+ warning and the `runId` when known — check the run's status instead of
278
+ retrying blindly.
279
+
280
+ **Batch requests don't work** — MCP forbids JSON-RPC batching and the hosted
281
+ server does not support batch arrays. The proxy forwards a batch as-is and
282
+ the upstream answers its own error; send one JSON-RPC message per request.
283
+
284
+ **Claude/other client can't connect** — make sure `tinyfish-mcp` is actually
285
+ running (it's a standalone server; clients do not launch it) and that
286
+ `GET http://127.0.0.1:3711/healthz` answers `ok`.
287
+
288
+ ## Development
289
+
290
+ ```sh
291
+ npm ci
292
+ npm run build # tsc → dist/, marks dist/index.js executable
293
+ npm test # unit tests (offline, mock upstream)
294
+ npm run test:watch # unit tests in watch mode
295
+ npm run lint # eslint over src/ tests/
296
+ npm run format # prettier
297
+ npm run type-check # tsc --noEmit over the whole tree
298
+ ```
299
+
300
+ Layout: `src/core/` is the transport-agnostic proxy core (upstream client,
301
+ session bridge, SSE relay, error shaping); `src/http/` is the thin HTTP
302
+ adapter (routing, Origin check); `tests/` holds the unit suites plus
303
+ `tests/helpers/mock-upstream.ts`, a mock of the hosted endpoint that the unit
304
+ tests run against entirely offline.
305
+
306
+ Integration tests hit the **real** hosted upstream and are gated: without
307
+ `TINYFISH_API_KEY` they skip with a printed notice.
308
+
309
+ ```sh
310
+ TINYFISH_API_KEY=... npm run test:integration
311
+ ```
312
+
313
+ Set `TINYFISH_UPSTREAM_URL` to point them at a sandbox deployment instead of
314
+ production. Note the `run_web_automation` integration test executes a real
315
+ automation and spends credits.
316
+
317
+ ## Maintenance
318
+
319
+ Maintained by [@Zechereh](https://github.com/Zechereh). Bugs and feature
320
+ requests: [GitHub issues](https://github.com/tinyfish-io/tinyfish-mcp-server/issues).
321
+ This fork is maintained at
322
+ [ByronFinn/tinyfish-mcp-lite](https://github.com/ByronFinn/tinyfish-mcp-lite);
323
+ fork-specific questions live in [FORK.md](FORK.md).
324
+
325
+ ## License
326
+
327
+ MIT
@@ -0,0 +1,17 @@
1
+ export declare const DEFAULT_PORT = 3711;
2
+ export declare const DEFAULT_UPSTREAM_URL = "https://agent.tinyfish.ai/mcp";
3
+ export declare const API_KEY_GUIDANCE = "Set TINYFISH_API_KEY \u2014 get a key at https://agent.tinyfish.ai";
4
+ export interface Config {
5
+ /** Never log this field. */
6
+ apiKey: string;
7
+ port: number;
8
+ upstreamUrl: string;
9
+ }
10
+ /** Thrown by parseConfig; `message` is the actionable stderr line(s) for the user. */
11
+ export declare class ConfigError extends Error {
12
+ }
13
+ /**
14
+ * Pure env → Config parser. Throws ConfigError with an actionable message on
15
+ * invalid input; performs no I/O and never touches process state.
16
+ */
17
+ export declare function parseConfig(env: Record<string, string | undefined>): Config;
package/dist/config.js ADDED
@@ -0,0 +1,79 @@
1
+ import { z } from "zod";
2
+ export const DEFAULT_PORT = 3711;
3
+ export const DEFAULT_UPSTREAM_URL = "https://agent.tinyfish.ai/mcp";
4
+ export const API_KEY_GUIDANCE = "Set TINYFISH_API_KEY — get a key at https://agent.tinyfish.ai";
5
+ /** Thrown by parseConfig; `message` is the actionable stderr line(s) for the user. */
6
+ export class ConfigError extends Error {
7
+ }
8
+ const envSchema = z.object({
9
+ TINYFISH_API_KEY: z
10
+ .string({ error: API_KEY_GUIDANCE })
11
+ .min(1, { error: API_KEY_GUIDANCE }),
12
+ PORT: z
13
+ .string()
14
+ .optional()
15
+ .transform((value, ctx) => {
16
+ if (value === undefined)
17
+ return DEFAULT_PORT;
18
+ const port = /^\d+$/.test(value) ? Number(value) : NaN;
19
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
20
+ ctx.addIssue({
21
+ code: "custom",
22
+ message: `Invalid PORT "${value}" — must be an integer between 1 and 65535`,
23
+ });
24
+ return z.NEVER;
25
+ }
26
+ return port;
27
+ }),
28
+ TINYFISH_UPSTREAM_URL: z
29
+ .string()
30
+ .optional()
31
+ .transform((value, ctx) => {
32
+ const raw = value ?? DEFAULT_UPSTREAM_URL;
33
+ let url;
34
+ // Error messages never echo the raw value: it may embed credentials or
35
+ // query-string secrets, and these messages go to stderr.
36
+ try {
37
+ url = new URL(raw);
38
+ }
39
+ catch {
40
+ ctx.addIssue({
41
+ code: "custom",
42
+ message: "Invalid TINYFISH_UPSTREAM_URL — must be an absolute URL",
43
+ });
44
+ return z.NEVER;
45
+ }
46
+ if (url.username !== "" || url.password !== "") {
47
+ ctx.addIssue({
48
+ code: "custom",
49
+ message: "Invalid TINYFISH_UPSTREAM_URL — URL credentials are not allowed",
50
+ });
51
+ return z.NEVER;
52
+ }
53
+ const isLoopback = url.hostname === "127.0.0.1" || url.hostname === "localhost";
54
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopback)) {
55
+ ctx.addIssue({
56
+ code: "custom",
57
+ message: "Invalid TINYFISH_UPSTREAM_URL — scheme must be https " +
58
+ "(http is allowed only for 127.0.0.1/localhost)",
59
+ });
60
+ return z.NEVER;
61
+ }
62
+ return raw;
63
+ }),
64
+ });
65
+ /**
66
+ * Pure env → Config parser. Throws ConfigError with an actionable message on
67
+ * invalid input; performs no I/O and never touches process state.
68
+ */
69
+ export function parseConfig(env) {
70
+ const parsed = envSchema.safeParse(env);
71
+ if (!parsed.success) {
72
+ throw new ConfigError(parsed.error.issues.map((issue) => issue.message).join("\n"));
73
+ }
74
+ return {
75
+ apiKey: parsed.data.TINYFISH_API_KEY,
76
+ port: parsed.data.PORT,
77
+ upstreamUrl: parsed.data.TINYFISH_UPSTREAM_URL,
78
+ };
79
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Typed transport-level errors thrown by the proxy core, plus the only
3
+ * client-facing shaping functions: `toJsonRpcError` for pre-stream failures
4
+ * and `toStreamErrorFrame` for failures after an SSE relay started. Every
5
+ * adapter catch path routes through these two functions — no ad-hoc error
6
+ * bodies anywhere else, with exactly two deliberate exceptions: the adapter's
7
+ * ParseError reply (http/adapter.ts — built where the unparseable body is
8
+ * caught, since there is nothing to route), and the last-resort -32603
9
+ * backstop in http/index.ts's invokeSafely. Messages must never contain the
10
+ * API key (they describe network/protocol conditions only).
11
+ *
12
+ * HTTP status decision for locally shaped errors: the hosted server maps
13
+ * client-error JSON-RPC codes to HTTP 400 and everything else to 500. The
14
+ * proxy mirrors the 400 for client errors (-32700 ParseError) and picks
15
+ * **502 Bad Gateway** for the upstream-leg failures it shapes itself (-32000
16
+ * unreachable/stream-failed, -32001 auth rejection): the proxy is healthy,
17
+ * the upstream hop failed — distinguishing these from a genuine local proxy
18
+ * bug, which stays **500** with -32603 InternalError. Upstream-originated
19
+ * JSON-RPC errors are never shaped at all: they forward verbatim under
20
+ * upstream's own HTTP status.
21
+ */
22
+ /** JSON-RPC error codes used by locally shaped errors. */
23
+ export declare const JsonRpcErrorCodes: {
24
+ /** Upstream unreachable / upstream stream failed (server-side, HTTP 502). */
25
+ readonly UpstreamUnavailable: -32000;
26
+ /** Upstream rejected auth — check TINYFISH_API_KEY (HTTP 502). */
27
+ readonly UpstreamAuth: -32001;
28
+ /** Local proxy bug (HTTP 500). */
29
+ readonly InternalError: -32603;
30
+ /** Malformed client JSON (HTTP 400, id -1 — mirrors upstream). */
31
+ readonly ParseError: -32700;
32
+ };
33
+ /** Base class for all proxy-core errors (transport level, not JSON-RPC). */
34
+ export declare class ProxyCoreError extends Error {
35
+ constructor(message: string, options?: ErrorOptions);
36
+ }
37
+ /** The upstream server could not be reached (DNS, TLS, refused, reset). */
38
+ export declare class UpstreamUnreachableError extends ProxyCoreError {
39
+ /** Upstream "host[:port]" when known — used in the client-facing message. */
40
+ host?: string;
41
+ }
42
+ /**
43
+ * Upstream answered 401/403 with a body that is NOT a JSON-RPC message (a
44
+ * JSON-RPC error body, whatever its HTTP status, forwards verbatim instead).
45
+ * Carries the upstream status and body text so the shaped
46
+ * client error can include them as diagnostics. The body text is truncated to
47
+ * ~2KB at construction; the Error message itself never includes it.
48
+ */
49
+ export declare class UpstreamAuthError extends ProxyCoreError {
50
+ readonly status: number;
51
+ /** Upstream response body text, truncated to AUTH_BODY_LIMIT chars. */
52
+ readonly bodyText: string;
53
+ constructor(status: number, bodyText: string, options?: ErrorOptions);
54
+ }
55
+ /**
56
+ * ~2KB cap on the upstream auth-failure body relayed in error data.
57
+ *
58
+ * Measured in UTF-16 code units (String.prototype.slice), not bytes: for
59
+ * multi-byte scripts the UTF-8 wire size can reach ~3× (≤ ~6KB) — bounded
60
+ * either way, which is all the "~2KB" contract promises. A slice boundary can
61
+ * split a surrogate pair; harmless, since Node's well-formed JSON.stringify
62
+ * escapes the lone surrogate and the response stays valid JSON.
63
+ */
64
+ export declare const AUTH_BODY_LIMIT = 2048;
65
+ /**
66
+ * Delivering a relayed SSE frame to the LOCAL client failed (the transport's
67
+ * onEvent callback rejected — e.g. the client socket died mid-write). This is
68
+ * a client-side condition, never an upstream one: it must not be logged or
69
+ * classified as "Upstream unreachable".
70
+ */
71
+ export declare class LocalWriteError extends ProxyCoreError {
72
+ }
73
+ /** An in-flight upstream request was aborted locally (session close / shutdown). */
74
+ export declare class UpstreamAbortedError extends ProxyCoreError {
75
+ }
76
+ /**
77
+ * Upstream answered with something the core cannot interpret (non-JSON body,
78
+ * SSE stream that ends without a final response frame, unexpected empty body).
79
+ */
80
+ export declare class UpstreamProtocolError extends ProxyCoreError {
81
+ /** Upstream HTTP status when one was received before the failure. */
82
+ readonly status?: number;
83
+ constructor(message: string, status?: number, options?: ErrorOptions);
84
+ }
85
+ /** True for the AbortError DOMException fetch throws when its signal fires. */
86
+ export declare function isAbortError(err: unknown): boolean;
87
+ export interface JsonRpcErrorBody {
88
+ jsonrpc: "2.0";
89
+ error: {
90
+ code: number;
91
+ message: string;
92
+ data?: unknown;
93
+ };
94
+ id: unknown;
95
+ }
96
+ /** A shaped failure: the HTTP status to answer with plus the JSON-RPC body. */
97
+ export interface ShapedJsonRpcError {
98
+ httpStatus: number;
99
+ body: JsonRpcErrorBody;
100
+ }
101
+ /**
102
+ * Map a failure the upstream never answered (or answered unusably) to the
103
+ * client-facing JSON-RPC error + local HTTP status.
104
+ * Only failures upstream never saw as JSON-RPC get shaped here —
105
+ * upstream JSON-RPC errors forward verbatim and never reach this function.
106
+ * Never includes the API key: transport-error messages describe network and
107
+ * protocol conditions only, and unexpected local errors get a generic message
108
+ * (their stack goes to stderr at the catch site, not to the client).
109
+ */
110
+ export declare function toJsonRpcError(failure: unknown, requestId: unknown): ShapedJsonRpcError;
111
+ /**
112
+ * Build the final SSE-framed JSON-RPC error for a failure AFTER the local SSE
113
+ * relay started (mid-stream upstream disconnect). A
114
+ * tools/call may have side effects, so the message warns that the run may
115
+ * still be executing and is never retried silently; when a run id was already
116
+ * seen in a progress frame's `_meta.runId` it is included in `data.runId`
117
+ * (camelCase, matching upstream's `_meta.runId` convention) and named in the
118
+ * message, else `data` is omitted entirely.
119
+ *
120
+ * The -32000 message differentiates the failure kind: a locally aborted
121
+ * upstream request (session close / shutdown mid-stream) reads differently
122
+ * from an upstream that died or broke protocol — but both keep the "run may
123
+ * still be executing" guidance, because in either case a live run could be
124
+ * left behind upstream.
125
+ */
126
+ export declare function toStreamErrorFrame(failure: unknown, requestId: unknown, runId?: string): JsonRpcErrorBody;