@graphorin/mcp 0.6.1 → 0.7.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 (70) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +64 -3
  3. package/dist/client/adapt-result.d.ts +9 -1
  4. package/dist/client/adapt-result.d.ts.map +1 -1
  5. package/dist/client/adapt-result.js +28 -10
  6. package/dist/client/adapt-result.js.map +1 -1
  7. package/dist/client/client-handlers.js +7 -1
  8. package/dist/client/client-handlers.js.map +1 -1
  9. package/dist/client/client.d.ts.map +1 -1
  10. package/dist/client/client.js +45 -70
  11. package/dist/client/client.js.map +1 -1
  12. package/dist/client/inbound-filters.js +101 -1
  13. package/dist/client/inbound-filters.js.map +1 -1
  14. package/dist/client/index.d.ts +2 -1
  15. package/dist/client/index.js +2 -1
  16. package/dist/client/managed.d.ts +35 -0
  17. package/dist/client/managed.d.ts.map +1 -0
  18. package/dist/client/managed.js +136 -0
  19. package/dist/client/managed.js.map +1 -0
  20. package/dist/client/mcp-resource-reader.js +1 -1
  21. package/dist/client/mcp-resource-reader.js.map +1 -1
  22. package/dist/client/to-tools-run.js +119 -0
  23. package/dist/client/to-tools-run.js.map +1 -0
  24. package/dist/client/to-tools.d.ts +8 -0
  25. package/dist/client/to-tools.d.ts.map +1 -1
  26. package/dist/client/to-tools.js +27 -4
  27. package/dist/client/to-tools.js.map +1 -1
  28. package/dist/client/types.d.ts +12 -3
  29. package/dist/client/types.d.ts.map +1 -1
  30. package/dist/errors/index.d.ts +3 -3
  31. package/dist/errors/index.js +1 -1
  32. package/dist/errors/index.js.map +1 -1
  33. package/dist/helpers/identity.d.ts +11 -0
  34. package/dist/helpers/identity.d.ts.map +1 -1
  35. package/dist/helpers/identity.js +13 -2
  36. package/dist/helpers/identity.js.map +1 -1
  37. package/dist/index.d.ts +3 -2
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +3 -4
  40. package/dist/index.js.map +1 -1
  41. package/dist/package.js +1 -1
  42. package/dist/package.js.map +1 -1
  43. package/dist/registry/json-schema.js +26 -8
  44. package/dist/registry/json-schema.js.map +1 -1
  45. package/dist/transport/types.d.ts +9 -0
  46. package/package.json +13 -12
  47. package/src/client/adapt-result.ts +302 -0
  48. package/src/client/client-handlers.ts +215 -0
  49. package/src/client/client.ts +725 -0
  50. package/src/client/defer-loading.ts +108 -0
  51. package/src/client/inbound-filters.ts +246 -0
  52. package/src/client/index.ts +42 -0
  53. package/src/client/managed.ts +222 -0
  54. package/src/client/mcp-resource-reader.ts +183 -0
  55. package/src/client/pinning.ts +48 -0
  56. package/src/client/to-tools-run.ts +178 -0
  57. package/src/client/to-tools.ts +294 -0
  58. package/src/client/transport-factory.ts +117 -0
  59. package/src/client/types.ts +422 -0
  60. package/src/errors/index.ts +170 -0
  61. package/src/helpers/identity.ts +128 -0
  62. package/src/helpers/index.ts +8 -0
  63. package/src/helpers/validate-config.ts +95 -0
  64. package/src/index.ts +49 -0
  65. package/src/oauth/bridge.ts +143 -0
  66. package/src/oauth/index.ts +18 -0
  67. package/src/oauth/library.ts +61 -0
  68. package/src/registry/json-schema.ts +402 -0
  69. package/src/transport/index.ts +15 -0
  70. package/src/transport/types.ts +108 -0
@@ -0,0 +1,422 @@
1
+ /**
2
+ * Public types for {@link createMCPClient} and the {@link MCPClient}
3
+ * surface.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+
8
+ import type {
9
+ InboundSanitizationPolicy,
10
+ ModelHint,
11
+ ModelSpec,
12
+ SideEffectClass,
13
+ Tool,
14
+ TruncationStrategy,
15
+ } from '@graphorin/core';
16
+ import type { CollisionStrategy } from '@graphorin/tools/registry';
17
+
18
+ import type { OAuthAuthorizationProvider } from '../oauth/bridge.js';
19
+ import type { MCPTransportConfig, ServerIdentity } from '../transport/types.js';
20
+
21
+ /**
22
+ * Options accepted by {@link createMCPClient}.
23
+ *
24
+ * @stable
25
+ */
26
+ export interface CreateMCPClientOptions {
27
+ readonly transport: MCPTransportConfig;
28
+ /**
29
+ * Pre-built OAuth provider that resolves the bearer header on every
30
+ * request. Mutually exclusive with {@link bearerToken}.
31
+ */
32
+ readonly authProvider?: OAuthAuthorizationProvider;
33
+ /** Pre-shared bearer token (rare; prefer {@link authProvider}). */
34
+ readonly bearerToken?: string;
35
+ /**
36
+ * Per-client default for the strategy-aware tool registry. Falls
37
+ * through to the per-call value on {@link MCPClient.toTools}.
38
+ *
39
+ * @default `'auto-prefix'`
40
+ */
41
+ readonly collisionStrategy?: CollisionStrategy;
42
+ /** Per-client priority value used by the `'priority'` strategy. */
43
+ readonly priority?: number;
44
+ /** Operator-supplied server identity overrides. */
45
+ readonly serverInfoName?: string;
46
+ /** Operator-supplied logger. */
47
+ readonly logger?: (
48
+ level: 'debug' | 'info' | 'warn' | 'error',
49
+ message: string,
50
+ fields?: Record<string, unknown>,
51
+ ) => void;
52
+ /** Operator-supplied client name advertised to the server on `initialize`. */
53
+ readonly clientName?: string;
54
+ /** Operator-supplied client version advertised to the server on `initialize`. */
55
+ readonly clientVersion?: string;
56
+ /**
57
+ * Skip the deprecated-transport WARN log. Useful for tests + the
58
+ * standalone server's startup banner.
59
+ *
60
+ * @default `false`
61
+ */
62
+ readonly suppressDeprecatedTransportWarning?: boolean;
63
+ /**
64
+ * Handler for server-initiated **elicitation** (`elicitation/create`)
65
+ * requests - the server asks the human for structured input mid-call
66
+ * (WI-13 / P2-2). When provided, the client advertises the
67
+ * `elicitation` capability and routes requests here; back it with a
68
+ * HITL surface (e.g. a CLI prompt or the agent's approval channel).
69
+ * When omitted, the capability is **not** advertised and a conforming
70
+ * server will not elicit (gated; no implicit prompting).
71
+ *
72
+ * Note: an elicitation arrives while a `callTool(...)` JSON-RPC request
73
+ * is in flight, so the handler resolves in-process - it does not
74
+ * durably suspend a Graphorin run. Durable-suspend elicitation across
75
+ * the request lifetime is a follow-up.
76
+ */
77
+ readonly elicitation?: MCPElicitationHandler;
78
+ /**
79
+ * Handler for server-initiated **sampling** (`sampling/createMessage`)
80
+ * requests - the server asks the client's model to generate a
81
+ * completion (WI-13 / P2-2). When provided, the client advertises the
82
+ * `sampling` capability and routes requests here; back it with a
83
+ * `Provider`. The request messages are **MCP-derived (untrusted)**, so
84
+ * the backing provider should apply the usual sensitivity/redaction
85
+ * middleware. When omitted, the capability is **not** advertised
86
+ * (gated).
87
+ */
88
+ readonly sampling?: MCPSamplingHandler;
89
+ /**
90
+ * mcp-skills-10: called when the underlying transport closes (a
91
+ * stdio child dying, an HTTP session dropping beyond the SDK's SSE
92
+ * resume). Without it a disconnect is observable only as
93
+ * `MCPProtocolError`s on subsequent calls. The client does NOT
94
+ * auto-reconnect - rebuild it via `createMCPClient(...)` (and re-run
95
+ * `toTools()` for the drift diff) when this fires, or use the W-080
96
+ * `createManagedMCPClient(...)` wrapper, which does exactly that
97
+ * automatically (there the operator callback fires only when the
98
+ * wrapper's reconnect attempts are exhausted).
99
+ */
100
+ readonly onTransportClose?: (info: { readonly server: string }) => void;
101
+ /** mcp-skills-10: called on transport-level errors (see {@link onTransportClose}). */
102
+ readonly onTransportError?: (error: Error, info: { readonly server: string }) => void;
103
+ }
104
+
105
+ /**
106
+ * Server-initiated elicitation request surfaced to the operator's HITL
107
+ * handler.
108
+ *
109
+ * @stable
110
+ */
111
+ export interface MCPElicitationRequest {
112
+ /** Human-readable prompt the server wants answered. */
113
+ readonly message: string;
114
+ /** JSON-Schema-like shape (`{ type: 'object', properties, required? }`) for the requested input. */
115
+ readonly requestedSchema: Readonly<Record<string, unknown>>;
116
+ }
117
+
118
+ /**
119
+ * Operator response to an {@link MCPElicitationRequest}. `accept` returns
120
+ * the collected flat values; `decline`/`cancel` carry no content.
121
+ *
122
+ * @stable
123
+ */
124
+ export type MCPElicitationResult =
125
+ | {
126
+ readonly action: 'accept';
127
+ readonly content?: Readonly<
128
+ Record<string, string | number | boolean | ReadonlyArray<string>>
129
+ >;
130
+ }
131
+ | { readonly action: 'decline' }
132
+ | { readonly action: 'cancel' };
133
+
134
+ /** Handler for server-initiated elicitation requests. */
135
+ export type MCPElicitationHandler = (
136
+ request: MCPElicitationRequest,
137
+ opts: { readonly signal?: AbortSignal },
138
+ ) => MCPElicitationResult | Promise<MCPElicitationResult>;
139
+
140
+ /** A single content block carried by a sampling message or result. */
141
+ export type MCPSamplingContent =
142
+ | { readonly type: 'text'; readonly text: string }
143
+ | { readonly type: 'image'; readonly data: string; readonly mimeType: string }
144
+ | { readonly type: 'audio'; readonly data: string; readonly mimeType: string };
145
+
146
+ /** A message in a sampling conversation. */
147
+ export interface MCPSamplingMessage {
148
+ readonly role: 'user' | 'assistant';
149
+ /**
150
+ * Every content block of the SDK message (MC-13) - previously only
151
+ * the FIRST block survived, silently dropping e.g. the image in a
152
+ * text+image message before it reached the operator's handler.
153
+ */
154
+ readonly content: ReadonlyArray<MCPSamplingContent>;
155
+ }
156
+
157
+ /**
158
+ * Server-initiated sampling request surfaced to the operator's handler
159
+ * (typically backed by a `Provider`).
160
+ *
161
+ * @stable
162
+ */
163
+ export interface MCPSamplingRequest {
164
+ readonly messages: ReadonlyArray<MCPSamplingMessage>;
165
+ readonly systemPrompt?: string;
166
+ readonly maxTokens: number;
167
+ readonly temperature?: number;
168
+ readonly stopSequences?: ReadonlyArray<string>;
169
+ readonly modelPreferences?: {
170
+ readonly hints?: ReadonlyArray<{ readonly name?: string }>;
171
+ readonly costPriority?: number;
172
+ readonly speedPriority?: number;
173
+ readonly intelligencePriority?: number;
174
+ };
175
+ readonly includeContext?: 'none' | 'thisServer' | 'allServers';
176
+ }
177
+
178
+ /**
179
+ * Operator response to an {@link MCPSamplingRequest}.
180
+ *
181
+ * @stable
182
+ */
183
+ export interface MCPSamplingResult {
184
+ readonly role: 'assistant';
185
+ readonly content: MCPSamplingContent;
186
+ readonly model: string;
187
+ readonly stopReason?: string;
188
+ }
189
+
190
+ /** Handler for server-initiated sampling requests. */
191
+ export type MCPSamplingHandler = (
192
+ request: MCPSamplingRequest,
193
+ opts: { readonly signal?: AbortSignal },
194
+ ) => MCPSamplingResult | Promise<MCPSamplingResult>;
195
+
196
+ /**
197
+ * Per-MCP-server `toTools()` options.
198
+ *
199
+ * @stable
200
+ */
201
+ export interface MCPToToolsOptions {
202
+ /** Filter the produced tools. */
203
+ readonly filter?: (tool: MCPToolDefinition) => boolean;
204
+ /** Tool-name namespace prepended to every produced tool. */
205
+ readonly namespace?: string;
206
+ /**
207
+ * Per-server inbound prompt-injection sanitization policy override.
208
+ * Defaults to `'detect-and-strip-and-wrap'` for MCP-derived tools.
209
+ */
210
+ readonly inboundSanitization?: InboundSanitizationPolicy;
211
+ /**
212
+ * Per-call timeout (ms) applied to every adapted tool's
213
+ * `client.callTool` invocation (MC-3/MC-5). Default: the SDK default.
214
+ */
215
+ readonly callTimeoutMs?: number;
216
+ /**
217
+ * Operator-pinned definition fingerprints by MCP tool name (MC-6) -
218
+ * the `__definitionHash` stamped on a previously approved snapshot.
219
+ * A mismatch means the server changed the definition behind the name.
220
+ */
221
+ readonly pinnedFingerprints?: Readonly<Record<string, string>>;
222
+ /**
223
+ * What to do on a pinned-fingerprint mismatch (MC-6). `'warn'`
224
+ * (default without a {@link pinStore}) audits
225
+ * `mcp.tools.pin-mismatch.total` and continues; `'reject'` (the
226
+ * default WHEN a `pinStore` is wired - a persisted first approval is
227
+ * an explicit trust decision) throws `MCPToolPinningError`.
228
+ *
229
+ * W-079: `'accept-and-update'` is the documented operator path to
230
+ * ACCEPT a legitimate catalogue change: after the comparison (and its
231
+ * counters/logs) the store is overwritten with the current snapshot
232
+ * (`mcp.tools.pins-updated.total`), so subsequent calls are clean -
233
+ * an explicit re-trust decision, never a silent default.
234
+ */
235
+ readonly onPinMismatch?: 'warn' | 'reject' | 'accept-and-update';
236
+ /**
237
+ * C6: durable trust-on-first-use pin storage. On the first `toTools()`
238
+ * the current definition fingerprints are RECORDED
239
+ * (`mcp.tools.pins-recorded.total`); on every later call they are
240
+ * COMPARED - drift is handled per {@link onPinMismatch}, which
241
+ * defaults to `'reject'` when a store is present (the rug-pull
242
+ * defense). Explicit `pinnedFingerprints` win over the store.
243
+ */
244
+ readonly pinStore?: MCPPinStore;
245
+ /**
246
+ * Per-server `defer_loading` override. When unset and
247
+ * `listTools()` returns more than `deferLoadingThreshold` entries
248
+ * the auto-default flips deferral on for every tool from this
249
+ * server.
250
+ */
251
+ readonly defer_loading?: boolean;
252
+ /** Auto-default trigger threshold. Defaults to `10`. */
253
+ readonly deferLoadingThreshold?: number;
254
+ /** Per-server token cap override applied at registration. */
255
+ readonly maxResultTokens?: number;
256
+ /** Per-server truncation strategy override applied at registration. */
257
+ readonly truncationStrategy?: TruncationStrategy;
258
+ /** Tool-name -> per-tool side-effect class override map. */
259
+ readonly sideEffectClassByTool?: Readonly<Record<string, SideEffectClass>>;
260
+ /** Tool-name -> per-tool preferred-model override map. */
261
+ readonly preferredModelByTool?: Readonly<Record<string, ModelHint | ModelSpec>>;
262
+ }
263
+
264
+ /**
265
+ * Single MCP tool descriptor returned by `listTools()`. Mirrors the
266
+ * MCP spec subset we consume.
267
+ *
268
+ * @stable
269
+ */
270
+ export interface MCPToolDefinition {
271
+ readonly name: string;
272
+ readonly description: string;
273
+ readonly inputSchema: Readonly<Record<string, unknown>>;
274
+ readonly outputSchema?: Readonly<Record<string, unknown>>;
275
+ readonly title?: string;
276
+ }
277
+
278
+ /** Resource descriptor returned by `listResources()`. */
279
+ export interface MCPResourceDefinition {
280
+ readonly uri: string;
281
+ readonly name?: string;
282
+ readonly description?: string;
283
+ readonly mimeType?: string;
284
+ }
285
+
286
+ /** Prompt descriptor returned by `listPrompts()`. */
287
+ export interface MCPPromptDefinition {
288
+ readonly name: string;
289
+ readonly description?: string;
290
+ readonly arguments?: ReadonlyArray<{
291
+ readonly name: string;
292
+ readonly description?: string;
293
+ readonly required?: boolean;
294
+ }>;
295
+ }
296
+
297
+ /** Resource content returned by `readResource(...)`. */
298
+ export interface MCPResourceContent {
299
+ readonly uri: string;
300
+ readonly mimeType?: string;
301
+ readonly text?: string;
302
+ readonly blob?: string;
303
+ }
304
+
305
+ /** Tool result envelope returned by `callTool(...)`. */
306
+ export interface MCPCallToolResult {
307
+ readonly content: ReadonlyArray<MCPContentPart>;
308
+ readonly structuredContent?: Readonly<Record<string, unknown>>;
309
+ readonly isError?: boolean;
310
+ }
311
+
312
+ /** Discriminated union over MCP content parts. */
313
+ export type MCPContentPart =
314
+ | { readonly type: 'text'; readonly text: string }
315
+ | { readonly type: 'image'; readonly data: string; readonly mimeType: string }
316
+ | { readonly type: 'audio'; readonly data: string; readonly mimeType: string }
317
+ | {
318
+ readonly type: 'resource';
319
+ readonly resource: {
320
+ readonly uri: string;
321
+ readonly text?: string;
322
+ readonly blob?: string;
323
+ readonly mimeType?: string;
324
+ };
325
+ }
326
+ | {
327
+ /**
328
+ * A link to a resource the server can serve on demand (MCP
329
+ * `resource_link`). Unlike an embedded `resource`, the body is
330
+ * **not** inlined: the adapter surfaces a preview + the `uri` as a
331
+ * result handle so the model fetches it via `read_result` only
332
+ * when needed (WI-13 / P2-2, ties to WI-10 result handles).
333
+ */
334
+ readonly type: 'resource_link';
335
+ readonly uri: string;
336
+ readonly name: string;
337
+ readonly title?: string;
338
+ readonly description?: string;
339
+ readonly mimeType?: string;
340
+ readonly size?: number;
341
+ };
342
+
343
+ /**
344
+ * Public surface of an active MCP client.
345
+ *
346
+ * @stable
347
+ */
348
+ export interface MCPClient {
349
+ /** Stable identifier - derived from the transport. */
350
+ readonly id: string;
351
+ /** Server-advertised information from the `initialize` handshake. */
352
+ readonly serverInfo: { readonly name: string; readonly version: string };
353
+ /** Server identity descriptor consumed by the tool-registry resolver. */
354
+ readonly serverIdentity: ServerIdentity;
355
+ /** Per-client default collision strategy. */
356
+ readonly collisionStrategy: CollisionStrategy;
357
+ /** Per-client priority value used by the `'priority'` strategy. */
358
+ readonly priority?: number;
359
+ /**
360
+ * Whether the Streamable HTTP server assigned an `Mcp-Session-Id`
361
+ * at `initialize` time (MC-9). A session id means stateful routing -
362
+ * it is NOT a replay guarantee: per the Streamable HTTP spec,
363
+ * event replay is the SERVER's responsibility, and the SDK
364
+ * transport already auto-reconnects with `Last-Event-ID` when the
365
+ * server supports it.
366
+ */
367
+ readonly sessionIdPresent: boolean;
368
+ /** @deprecated Alias of {@link sessionIdPresent} - same value, misleading name. */
369
+ readonly resumable: boolean;
370
+
371
+ listTools(opts?: { signal?: AbortSignal }): Promise<ReadonlyArray<MCPToolDefinition>>;
372
+ listResources(opts?: { signal?: AbortSignal }): Promise<ReadonlyArray<MCPResourceDefinition>>;
373
+ listPrompts(opts?: { signal?: AbortSignal }): Promise<ReadonlyArray<MCPPromptDefinition>>;
374
+ callTool(
375
+ name: string,
376
+ args: unknown,
377
+ opts?: { signal?: AbortSignal; timeoutMs?: number },
378
+ ): Promise<MCPCallToolResult>;
379
+ /**
380
+ * First content item of the resource. mcp-skills-11: a multi-content
381
+ * response (one URI can yield several items) is truncated to the
382
+ * FIRST item - a WARN + counter fire when that happens; use
383
+ * {@link readResourceContents} for the full array.
384
+ */
385
+ readResource(uri: string, opts?: { signal?: AbortSignal }): Promise<MCPResourceContent>;
386
+ /** Every content item of the resource (mcp-skills-11). */
387
+ readResourceContents(
388
+ uri: string,
389
+ opts?: { signal?: AbortSignal },
390
+ ): Promise<ReadonlyArray<MCPResourceContent>>;
391
+ getPrompt(
392
+ name: string,
393
+ args?: unknown,
394
+ opts?: { signal?: AbortSignal },
395
+ ): Promise<{ readonly messages: ReadonlyArray<MCPPromptMessage> }>;
396
+ toTools(opts?: MCPToToolsOptions): Promise<ReadonlyArray<Tool>>;
397
+ close(): Promise<void>;
398
+ }
399
+
400
+ /** Single prompt message returned by `getPrompt(...)`. */
401
+ export interface MCPPromptMessage {
402
+ readonly role: 'user' | 'assistant';
403
+ readonly content: MCPContentPart;
404
+ }
405
+
406
+ /**
407
+ * C6: durable storage for trust-on-first-use MCP tool pins. Keyed by the
408
+ * server identity id; values are `toolName -> sha256 fingerprint` maps
409
+ * (the same shape as `pinnedFingerprints`). Implementations may be sync
410
+ * or async - a JSON file, a SQLite table, a secret store.
411
+ *
412
+ * @stable
413
+ */
414
+ export interface MCPPinStore {
415
+ get(
416
+ serverId: string,
417
+ ):
418
+ | Readonly<Record<string, string>>
419
+ | undefined
420
+ | Promise<Readonly<Record<string, string>> | undefined>;
421
+ set(serverId: string, fingerprints: Readonly<Record<string, string>>): void | Promise<void>;
422
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Typed error union for the `@graphorin/mcp` package.
3
+ *
4
+ * Every error carries a stable lowercase {@link MCPErrorKind}
5
+ * discriminator, an actionable {@link GraphorinMCPError.hint} field where
6
+ * applicable, and an optional structured `metadata` bag the audit
7
+ * emitter persists alongside the standard `runId` / `sessionId`
8
+ * context.
9
+ *
10
+ * @packageDocumentation
11
+ */
12
+
13
+ /**
14
+ * Discriminator union for every error class produced by
15
+ * `@graphorin/mcp`. New kinds extend this union; never throw plain
16
+ * `Error` from framework code.
17
+ *
18
+ * @stable
19
+ */
20
+ export type MCPErrorKind =
21
+ | 'connection-failed'
22
+ | 'protocol-error'
23
+ | 'auth-error'
24
+ | 'tool-not-found'
25
+ | 'tool-execution'
26
+ | 'pin-mismatch'
27
+ | 'call-timeout'
28
+ | 'cancelled'
29
+ | 'invalid-config'
30
+ | 'session-lost'
31
+ | 'transport-not-supported'
32
+ | 'transport-resumable-not-supported';
33
+
34
+ /** Common metadata bag attached to every {@link GraphorinMCPError}. */
35
+ export interface MCPErrorMetadata {
36
+ readonly server?: string;
37
+ readonly tool?: string;
38
+ readonly transport?: 'stdio' | 'streamable-http' | 'sse';
39
+ readonly cause?: unknown;
40
+ readonly httpStatus?: number;
41
+ readonly sessionId?: string;
42
+ readonly lastEventId?: string;
43
+ }
44
+
45
+ /**
46
+ * Base class for every typed error produced by `@graphorin/mcp`.
47
+ *
48
+ * @stable
49
+ */
50
+ export abstract class GraphorinMCPError extends Error {
51
+ /** Lowercase discriminator. */
52
+ public abstract readonly kind: MCPErrorKind;
53
+ /** Optional remediation hint surfaced in CLI output. */
54
+ public readonly hint?: string;
55
+ /** Sanitized metadata; never carries secret material. */
56
+ public readonly metadata: Readonly<MCPErrorMetadata>;
57
+ /** Underlying cause (chained errors). */
58
+ public override readonly cause?: unknown;
59
+
60
+ public constructor(
61
+ message: string,
62
+ opts: {
63
+ readonly hint?: string;
64
+ readonly metadata?: MCPErrorMetadata;
65
+ readonly cause?: unknown;
66
+ } = {},
67
+ ) {
68
+ super(message);
69
+ this.name = new.target.name;
70
+ if (opts.hint !== undefined) this.hint = opts.hint;
71
+ this.metadata = Object.freeze({ ...(opts.metadata ?? {}) });
72
+ if (opts.cause !== undefined) this.cause = opts.cause;
73
+ }
74
+ }
75
+
76
+ /** Raised when a transport fails to connect or is dropped unexpectedly. */
77
+ export class MCPConnectionError extends GraphorinMCPError {
78
+ public readonly kind = 'connection-failed' as const;
79
+ }
80
+
81
+ /** Raised on JSON-RPC / MCP protocol-level errors. */
82
+ export class MCPProtocolError extends GraphorinMCPError {
83
+ public readonly kind = 'protocol-error' as const;
84
+ }
85
+
86
+ /** Raised when an authentication / authorization step fails. */
87
+ export class MCPAuthError extends GraphorinMCPError {
88
+ public readonly kind = 'auth-error' as const;
89
+ }
90
+
91
+ /** Raised when `MCPClient.callTool` is invoked for an unknown tool. */
92
+ export class MCPToolNotFoundError extends GraphorinMCPError {
93
+ public readonly kind = 'tool-not-found' as const;
94
+ }
95
+
96
+ /**
97
+ * Raised when a pinned tool-definition fingerprint does not match the
98
+ * server's current definition and `onPinMismatch: 'reject'` is set
99
+ * (MC-6) - the approve-then-swap rug-pull posture.
100
+ */
101
+ export class MCPToolPinningError extends GraphorinMCPError {
102
+ public readonly kind = 'pin-mismatch' as const;
103
+ }
104
+
105
+ /**
106
+ * Raised when the MCP server reports a tool-level failure
107
+ * (`CallToolResult.isError === true`, MC-4). The server's content text
108
+ * rides in the message so the model keeps its self-correction signal -
109
+ * while the executor records a real tool FAILURE (audit, retry and
110
+ * error policies all engage) instead of a fake success.
111
+ */
112
+ export class MCPToolExecutionError extends GraphorinMCPError {
113
+ public readonly kind = 'tool-execution' as const;
114
+ }
115
+
116
+ /** Raised when a tool call exceeds its configured timeout / aborts. */
117
+ export class MCPCallTimeoutError extends GraphorinMCPError {
118
+ public readonly kind: 'call-timeout' | 'session-lost' = 'call-timeout';
119
+
120
+ public constructor(
121
+ message: string,
122
+ opts: {
123
+ readonly hint?: string;
124
+ readonly metadata?: MCPErrorMetadata;
125
+ readonly cause?: unknown;
126
+ readonly variant?: 'call-timeout' | 'session-lost';
127
+ },
128
+ ) {
129
+ super(message, opts);
130
+ if (opts.variant !== undefined) {
131
+ this.kind = opts.variant;
132
+ }
133
+ }
134
+ }
135
+
136
+ /** Raised when an in-flight call is cancelled by an `AbortSignal`. */
137
+ export class MCPCancelledError extends GraphorinMCPError {
138
+ public readonly kind = 'cancelled' as const;
139
+ }
140
+
141
+ /** Raised on invalid `createMCPClient(...)` configuration. */
142
+ export class MCPInvalidConfigError extends GraphorinMCPError {
143
+ public readonly kind = 'invalid-config' as const;
144
+ }
145
+
146
+ /**
147
+ * Raised when an operator requests a transport / capability that the
148
+ * runtime does not support (e.g. `resumable: true` on `stdio`).
149
+ *
150
+ * @stable
151
+ */
152
+ export class MCPTransportNotSupportedError extends GraphorinMCPError {
153
+ public readonly kind: 'transport-not-supported' | 'transport-resumable-not-supported' =
154
+ 'transport-not-supported';
155
+
156
+ public constructor(
157
+ message: string,
158
+ opts: {
159
+ readonly hint?: string;
160
+ readonly metadata?: MCPErrorMetadata;
161
+ readonly cause?: unknown;
162
+ readonly variant?: 'transport-not-supported' | 'transport-resumable-not-supported';
163
+ },
164
+ ) {
165
+ super(message, opts);
166
+ if (opts.variant !== undefined) {
167
+ this.kind = opts.variant;
168
+ }
169
+ }
170
+ }