@chrischall/mcp-utils 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/README.md +235 -0
- package/dist/auth/index.d.ts +223 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +267 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/config/index.d.ts +86 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +121 -0
- package/dist/config/index.js.map +1 -0
- package/dist/errors/index.d.ts +90 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +157 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/fetchproxy/index.d.ts +156 -0
- package/dist/fetchproxy/index.d.ts.map +1 -0
- package/dist/fetchproxy/index.js +197 -0
- package/dist/fetchproxy/index.js.map +1 -0
- package/dist/html/index.d.ts +142 -0
- package/dist/html/index.d.ts.map +1 -0
- package/dist/html/index.js +321 -0
- package/dist/html/index.js.map +1 -0
- package/dist/http/index.d.ts +202 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +341 -0
- package/dist/http/index.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/response/index.d.ts +22 -0
- package/dist/response/index.d.ts.map +1 -0
- package/dist/response/index.js +61 -0
- package/dist/response/index.js.map +1 -0
- package/dist/server/index.d.ts +109 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +95 -0
- package/dist/server/index.js.map +1 -0
- package/dist/session/index.d.ts +233 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +404 -0
- package/dist/session/index.js.map +1 -0
- package/dist/test/index.d.ts +124 -0
- package/dist/test/index.d.ts.map +1 -0
- package/dist/test/index.js +181 -0
- package/dist/test/index.js.map +1 -0
- package/dist/zod/index.d.ts +130 -0
- package/dist/zod/index.d.ts.map +1 -0
- package/dist/zod/index.js +184 -0
- package/dist/zod/index.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared MCP error classes, error wrapping, and bridge-error discrimination.
|
|
3
|
+
*
|
|
4
|
+
* Consolidates the `instanceof`-chains and remediation-message patterns found
|
|
5
|
+
* in `client.ts` / `auth.ts` across the fleet. Every error carries an optional
|
|
6
|
+
* `hint` — a "here's how to fix it" string the tool surface can show the user.
|
|
7
|
+
*
|
|
8
|
+
* The fetchproxy typed-error hierarchy (`Fetchproxy*Error`) is re-exported, not
|
|
9
|
+
* reimplemented; {@link classifyBridgeError} is a thin discriminator over it.
|
|
10
|
+
*/
|
|
11
|
+
// NOTE: the fetchproxy typed-error hierarchy (Fetchproxy*Error) and the
|
|
12
|
+
// `classifyBridgeError` discriminator live in the `@chrischall/mcp-utils/fetchproxy`
|
|
13
|
+
// subpath — NOT here. Keeping `@fetchproxy/server` (an optional peer dep) out of
|
|
14
|
+
// this core module is what lets bearer-only MCPs import the core barrel without
|
|
15
|
+
// installing fetchproxy.
|
|
16
|
+
/** Default seconds to wait before retrying a tripped bot-wall (issue #90 tuning). */
|
|
17
|
+
export const DEFAULT_BOT_WALL_RETRY_AFTER_S = 30;
|
|
18
|
+
/** Default truncation budget for upstream error bodies surfaced to clients. */
|
|
19
|
+
export const DEFAULT_ERROR_MESSAGE_MAX = 500;
|
|
20
|
+
/**
|
|
21
|
+
* Base class for every tool-facing error. Carries an optional `hint` —
|
|
22
|
+
* actionable remediation text ("set ZOLA_REFRESH_TOKEN", "sign in at compass.com")
|
|
23
|
+
* the tool surface can present separately from the message.
|
|
24
|
+
*/
|
|
25
|
+
export class McpToolError extends Error {
|
|
26
|
+
/** Actionable remediation text, when one applies. */
|
|
27
|
+
hint;
|
|
28
|
+
constructor(message, opts) {
|
|
29
|
+
super(message, opts?.cause !== undefined ? { cause: opts.cause } : undefined);
|
|
30
|
+
this.name = 'McpToolError';
|
|
31
|
+
if (opts?.hint !== undefined)
|
|
32
|
+
this.hint = opts.hint;
|
|
33
|
+
// Restore the prototype chain for transpiled `extends Error`.
|
|
34
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The user's browser session isn't signed in to the upstream service. Distinct
|
|
39
|
+
* from a transient bot-wall — this is a stable "go authenticate" condition.
|
|
40
|
+
*/
|
|
41
|
+
export class SessionNotAuthenticatedError extends McpToolError {
|
|
42
|
+
constructor(service, signInHost) {
|
|
43
|
+
const name = service ?? 'the service';
|
|
44
|
+
const where = signInHost ? `Open ${signInHost} in your browser and sign in, then try again.` : 'Sign in in your browser, then try again.';
|
|
45
|
+
super(`Not signed in to ${name}. ${where} ` +
|
|
46
|
+
'Saved searches, saved homes, and other account data require a signed-in session.', { hint: where });
|
|
47
|
+
this.name = 'SessionNotAuthenticatedError';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Transient anti-bot interstitial (PerimeterX / DataDome CAPTCHA). The request
|
|
52
|
+
* was rate-limited, NOT a missing resource — back off and retry. Kept distinct
|
|
53
|
+
* from {@link SessionNotAuthenticatedError} so callers don't misclassify a
|
|
54
|
+
* retryable wall as a stale session (issue #90).
|
|
55
|
+
*/
|
|
56
|
+
export class BotWallError extends McpToolError {
|
|
57
|
+
/** Suggested seconds to wait before retrying the blocked request(s). */
|
|
58
|
+
retryAfterSeconds;
|
|
59
|
+
constructor(path, retryAfterSeconds = DEFAULT_BOT_WALL_RETRY_AFTER_S) {
|
|
60
|
+
const hint = `Back off and retry (suggested wait: ${retryAfterSeconds}s). If it persists, open the site in your browser, clear the CAPTCHA, then retry with a smaller batch.`;
|
|
61
|
+
super(`Served an anti-bot CAPTCHA wall for ${path} — the request was rate-limited, not a missing resource. ${hint}`, { hint });
|
|
62
|
+
this.name = 'BotWallError';
|
|
63
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Upstream returned HTTP 429 (or an equivalent rate-limit signal). */
|
|
67
|
+
export class RateLimitError extends McpToolError {
|
|
68
|
+
/** Seconds the upstream asked us to wait, when it told us. */
|
|
69
|
+
retryAfterSeconds;
|
|
70
|
+
constructor(service, retryAfterSeconds) {
|
|
71
|
+
const wait = retryAfterSeconds !== undefined ? ` Retry after ${retryAfterSeconds}s.` : ' Back off and retry.';
|
|
72
|
+
super(`Rate limited by ${service}.${wait}`, { hint: wait.trim() });
|
|
73
|
+
this.name = 'RateLimitError';
|
|
74
|
+
if (retryAfterSeconds !== undefined)
|
|
75
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Upstream is unreachable (5xx / transport failure) — not the caller's fault. */
|
|
79
|
+
export class UnreachableError extends McpToolError {
|
|
80
|
+
/** Upstream HTTP status, when one was observed. */
|
|
81
|
+
status;
|
|
82
|
+
constructor(service, status) {
|
|
83
|
+
const suffix = status !== undefined ? ` (status ${status})` : '';
|
|
84
|
+
super(`${service} unreachable${suffix}. The service may be down — try again later.`, {
|
|
85
|
+
hint: 'The upstream service is temporarily unavailable; retry later.',
|
|
86
|
+
});
|
|
87
|
+
this.name = 'UnreachableError';
|
|
88
|
+
if (status !== undefined)
|
|
89
|
+
this.status = status;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A tool requires a different auth/operation mode than the server is running in
|
|
94
|
+
* (e.g. a Pro key-mode-only report invoked while in session mode).
|
|
95
|
+
*/
|
|
96
|
+
export class ModeMismatchError extends McpToolError {
|
|
97
|
+
currentMode;
|
|
98
|
+
requiredMode;
|
|
99
|
+
feature;
|
|
100
|
+
constructor(currentMode, requiredMode, feature) {
|
|
101
|
+
const hint = `Switch to ${requiredMode} mode to use ${feature}.`;
|
|
102
|
+
super(`${feature} requires ${requiredMode} mode but the server is running in ${currentMode} mode. ${hint}`, { hint });
|
|
103
|
+
this.currentMode = currentMode;
|
|
104
|
+
this.requiredMode = requiredMode;
|
|
105
|
+
this.feature = feature;
|
|
106
|
+
this.name = 'ModeMismatchError';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** Factory for an {@link McpToolError} with a remediation hint. */
|
|
110
|
+
export function createHelpfulError(message, opts) {
|
|
111
|
+
return new McpToolError(message, opts);
|
|
112
|
+
}
|
|
113
|
+
const BEARER_RE = /(bearer\s+)[A-Za-z0-9._~+/=-]{8,}/gi;
|
|
114
|
+
// A JWT-shaped triple (header.payload.signature), each segment base64url-ish.
|
|
115
|
+
const JWT_RE = /\b[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{8,}\b/g;
|
|
116
|
+
/**
|
|
117
|
+
* Redact secrets that commonly leak into upstream error bodies before the text
|
|
118
|
+
* is surfaced to a client: `Bearer <token>` headers and standalone JWTs.
|
|
119
|
+
*/
|
|
120
|
+
function redactSecrets(text) {
|
|
121
|
+
return text.replace(BEARER_RE, '$1[REDACTED]').replace(JWT_RE, '[REDACTED]');
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Redact secrets, then cap an (upstream) error string at `max` characters,
|
|
125
|
+
* appending a `… [truncated]` marker when clipped.
|
|
126
|
+
*
|
|
127
|
+
* Security: redaction runs BEFORE truncation so a token straddling the cut
|
|
128
|
+
* boundary can't survive in a half-form. Untrusted upstream bodies must always
|
|
129
|
+
* go through this before reaching a tool result.
|
|
130
|
+
*/
|
|
131
|
+
export function truncateErrorMessage(text, max = DEFAULT_ERROR_MESSAGE_MAX) {
|
|
132
|
+
const str = text === null || text === undefined ? '' : String(text);
|
|
133
|
+
const redacted = redactSecrets(str);
|
|
134
|
+
if (redacted.length <= max)
|
|
135
|
+
return redacted;
|
|
136
|
+
return `${redacted.slice(0, max)}… [truncated]`;
|
|
137
|
+
}
|
|
138
|
+
/** Extract a string message from any thrown value. */
|
|
139
|
+
export function messageOf(err) {
|
|
140
|
+
if (err instanceof Error)
|
|
141
|
+
return err.message;
|
|
142
|
+
return String(err);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Prepend the tool name to an error's context and return an {@link McpToolError},
|
|
146
|
+
* preserving any `hint` and chaining the original via `cause`. The message is
|
|
147
|
+
* run through {@link truncateErrorMessage} (redaction + truncation). Re-wrapping
|
|
148
|
+
* an already-prefixed error does not double-prefix.
|
|
149
|
+
*/
|
|
150
|
+
export function wrapToolError(toolName, err) {
|
|
151
|
+
const inner = messageOf(err);
|
|
152
|
+
const prefix = `[${toolName}]`;
|
|
153
|
+
const message = inner.includes(prefix) ? inner : `${prefix} ${inner}`;
|
|
154
|
+
const hint = err instanceof McpToolError ? err.hint : undefined;
|
|
155
|
+
return new McpToolError(truncateErrorMessage(message), { hint, cause: err });
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetchproxy transport adapter — the Pattern-A glue shared by every
|
|
3
|
+
* fetchproxy-backed MCP (redfin/zillow/compass/homes/onehome/resy/opentable/…).
|
|
4
|
+
*
|
|
5
|
+
* `@fetchproxy/server` already owns HTTP proxying, session bootstrap, and the
|
|
6
|
+
* bot-wall / backoff / deadline / concurrency / retry primitives. This module
|
|
7
|
+
* does NOT reimplement any of that — it re-exports the primitives so MCPs have a
|
|
8
|
+
* single import site, and provides two thin factories:
|
|
9
|
+
*
|
|
10
|
+
* - {@link createFetchproxyTransport} wraps a `FetchproxyServer` in the
|
|
11
|
+
* `start` / `close` / `status` lifecycle every MCP's transport interface
|
|
12
|
+
* expects, with optional debug-gated role logging.
|
|
13
|
+
* - {@link createBootstrapOpts} assembles a multi-domain / capture-header /
|
|
14
|
+
* storage-pointer declaration fragment of `FetchproxyServerOpts`, deriving
|
|
15
|
+
* the required `capabilities` from the declared bootstrap so callers can't
|
|
16
|
+
* forget to unlock the verb they declared.
|
|
17
|
+
*
|
|
18
|
+
* The adapter shape is identical across 12+ fetchproxy MCPs; collapsing it here
|
|
19
|
+
* keeps the per-row / concurrency / deadline helpers as re-exports rather than
|
|
20
|
+
* re-rolled code.
|
|
21
|
+
*/
|
|
22
|
+
import { FetchproxyServer, type FetchproxyServerOpts } from '@fetchproxy/server';
|
|
23
|
+
export { FetchproxyServer, mapWithConcurrency, withDeadline, TokenBucket, classifyBotWall, retryOnceOnTimeout, FetchproxyProtocolError, FetchproxyHttpError, FetchproxyBridgeDownError, FetchproxyTimeoutError, classifyFetchError, backoffDelayMs, BRIDGE_CONCURRENCY, } from '@fetchproxy/server';
|
|
24
|
+
export type { FetchproxyServerOpts, FetchResult, FetchResultError, HttpResponse, RequestOpts, BodylessRequestOpts, BridgeHealth, BridgeProbeResult, BridgeError, FetchErrorKind, BotWallResult, BotWallVendor, TokenBucketOptions, BackoffOptions, DeadlineOutcome, } from '@fetchproxy/server';
|
|
25
|
+
/**
|
|
26
|
+
* The lifecycle surface every per-MCP fetchproxy transport interface exposes.
|
|
27
|
+
* `createFetchproxyTransport` returns this, typed as the caller's `T` so it can
|
|
28
|
+
* stand in for `RedfinTransport`, `ZillowTransport`, etc. without those
|
|
29
|
+
* interfaces depending on this package.
|
|
30
|
+
*/
|
|
31
|
+
export interface FetchproxyTransport {
|
|
32
|
+
/**
|
|
33
|
+
* Load identity (creating the 0600 keypair on first run) and prepare the
|
|
34
|
+
* bridge. Does NOT bind the port or dial — connection is lazy on first verb.
|
|
35
|
+
* When `debugEnvVar` is set and truthy, logs the landed role to stderr.
|
|
36
|
+
*/
|
|
37
|
+
start(): Promise<void>;
|
|
38
|
+
/** Tear the bridge connection down. Safe to call before {@link start}. */
|
|
39
|
+
close(): Promise<void>;
|
|
40
|
+
/** Process-wide bridge freshness snapshot, for a healthcheck tool. */
|
|
41
|
+
status(): ReturnType<FetchproxyServer['bridgeHealth']>;
|
|
42
|
+
/** Bridge role; `null` until the first verb call / explicit connect. */
|
|
43
|
+
readonly role: FetchproxyServer['role'];
|
|
44
|
+
/**
|
|
45
|
+
* The wrapped `FetchproxyServer` — the verb surface (`request`/`get`/`post`/
|
|
46
|
+
* `getJson`/`getHtml`/`readCookies`/`captureRequestHeader`/…). Exposed so the
|
|
47
|
+
* caller's tool layer can issue requests without this package modelling every
|
|
48
|
+
* verb.
|
|
49
|
+
*/
|
|
50
|
+
readonly server: FetchproxyServer;
|
|
51
|
+
}
|
|
52
|
+
/** Options for {@link createFetchproxyTransport}. */
|
|
53
|
+
export type CreateFetchproxyTransportOptions = FetchproxyServerOpts & {
|
|
54
|
+
/**
|
|
55
|
+
* Env var name that gates stderr role/lifecycle logging (e.g. `REDFIN_DEBUG`).
|
|
56
|
+
* The value is read defensively — empty / `'null'` / `${...}` placeholders are
|
|
57
|
+
* treated as unset, so an unexpanded MCP-host env block never enables logging.
|
|
58
|
+
*/
|
|
59
|
+
debugEnvVar?: string;
|
|
60
|
+
/** Env source for {@link debugEnvVar}. Defaults to `process.env`. */
|
|
61
|
+
env?: NodeJS.ProcessEnv;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Wrap a `FetchproxyServer` in the `start`/`close`/`status` lifecycle the
|
|
65
|
+
* per-MCP transport interface expects. The full `FetchproxyServerOpts` is
|
|
66
|
+
* forwarded verbatim (so `fetchTimeoutMs`, `keepAliveIntervalMs`, capture
|
|
67
|
+
* declarations, etc. all pass through); the only added knob is `debugEnvVar`.
|
|
68
|
+
*
|
|
69
|
+
* Returns the wrapper typed as the caller's `T` (defaulting to
|
|
70
|
+
* {@link FetchproxyTransport}) so it can satisfy a structurally-compatible
|
|
71
|
+
* per-MCP interface without that interface importing this package.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* const transport = createFetchproxyTransport<RedfinTransport>({
|
|
75
|
+
* serverName: 'redfin-mcp', version, domains: ['redfin.com'],
|
|
76
|
+
* debugEnvVar: 'REDFIN_DEBUG',
|
|
77
|
+
* });
|
|
78
|
+
*/
|
|
79
|
+
export declare function createFetchproxyTransport<T = FetchproxyTransport>(opts: CreateFetchproxyTransportOptions): T;
|
|
80
|
+
/** Re-export the protocol declaration shapes so callers have one import site. */
|
|
81
|
+
export type { Capability, CaptureHeaderDecl, IndexedDbScopeDecl, StoragePointerDecl, } from '@fetchproxy/protocol';
|
|
82
|
+
import type { CaptureHeaderDecl, IndexedDbScopeDecl, StoragePointerDecl } from '@fetchproxy/protocol';
|
|
83
|
+
/**
|
|
84
|
+
* The bootstrap declarations an MCP needs to extract auth from the user's
|
|
85
|
+
* signed-in tab. Each present, non-empty group unlocks the capability that
|
|
86
|
+
* gates the matching verb — `createBootstrapOpts` derives `capabilities` so the
|
|
87
|
+
* caller can't declare a capture without unlocking it (or vice-versa).
|
|
88
|
+
*/
|
|
89
|
+
export interface BootstrapDecls {
|
|
90
|
+
/** `read_cookies`: declared cookie names readable via `readCookies({ keys })`. */
|
|
91
|
+
cookieKeys?: string[];
|
|
92
|
+
/** `read_local_storage`: declared localStorage keys. */
|
|
93
|
+
localStorageKeys?: string[];
|
|
94
|
+
/** `read_session_storage`: declared sessionStorage keys. */
|
|
95
|
+
sessionStorageKeys?: string[];
|
|
96
|
+
/** JSON-pointer extractions over localStorage values (implies `read_local_storage`). */
|
|
97
|
+
localStoragePointers?: StoragePointerDecl[];
|
|
98
|
+
/** JSON-pointer extractions over sessionStorage values (implies `read_session_storage`). */
|
|
99
|
+
sessionStoragePointers?: StoragePointerDecl[];
|
|
100
|
+
/** `capture_request_header`: (urlPattern, headerName) pairs to snapshot. */
|
|
101
|
+
captureHeaders?: CaptureHeaderDecl[];
|
|
102
|
+
/** `read_indexed_db`: declared IndexedDB scopes. */
|
|
103
|
+
indexedDbScopes?: IndexedDbScopeDecl[];
|
|
104
|
+
}
|
|
105
|
+
/** Options for {@link createBootstrapOpts}. */
|
|
106
|
+
export interface CreateBootstrapOptsArgs {
|
|
107
|
+
/**
|
|
108
|
+
* Trust-boundary hostname(s). A bare string is accepted for the common
|
|
109
|
+
* single-domain case; multi-domain MCPs pass an array (and must then specify
|
|
110
|
+
* `{ domain }` on each per-call request).
|
|
111
|
+
*/
|
|
112
|
+
domains: string | string[];
|
|
113
|
+
/**
|
|
114
|
+
* Documentation hint for *where* the bootstrap reads from (e.g.
|
|
115
|
+
* `portal.onehome.com`). Recorded on the returned fragment as a comment-level
|
|
116
|
+
* concern only — the actual gating is per declaration. Must be a subdomain of
|
|
117
|
+
* (or equal to) one of `domains` if provided.
|
|
118
|
+
*/
|
|
119
|
+
storageDomain?: string;
|
|
120
|
+
/** The capture/storage declarations to thread into capabilities + opts. */
|
|
121
|
+
bootstrap?: BootstrapDecls;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Assemble the multi-domain / bootstrap-declaration fragment of
|
|
125
|
+
* `FetchproxyServerOpts`. Spread the result into
|
|
126
|
+
* {@link createFetchproxyTransport} alongside `serverName` / `version`.
|
|
127
|
+
*
|
|
128
|
+
* The returned `capabilities` is derived from the declared bootstrap: each
|
|
129
|
+
* present declaration group adds exactly the capability that gates its verb
|
|
130
|
+
* (deduped). When no bootstrap declarations are given, `capabilities` is left
|
|
131
|
+
* unset so the server falls back to its default `['fetch']`.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* const opts = createBootstrapOpts({
|
|
135
|
+
* domains: 'onehome.com',
|
|
136
|
+
* storageDomain: 'portal.onehome.com',
|
|
137
|
+
* bootstrap: { captureHeaders: [{ urlPattern: 'https://portal.onehome.com/graphql*', headerName: 'Authorization' }] },
|
|
138
|
+
* });
|
|
139
|
+
* createFetchproxyTransport({ ...opts, serverName: 'onehome-mcp', version });
|
|
140
|
+
*/
|
|
141
|
+
export declare function createBootstrapOpts(args: CreateBootstrapOptsArgs): Pick<FetchproxyServerOpts, 'domains' | 'capabilities' | 'cookieKeys' | 'localStorageKeys' | 'sessionStorageKeys' | 'localStoragePointers' | 'sessionStoragePointers' | 'captureHeaders' | 'indexedDbScopes'>;
|
|
142
|
+
/** Discriminated classification of a tool-boundary error. */
|
|
143
|
+
export interface BridgeErrorInfo {
|
|
144
|
+
type: 'bridge_down' | 'timeout' | 'http' | 'protocol' | 'unknown';
|
|
145
|
+
message: string;
|
|
146
|
+
hint?: string;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Thin discriminator over the `@fetchproxy/server` typed-error hierarchy. Folds
|
|
150
|
+
* the fetchproxy `classifyBridgeError` (which returns a bare kind string) into a
|
|
151
|
+
* `{ type, message, hint? }` envelope, mapping fetchproxy's `'other'` to
|
|
152
|
+
* `'unknown'` and lifting the per-class remediation `hint` where one exists. The
|
|
153
|
+
* surfaced message is redacted + truncated.
|
|
154
|
+
*/
|
|
155
|
+
export declare function classifyBridgeError(err: unknown): BridgeErrorInfo;
|
|
156
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fetchproxy/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetchproxy transport adapter — the Pattern-A glue shared by every
|
|
3
|
+
* fetchproxy-backed MCP (redfin/zillow/compass/homes/onehome/resy/opentable/…).
|
|
4
|
+
*
|
|
5
|
+
* `@fetchproxy/server` already owns HTTP proxying, session bootstrap, and the
|
|
6
|
+
* bot-wall / backoff / deadline / concurrency / retry primitives. This module
|
|
7
|
+
* does NOT reimplement any of that — it re-exports the primitives so MCPs have a
|
|
8
|
+
* single import site, and provides two thin factories:
|
|
9
|
+
*
|
|
10
|
+
* - {@link createFetchproxyTransport} wraps a `FetchproxyServer` in the
|
|
11
|
+
* `start` / `close` / `status` lifecycle every MCP's transport interface
|
|
12
|
+
* expects, with optional debug-gated role logging.
|
|
13
|
+
* - {@link createBootstrapOpts} assembles a multi-domain / capture-header /
|
|
14
|
+
* storage-pointer declaration fragment of `FetchproxyServerOpts`, deriving
|
|
15
|
+
* the required `capabilities` from the declared bootstrap so callers can't
|
|
16
|
+
* forget to unlock the verb they declared.
|
|
17
|
+
*
|
|
18
|
+
* The adapter shape is identical across 12+ fetchproxy MCPs; collapsing it here
|
|
19
|
+
* keeps the per-row / concurrency / deadline helpers as re-exports rather than
|
|
20
|
+
* re-rolled code.
|
|
21
|
+
*/
|
|
22
|
+
import { FetchproxyServer, FetchproxyBridgeDownError, classifyBridgeError as classifyBridgeErrorKind, } from '@fetchproxy/server';
|
|
23
|
+
import { truncateErrorMessage, messageOf } from '../errors/index.js';
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Re-exports — single import site for the bridge primitives (design: re-export,
|
|
26
|
+
// never reimplement). MCPs import these from here instead of reaching into
|
|
27
|
+
// `@fetchproxy/server` directly, so a version bump is absorbed in one place.
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
export { FetchproxyServer, mapWithConcurrency, withDeadline, TokenBucket, classifyBotWall, retryOnceOnTimeout, FetchproxyProtocolError, FetchproxyHttpError, FetchproxyBridgeDownError, FetchproxyTimeoutError, classifyFetchError, backoffDelayMs, BRIDGE_CONCURRENCY, } from '@fetchproxy/server';
|
|
30
|
+
/**
|
|
31
|
+
* Matches a value that is *entirely* an unsubstituted shell-style placeholder
|
|
32
|
+
* (`${FOO}`). MCP hosts that forward an env block without expanding it leak
|
|
33
|
+
* these literals; treating them as unset is the canonical placeholder-leakage
|
|
34
|
+
* defense (mirrors `config.readEnvVar`).
|
|
35
|
+
*/
|
|
36
|
+
const PLACEHOLDER_RE = /^\$\{[^}]*\}$/;
|
|
37
|
+
/**
|
|
38
|
+
* Defensive truthiness check for a debug/flag env var: trims, and treats the
|
|
39
|
+
* empty string, `'undefined'`, `'null'`, and unexpanded `${...}` placeholders
|
|
40
|
+
* as unset (falsey). Any other non-empty value enables the flag.
|
|
41
|
+
*/
|
|
42
|
+
function envFlagEnabled(key, env = process.env) {
|
|
43
|
+
const raw = env[key];
|
|
44
|
+
if (typeof raw !== 'string')
|
|
45
|
+
return false;
|
|
46
|
+
const trimmed = raw.trim();
|
|
47
|
+
return (trimmed.length > 0 &&
|
|
48
|
+
trimmed !== 'undefined' &&
|
|
49
|
+
trimmed !== 'null' &&
|
|
50
|
+
!PLACEHOLDER_RE.test(trimmed));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Wrap a `FetchproxyServer` in the `start`/`close`/`status` lifecycle the
|
|
54
|
+
* per-MCP transport interface expects. The full `FetchproxyServerOpts` is
|
|
55
|
+
* forwarded verbatim (so `fetchTimeoutMs`, `keepAliveIntervalMs`, capture
|
|
56
|
+
* declarations, etc. all pass through); the only added knob is `debugEnvVar`.
|
|
57
|
+
*
|
|
58
|
+
* Returns the wrapper typed as the caller's `T` (defaulting to
|
|
59
|
+
* {@link FetchproxyTransport}) so it can satisfy a structurally-compatible
|
|
60
|
+
* per-MCP interface without that interface importing this package.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* const transport = createFetchproxyTransport<RedfinTransport>({
|
|
64
|
+
* serverName: 'redfin-mcp', version, domains: ['redfin.com'],
|
|
65
|
+
* debugEnvVar: 'REDFIN_DEBUG',
|
|
66
|
+
* });
|
|
67
|
+
*/
|
|
68
|
+
export function createFetchproxyTransport(opts) {
|
|
69
|
+
const { debugEnvVar, env, ...serverOpts } = opts;
|
|
70
|
+
if (!serverOpts.serverName || serverOpts.serverName.trim().length === 0) {
|
|
71
|
+
throw new Error('createFetchproxyTransport: `serverName` is required.');
|
|
72
|
+
}
|
|
73
|
+
if (!Array.isArray(serverOpts.domains) || serverOpts.domains.length === 0) {
|
|
74
|
+
throw new Error('createFetchproxyTransport: at least one `domains` entry is required.');
|
|
75
|
+
}
|
|
76
|
+
const server = new FetchproxyServer(serverOpts);
|
|
77
|
+
const debug = debugEnvVar !== undefined && envFlagEnabled(debugEnvVar, env ?? process.env);
|
|
78
|
+
const transport = {
|
|
79
|
+
server,
|
|
80
|
+
get role() {
|
|
81
|
+
return server.role;
|
|
82
|
+
},
|
|
83
|
+
async start() {
|
|
84
|
+
await server.listen();
|
|
85
|
+
if (debug) {
|
|
86
|
+
// Stderr only — stdio MCP transports reserve stdout for JSON-RPC.
|
|
87
|
+
console.error(`[${serverOpts.serverName}:bridge] listening ` +
|
|
88
|
+
`(role=${server.role ?? 'unknown'}, version=${serverOpts.version})`);
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
async close() {
|
|
92
|
+
await server.close();
|
|
93
|
+
},
|
|
94
|
+
status() {
|
|
95
|
+
return server.bridgeHealth();
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
return transport;
|
|
99
|
+
}
|
|
100
|
+
function nonEmpty(arr) {
|
|
101
|
+
return Array.isArray(arr) && arr.length > 0;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Assemble the multi-domain / bootstrap-declaration fragment of
|
|
105
|
+
* `FetchproxyServerOpts`. Spread the result into
|
|
106
|
+
* {@link createFetchproxyTransport} alongside `serverName` / `version`.
|
|
107
|
+
*
|
|
108
|
+
* The returned `capabilities` is derived from the declared bootstrap: each
|
|
109
|
+
* present declaration group adds exactly the capability that gates its verb
|
|
110
|
+
* (deduped). When no bootstrap declarations are given, `capabilities` is left
|
|
111
|
+
* unset so the server falls back to its default `['fetch']`.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* const opts = createBootstrapOpts({
|
|
115
|
+
* domains: 'onehome.com',
|
|
116
|
+
* storageDomain: 'portal.onehome.com',
|
|
117
|
+
* bootstrap: { captureHeaders: [{ urlPattern: 'https://portal.onehome.com/graphql*', headerName: 'Authorization' }] },
|
|
118
|
+
* });
|
|
119
|
+
* createFetchproxyTransport({ ...opts, serverName: 'onehome-mcp', version });
|
|
120
|
+
*/
|
|
121
|
+
export function createBootstrapOpts(args) {
|
|
122
|
+
const domains = Array.isArray(args.domains) ? args.domains : [args.domains];
|
|
123
|
+
if (domains.length === 0 || domains.some((d) => !d || d.trim().length === 0)) {
|
|
124
|
+
throw new Error('createBootstrapOpts: at least one non-empty `domains` entry is required.');
|
|
125
|
+
}
|
|
126
|
+
if (args.storageDomain !== undefined) {
|
|
127
|
+
const host = args.storageDomain.trim();
|
|
128
|
+
const ok = domains.some((d) => host === d || host.endsWith(`.${d}`));
|
|
129
|
+
if (!ok) {
|
|
130
|
+
throw new Error(`createBootstrapOpts: storageDomain '${args.storageDomain}' is not within declared domains [${domains.join(', ')}].`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const b = args.bootstrap ?? {};
|
|
134
|
+
const capabilities = new Set();
|
|
135
|
+
if (nonEmpty(b.cookieKeys))
|
|
136
|
+
capabilities.add('read_cookies');
|
|
137
|
+
if (nonEmpty(b.localStorageKeys) || nonEmpty(b.localStoragePointers)) {
|
|
138
|
+
capabilities.add('read_local_storage');
|
|
139
|
+
}
|
|
140
|
+
if (nonEmpty(b.sessionStorageKeys) || nonEmpty(b.sessionStoragePointers)) {
|
|
141
|
+
capabilities.add('read_session_storage');
|
|
142
|
+
}
|
|
143
|
+
if (nonEmpty(b.captureHeaders))
|
|
144
|
+
capabilities.add('capture_request_header');
|
|
145
|
+
if (nonEmpty(b.indexedDbScopes))
|
|
146
|
+
capabilities.add('read_indexed_db');
|
|
147
|
+
return {
|
|
148
|
+
domains,
|
|
149
|
+
...(capabilities.size > 0 ? { capabilities: [...capabilities] } : {}),
|
|
150
|
+
...(nonEmpty(b.cookieKeys) ? { cookieKeys: b.cookieKeys } : {}),
|
|
151
|
+
...(nonEmpty(b.localStorageKeys) ? { localStorageKeys: b.localStorageKeys } : {}),
|
|
152
|
+
...(nonEmpty(b.sessionStorageKeys) ? { sessionStorageKeys: b.sessionStorageKeys } : {}),
|
|
153
|
+
...(nonEmpty(b.localStoragePointers) ? { localStoragePointers: b.localStoragePointers } : {}),
|
|
154
|
+
...(nonEmpty(b.sessionStoragePointers) ? { sessionStoragePointers: b.sessionStoragePointers } : {}),
|
|
155
|
+
...(nonEmpty(b.captureHeaders) ? { captureHeaders: b.captureHeaders } : {}),
|
|
156
|
+
...(nonEmpty(b.indexedDbScopes) ? { indexedDbScopes: b.indexedDbScopes } : {}),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Thin discriminator over the `@fetchproxy/server` typed-error hierarchy. Folds
|
|
161
|
+
* the fetchproxy `classifyBridgeError` (which returns a bare kind string) into a
|
|
162
|
+
* `{ type, message, hint? }` envelope, mapping fetchproxy's `'other'` to
|
|
163
|
+
* `'unknown'` and lifting the per-class remediation `hint` where one exists. The
|
|
164
|
+
* surfaced message is redacted + truncated.
|
|
165
|
+
*/
|
|
166
|
+
export function classifyBridgeError(err) {
|
|
167
|
+
const kind = classifyBridgeErrorKind(err);
|
|
168
|
+
const message = truncateErrorMessage(messageOf(err));
|
|
169
|
+
switch (kind) {
|
|
170
|
+
case 'timeout':
|
|
171
|
+
return {
|
|
172
|
+
type: 'timeout',
|
|
173
|
+
message,
|
|
174
|
+
hint: 'The fetchproxy bridge timed out. Check the browser tab is open and responsive, then retry.',
|
|
175
|
+
};
|
|
176
|
+
case 'bridge_down': {
|
|
177
|
+
const hint = err instanceof FetchproxyBridgeDownError ? err.hint : undefined;
|
|
178
|
+
return {
|
|
179
|
+
type: 'bridge_down',
|
|
180
|
+
message,
|
|
181
|
+
hint: hint ?? 'The fetchproxy browser bridge is offline. Open a signed-in tab so the extension can relay the request.',
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
case 'http':
|
|
185
|
+
return { type: 'http', message };
|
|
186
|
+
case 'protocol':
|
|
187
|
+
return {
|
|
188
|
+
type: 'protocol',
|
|
189
|
+
message,
|
|
190
|
+
hint: 'The fetchproxy bridge could not relay the request (e.g. no signed-in tab or denied domain).',
|
|
191
|
+
};
|
|
192
|
+
case 'other':
|
|
193
|
+
default:
|
|
194
|
+
return { type: 'unknown', message };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fetchproxy/index.ts"],"names":[],"mappings":"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"}
|