@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.
Files changed (50) hide show
  1. package/README.md +235 -0
  2. package/dist/auth/index.d.ts +223 -0
  3. package/dist/auth/index.d.ts.map +1 -0
  4. package/dist/auth/index.js +267 -0
  5. package/dist/auth/index.js.map +1 -0
  6. package/dist/config/index.d.ts +86 -0
  7. package/dist/config/index.d.ts.map +1 -0
  8. package/dist/config/index.js +121 -0
  9. package/dist/config/index.js.map +1 -0
  10. package/dist/errors/index.d.ts +90 -0
  11. package/dist/errors/index.d.ts.map +1 -0
  12. package/dist/errors/index.js +157 -0
  13. package/dist/errors/index.js.map +1 -0
  14. package/dist/fetchproxy/index.d.ts +156 -0
  15. package/dist/fetchproxy/index.d.ts.map +1 -0
  16. package/dist/fetchproxy/index.js +197 -0
  17. package/dist/fetchproxy/index.js.map +1 -0
  18. package/dist/html/index.d.ts +142 -0
  19. package/dist/html/index.d.ts.map +1 -0
  20. package/dist/html/index.js +321 -0
  21. package/dist/html/index.js.map +1 -0
  22. package/dist/http/index.d.ts +202 -0
  23. package/dist/http/index.d.ts.map +1 -0
  24. package/dist/http/index.js +341 -0
  25. package/dist/http/index.js.map +1 -0
  26. package/dist/index.d.ts +23 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +23 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/response/index.d.ts +22 -0
  31. package/dist/response/index.d.ts.map +1 -0
  32. package/dist/response/index.js +61 -0
  33. package/dist/response/index.js.map +1 -0
  34. package/dist/server/index.d.ts +109 -0
  35. package/dist/server/index.d.ts.map +1 -0
  36. package/dist/server/index.js +95 -0
  37. package/dist/server/index.js.map +1 -0
  38. package/dist/session/index.d.ts +233 -0
  39. package/dist/session/index.d.ts.map +1 -0
  40. package/dist/session/index.js +404 -0
  41. package/dist/session/index.js.map +1 -0
  42. package/dist/test/index.d.ts +124 -0
  43. package/dist/test/index.d.ts.map +1 -0
  44. package/dist/test/index.js +181 -0
  45. package/dist/test/index.js.map +1 -0
  46. package/dist/zod/index.d.ts +130 -0
  47. package/dist/zod/index.d.ts.map +1 -0
  48. package/dist/zod/index.js +184 -0
  49. package/dist/zod/index.js.map +1 -0
  50. 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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"}