@graphorin/mcp 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +23 -16
  3. package/dist/client/adapt-result.js +22 -10
  4. package/dist/client/adapt-result.js.map +1 -1
  5. package/dist/client/client-handlers.js +11 -2
  6. package/dist/client/client-handlers.js.map +1 -1
  7. package/dist/client/client.d.ts.map +1 -1
  8. package/dist/client/client.js +111 -27
  9. package/dist/client/client.js.map +1 -1
  10. package/dist/client/inbound-filters.js +8 -3
  11. package/dist/client/inbound-filters.js.map +1 -1
  12. package/dist/client/mcp-resource-reader.d.ts +10 -0
  13. package/dist/client/mcp-resource-reader.d.ts.map +1 -1
  14. package/dist/client/mcp-resource-reader.js +16 -5
  15. package/dist/client/mcp-resource-reader.js.map +1 -1
  16. package/dist/client/pinning.js +2 -2
  17. package/dist/client/pinning.js.map +1 -1
  18. package/dist/client/to-tools.js.map +1 -1
  19. package/dist/client/transport-factory.js +1 -1
  20. package/dist/client/transport-factory.js.map +1 -1
  21. package/dist/client/types.d.ts +58 -10
  22. package/dist/client/types.d.ts.map +1 -1
  23. package/dist/errors/index.d.ts +2 -2
  24. package/dist/errors/index.js +2 -2
  25. package/dist/errors/index.js.map +1 -1
  26. package/dist/helpers/validate-config.js +1 -1
  27. package/dist/helpers/validate-config.js.map +1 -1
  28. package/dist/index.d.ts +2 -3
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +4 -3
  31. package/dist/index.js.map +1 -1
  32. package/dist/oauth/bridge.d.ts +1 -1
  33. package/dist/oauth/bridge.js.map +1 -1
  34. package/dist/package.js +6 -0
  35. package/dist/package.js.map +1 -0
  36. package/dist/registry/json-schema.js +36 -7
  37. package/dist/registry/json-schema.js.map +1 -1
  38. package/dist/transport/types.d.ts +4 -4
  39. package/package.json +6 -6
@@ -45,7 +45,7 @@ interface CreateMCPClientOptions {
45
45
  readonly suppressDeprecatedTransportWarning?: boolean;
46
46
  /**
47
47
  * Handler for server-initiated **elicitation** (`elicitation/create`)
48
- * requests the server asks the human for structured input mid-call
48
+ * requests - the server asks the human for structured input mid-call
49
49
  * (WI-13 / P2-2). When provided, the client advertises the
50
50
  * `elicitation` capability and routes requests here; back it with a
51
51
  * HITL surface (e.g. a CLI prompt or the agent's approval channel).
@@ -53,14 +53,14 @@ interface CreateMCPClientOptions {
53
53
  * server will not elicit (gated; no implicit prompting).
54
54
  *
55
55
  * Note: an elicitation arrives while a `callTool(...)` JSON-RPC request
56
- * is in flight, so the handler resolves in-process it does not
56
+ * is in flight, so the handler resolves in-process - it does not
57
57
  * durably suspend a Graphorin run. Durable-suspend elicitation across
58
58
  * the request lifetime is a follow-up.
59
59
  */
60
60
  readonly elicitation?: MCPElicitationHandler;
61
61
  /**
62
62
  * Handler for server-initiated **sampling** (`sampling/createMessage`)
63
- * requests the server asks the client's model to generate a
63
+ * requests - the server asks the client's model to generate a
64
64
  * completion (WI-13 / P2-2). When provided, the client advertises the
65
65
  * `sampling` capability and routes requests here; back it with a
66
66
  * `Provider`. The request messages are **MCP-derived (untrusted)**, so
@@ -69,6 +69,21 @@ interface CreateMCPClientOptions {
69
69
  * (gated).
70
70
  */
71
71
  readonly sampling?: MCPSamplingHandler;
72
+ /**
73
+ * mcp-skills-10: called when the underlying transport closes (a
74
+ * stdio child dying, an HTTP session dropping beyond the SDK's SSE
75
+ * resume). Without it a disconnect is observable only as
76
+ * `MCPProtocolError`s on subsequent calls. The client does NOT
77
+ * auto-reconnect - rebuild it via `createMCPClient(...)` (and re-run
78
+ * `toTools()` for the drift diff) when this fires.
79
+ */
80
+ readonly onTransportClose?: (info: {
81
+ readonly server: string;
82
+ }) => void;
83
+ /** mcp-skills-10: called on transport-level errors (see {@link onTransportClose}). */
84
+ readonly onTransportError?: (error: Error, info: {
85
+ readonly server: string;
86
+ }) => void;
72
87
  }
73
88
  /**
74
89
  * Server-initiated elicitation request surfaced to the operator's HITL
@@ -117,7 +132,7 @@ type MCPSamplingContent = {
117
132
  interface MCPSamplingMessage {
118
133
  readonly role: 'user' | 'assistant';
119
134
  /**
120
- * Every content block of the SDK message (MC-13) previously only
135
+ * Every content block of the SDK message (MC-13) - previously only
121
136
  * the FIRST block survived, silently dropping e.g. the image in a
122
137
  * text+image message before it reached the operator's handler.
123
138
  */
@@ -181,17 +196,28 @@ interface MCPToToolsOptions {
181
196
  */
182
197
  readonly callTimeoutMs?: number;
183
198
  /**
184
- * Operator-pinned definition fingerprints by MCP tool name (MC-6)
199
+ * Operator-pinned definition fingerprints by MCP tool name (MC-6) -
185
200
  * the `__definitionHash` stamped on a previously approved snapshot.
186
201
  * A mismatch means the server changed the definition behind the name.
187
202
  */
188
203
  readonly pinnedFingerprints?: Readonly<Record<string, string>>;
189
204
  /**
190
205
  * What to do on a pinned-fingerprint mismatch (MC-6). `'warn'`
191
- * (default) audits `mcp.tools.pin-mismatch.total` and continues;
192
- * `'reject'` throws `MCPToolPinningError`.
206
+ * (default without a {@link pinStore}) audits
207
+ * `mcp.tools.pin-mismatch.total` and continues; `'reject'` (the
208
+ * default WHEN a `pinStore` is wired - a persisted first approval is
209
+ * an explicit trust decision) throws `MCPToolPinningError`.
193
210
  */
194
211
  readonly onPinMismatch?: 'warn' | 'reject';
212
+ /**
213
+ * C6: durable trust-on-first-use pin storage. On the first `toTools()`
214
+ * the current definition fingerprints are RECORDED
215
+ * (`mcp.tools.pins-recorded.total`); on every later call they are
216
+ * COMPARED - drift is handled per {@link onPinMismatch}, which
217
+ * defaults to `'reject'` when a store is present (the rug-pull
218
+ * defense). Explicit `pinnedFingerprints` win over the store.
219
+ */
220
+ readonly pinStore?: MCPPinStore;
195
221
  /**
196
222
  * Per-server `defer_loading` override. When unset and
197
223
  * `listTools()` returns more than `deferLoadingThreshold` entries
@@ -295,7 +321,7 @@ type MCPContentPart = {
295
321
  * @stable
296
322
  */
297
323
  interface MCPClient {
298
- /** Stable identifier derived from the transport. */
324
+ /** Stable identifier - derived from the transport. */
299
325
  readonly id: string;
300
326
  /** Server-advertised information from the `initialize` handshake. */
301
327
  readonly serverInfo: {
@@ -310,14 +336,14 @@ interface MCPClient {
310
336
  readonly priority?: number;
311
337
  /**
312
338
  * Whether the Streamable HTTP server assigned an `Mcp-Session-Id`
313
- * at `initialize` time (MC-9). A session id means stateful routing
339
+ * at `initialize` time (MC-9). A session id means stateful routing -
314
340
  * it is NOT a replay guarantee: per the Streamable HTTP spec,
315
341
  * event replay is the SERVER's responsibility, and the SDK
316
342
  * transport already auto-reconnects with `Last-Event-ID` when the
317
343
  * server supports it.
318
344
  */
319
345
  readonly sessionIdPresent: boolean;
320
- /** @deprecated Alias of {@link sessionIdPresent} same value, misleading name. */
346
+ /** @deprecated Alias of {@link sessionIdPresent} - same value, misleading name. */
321
347
  readonly resumable: boolean;
322
348
  listTools(opts?: {
323
349
  signal?: AbortSignal;
@@ -332,9 +358,19 @@ interface MCPClient {
332
358
  signal?: AbortSignal;
333
359
  timeoutMs?: number;
334
360
  }): Promise<MCPCallToolResult>;
361
+ /**
362
+ * First content item of the resource. mcp-skills-11: a multi-content
363
+ * response (one URI can yield several items) is truncated to the
364
+ * FIRST item - a WARN + counter fire when that happens; use
365
+ * {@link readResourceContents} for the full array.
366
+ */
335
367
  readResource(uri: string, opts?: {
336
368
  signal?: AbortSignal;
337
369
  }): Promise<MCPResourceContent>;
370
+ /** Every content item of the resource (mcp-skills-11). */
371
+ readResourceContents(uri: string, opts?: {
372
+ signal?: AbortSignal;
373
+ }): Promise<ReadonlyArray<MCPResourceContent>>;
338
374
  getPrompt(name: string, args?: unknown, opts?: {
339
375
  signal?: AbortSignal;
340
376
  }): Promise<{
@@ -348,6 +384,18 @@ interface MCPPromptMessage {
348
384
  readonly role: 'user' | 'assistant';
349
385
  readonly content: MCPContentPart;
350
386
  }
387
+ /**
388
+ * C6: durable storage for trust-on-first-use MCP tool pins. Keyed by the
389
+ * server identity id; values are `toolName -> sha256 fingerprint` maps
390
+ * (the same shape as `pinnedFingerprints`). Implementations may be sync
391
+ * or async - a JSON file, a SQLite table, a secret store.
392
+ *
393
+ * @stable
394
+ */
395
+ interface MCPPinStore {
396
+ get(serverId: string): Readonly<Record<string, string>> | undefined | Promise<Readonly<Record<string, string>> | undefined>;
397
+ set(serverId: string, fingerprints: Readonly<Record<string, string>>): void | Promise<void>;
398
+ }
351
399
  //#endregion
352
400
  export { CreateMCPClientOptions, MCPCallToolResult, MCPClient, MCPContentPart, MCPElicitationHandler, MCPElicitationRequest, MCPElicitationResult, MCPPromptDefinition, MCPPromptMessage, MCPResourceContent, MCPResourceDefinition, MCPSamplingContent, MCPSamplingHandler, MCPSamplingMessage, MCPSamplingRequest, MCPSamplingResult, MCPToToolsOptions, MCPToolDefinition };
353
401
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/client/types.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;AAgGA;AAaY,UApFK,sBAAA,CAoFe;EAImB,SAAA,SAAA,EAvF7B,kBAuF6B;EAA3C;;;AAOR;EACW,SAAA,YAAA,CAAA,EA1Fe,0BA0Ff;EACiB;EACvB,SAAA,WAAA,CAAA,EAAA,MAAA;EAA+B;;;AAGpC;AAMA;AAgBA;EACmC,SAAA,iBAAA,CAAA,EA7GJ,iBA6GI;EAAd;EAIM,SAAA,QAAA,CAAA,EAAA,MAAA;EAEN;EAAa,SAAA,cAAA,CAAA,EAAA,MAAA;EAajB;EAQL,SAAA,MAAA,CAAA,EAAA,CAAA,KAAkB,EAAA,OAAA,GAAA,MAAA,GAAA,MAAA,GAAA,OAAA,EAAA,OAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EA/HjB,MA+HiB,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,GAAA,IAAA;EACnB;EACiB,SAAA,UAAA,CAAA,EAAA,MAAA;EACvB;EAA4B,SAAA,aAAA,CAAA,EAAA,MAAA;EAAR;;AAOzB;;;;EAoBgC,SAAA,kCAAA,CAAA,EAAA,OAAA;EAmBA;;;;;;;;;AAahC;;;;;EAIkC,SAAA,WAAA,CAAA,EAtKT,qBAsKS;EAKjB;AAQjB;AAWA;AAQA;;;;;;AAOA;EAmCiB,SAAA,QAAS,CAAA,EArOJ,kBAqOI;;;;;;;;AAwB8C,UApPvD,qBAAA,CAoPuD;EAAd;EAAR,SAAA,OAAA,EAAA,MAAA;EAClB;EAAsC,SAAA,eAAA,EAjP1C,QAiP0C,CAjPjC,MAiPiC,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;;;;;;;;AAMR,KA9OlD,oBAAA,GA8OkD;EAIxC,SAAA,MAAA,EAAA,QAAA;EAC0B,SAAA,OAAA,CAAA,EAhPvB,QAgPuB,CA/OxC,MA+OwC,CAAA,MAAA,EAAA,MAAA,GAAA,MAAA,GAAA,OAAA,GA/OG,aA+OH,CAAA,MAAA,CAAA,CAAA,CAAA;CAAd,GAAA;EAA7B,SAAA,MAAA,EAAA,SAAA;CACY,GAAA;EAA0C,SAAA,MAAA,EAAA,QAAA;CAAd;;AAClC,KA1OC,qBAAA,GA0OD,CAAA,OAAA,EAzOA,qBAyOA,EAAA,IAAA,EAAA;EAAO,SAAA,MAAA,CAAA,EAxOU,WAwOV;AAIlB,CAAA,EAAA,GA3OK,oBA2O4B,GA3OL,OA6OR,CA7OgB,oBA6OF,CAAA;;KA1OtB,kBAAA;;;;;;;;;;;;;UAMK,kBAAA;;;;;;;oBAOG,cAAc;;;;;;;;UASjB,kBAAA;qBACI,cAAc;;;;2BAIR;;qBAEN;;;;;;;;;;;;;;UAaJ,iBAAA;;oBAEG;;;;;KAMR,kBAAA,aACD;oBACiB;MACvB,oBAAoB,QAAQ;;;;;;UAOhB,iBAAA;;2BAEU;;;;;;;iCAOM;;;;;;;;;;;gCAWD,SAAS;;;;;;;;;;;;;;;;;;;gCAmBT;;mCAEG,SAAS,eAAe;;kCAEzB,SAAS,eAAe,YAAY;;;;;;;;UASrD,iBAAA;;;wBAGO,SAAS;0BACP,SAAS;;;;UAKlB,qBAAA;;;;;;;UAQA,mBAAA;;;uBAGM;;;;;;;UAQN,kBAAA;;;;;;;UAQA,iBAAA;oBACG,cAAc;+BACH,SAAS;;;;KAK5B,cAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAmCK,SAAA;;;;;;;;;2BAMU;;8BAEG;;;;;;;;;;;;;;;aAeA;MAAgB,QAAQ,cAAc;;aAClC;MAAgB,QAAQ,cAAc;;aACxC;MAAgB,QAAQ,cAAc;;aAIhD;;MACjB,QAAQ;;aACiC;MAAgB,QAAQ;;aAIhD;MACjB;uBAA6B,cAAc;;iBAC/B,oBAAoB,QAAQ,cAAc;WAChD;;;UAIM,gBAAA;;oBAEG"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/client/types.ts"],"sourcesContent":[],"mappings":"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"}
@@ -67,7 +67,7 @@ declare class MCPToolNotFoundError extends GraphorinMCPError {
67
67
  /**
68
68
  * Raised when a pinned tool-definition fingerprint does not match the
69
69
  * server's current definition and `onPinMismatch: 'reject'` is set
70
- * (MC-6) the approve-then-swap rug-pull posture.
70
+ * (MC-6) - the approve-then-swap rug-pull posture.
71
71
  */
72
72
  declare class MCPToolPinningError extends GraphorinMCPError {
73
73
  readonly kind: "pin-mismatch";
@@ -75,7 +75,7 @@ declare class MCPToolPinningError extends GraphorinMCPError {
75
75
  /**
76
76
  * Raised when the MCP server reports a tool-level failure
77
77
  * (`CallToolResult.isError === true`, MC-4). The server's content text
78
- * rides in the message so the model keeps its self-correction signal
78
+ * rides in the message so the model keeps its self-correction signal -
79
79
  * while the executor records a real tool FAILURE (audit, retry and
80
80
  * error policies all engage) instead of a fake success.
81
81
  */
@@ -38,7 +38,7 @@ var MCPToolNotFoundError = class extends GraphorinMCPError {
38
38
  /**
39
39
  * Raised when a pinned tool-definition fingerprint does not match the
40
40
  * server's current definition and `onPinMismatch: 'reject'` is set
41
- * (MC-6) the approve-then-swap rug-pull posture.
41
+ * (MC-6) - the approve-then-swap rug-pull posture.
42
42
  */
43
43
  var MCPToolPinningError = class extends GraphorinMCPError {
44
44
  kind = "pin-mismatch";
@@ -46,7 +46,7 @@ var MCPToolPinningError = class extends GraphorinMCPError {
46
46
  /**
47
47
  * Raised when the MCP server reports a tool-level failure
48
48
  * (`CallToolResult.isError === true`, MC-4). The server's content text
49
- * rides in the message so the model keeps its self-correction signal
49
+ * rides in the message so the model keeps its self-correction signal -
50
50
  * while the executor records a real tool FAILURE (audit, retry and
51
51
  * error policies all engage) instead of a fake success.
52
52
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/errors/index.ts"],"sourcesContent":["/**\n * Typed error union for the `@graphorin/mcp` package.\n *\n * Every error carries a stable lowercase {@link MCPErrorKind}\n * discriminator, an actionable {@link MCPError.hint} field where\n * applicable, and an optional structured `metadata` bag the audit\n * emitter persists alongside the standard `runId` / `sessionId`\n * context.\n *\n * @packageDocumentation\n */\n\n/**\n * Discriminator union for every error class produced by\n * `@graphorin/mcp`. New kinds extend this union; never throw plain\n * `Error` from framework code.\n *\n * @stable\n */\nexport type MCPErrorKind =\n | 'connection-failed'\n | 'protocol-error'\n | 'auth-error'\n | 'tool-not-found'\n | 'tool-execution'\n | 'pin-mismatch'\n | 'call-timeout'\n | 'cancelled'\n | 'invalid-config'\n | 'session-lost'\n | 'transport-not-supported'\n | 'transport-resumable-not-supported';\n\n/** Common metadata bag attached to every {@link MCPError}. */\nexport interface MCPErrorMetadata {\n readonly server?: string;\n readonly tool?: string;\n readonly transport?: 'stdio' | 'streamable-http' | 'sse';\n readonly cause?: unknown;\n readonly httpStatus?: number;\n readonly sessionId?: string;\n readonly lastEventId?: string;\n}\n\n/**\n * Base class for every typed error produced by `@graphorin/mcp`.\n *\n * @stable\n */\nexport abstract class GraphorinMCPError extends Error {\n /** Lowercase discriminator. */\n public abstract readonly kind: MCPErrorKind;\n /** Optional remediation hint surfaced in CLI output. */\n public readonly hint?: string;\n /** Sanitized metadata; never carries secret material. */\n public readonly metadata: Readonly<MCPErrorMetadata>;\n /** Underlying cause (chained errors). */\n public override readonly cause?: unknown;\n\n public constructor(\n message: string,\n opts: {\n readonly hint?: string;\n readonly metadata?: MCPErrorMetadata;\n readonly cause?: unknown;\n } = {},\n ) {\n super(message);\n this.name = new.target.name;\n if (opts.hint !== undefined) this.hint = opts.hint;\n this.metadata = Object.freeze({ ...(opts.metadata ?? {}) });\n if (opts.cause !== undefined) this.cause = opts.cause;\n }\n}\n\n/** Raised when a transport fails to connect or is dropped unexpectedly. */\nexport class MCPConnectionError extends GraphorinMCPError {\n public readonly kind = 'connection-failed' as const;\n}\n\n/** Raised on JSON-RPC / MCP protocol-level errors. */\nexport class MCPProtocolError extends GraphorinMCPError {\n public readonly kind = 'protocol-error' as const;\n}\n\n/** Raised when an authentication / authorization step fails. */\nexport class MCPAuthError extends GraphorinMCPError {\n public readonly kind = 'auth-error' as const;\n}\n\n/** Raised when {@link MCPClient.callTool} is invoked for an unknown tool. */\nexport class MCPToolNotFoundError extends GraphorinMCPError {\n public readonly kind = 'tool-not-found' as const;\n}\n\n/**\n * Raised when a pinned tool-definition fingerprint does not match the\n * server's current definition and `onPinMismatch: 'reject'` is set\n * (MC-6) the approve-then-swap rug-pull posture.\n */\nexport class MCPToolPinningError extends GraphorinMCPError {\n public readonly kind = 'pin-mismatch' as const;\n}\n\n/**\n * Raised when the MCP server reports a tool-level failure\n * (`CallToolResult.isError === true`, MC-4). The server's content text\n * rides in the message so the model keeps its self-correction signal —\n * while the executor records a real tool FAILURE (audit, retry and\n * error policies all engage) instead of a fake success.\n */\nexport class MCPToolExecutionError extends GraphorinMCPError {\n public readonly kind = 'tool-execution' as const;\n}\n\n/** Raised when a tool call exceeds its configured timeout / aborts. */\nexport class MCPCallTimeoutError extends GraphorinMCPError {\n public readonly kind: 'call-timeout' | 'session-lost' = 'call-timeout';\n\n public constructor(\n message: string,\n opts: {\n readonly hint?: string;\n readonly metadata?: MCPErrorMetadata;\n readonly cause?: unknown;\n readonly variant?: 'call-timeout' | 'session-lost';\n },\n ) {\n super(message, opts);\n if (opts.variant !== undefined) {\n this.kind = opts.variant;\n }\n }\n}\n\n/** Raised when an in-flight call is cancelled by an `AbortSignal`. */\nexport class MCPCancelledError extends GraphorinMCPError {\n public readonly kind = 'cancelled' as const;\n}\n\n/** Raised on invalid `createMCPClient(...)` configuration. */\nexport class MCPInvalidConfigError extends GraphorinMCPError {\n public readonly kind = 'invalid-config' as const;\n}\n\n/**\n * Raised when an operator requests a transport / capability that the\n * runtime does not support (e.g. `resumable: true` on `stdio`).\n *\n * @stable\n */\nexport class MCPTransportNotSupportedError extends GraphorinMCPError {\n public readonly kind: 'transport-not-supported' | 'transport-resumable-not-supported' =\n 'transport-not-supported';\n\n public constructor(\n message: string,\n opts: {\n readonly hint?: string;\n readonly metadata?: MCPErrorMetadata;\n readonly cause?: unknown;\n readonly variant?: 'transport-not-supported' | 'transport-resumable-not-supported';\n },\n ) {\n super(message, opts);\n if (opts.variant !== undefined) {\n this.kind = opts.variant;\n }\n }\n}\n"],"mappings":";;;;;;AAiDA,IAAsB,oBAAtB,cAAgD,MAAM;;CAIpD,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAyB;CAEzB,AAAO,YACL,SACA,OAII,EAAE,EACN;AACA,QAAM,QAAQ;AACd,OAAK,OAAO,IAAI,OAAO;AACvB,MAAI,KAAK,SAAS,OAAW,MAAK,OAAO,KAAK;AAC9C,OAAK,WAAW,OAAO,OAAO,EAAE,GAAI,KAAK,YAAY,EAAE,EAAG,CAAC;AAC3D,MAAI,KAAK,UAAU,OAAW,MAAK,QAAQ,KAAK;;;;AAKpD,IAAa,qBAAb,cAAwC,kBAAkB;CACxD,AAAgB,OAAO;;;AAIzB,IAAa,mBAAb,cAAsC,kBAAkB;CACtD,AAAgB,OAAO;;;AAIzB,IAAa,eAAb,cAAkC,kBAAkB;CAClD,AAAgB,OAAO;;;AAIzB,IAAa,uBAAb,cAA0C,kBAAkB;CAC1D,AAAgB,OAAO;;;;;;;AAQzB,IAAa,sBAAb,cAAyC,kBAAkB;CACzD,AAAgB,OAAO;;;;;;;;;AAUzB,IAAa,wBAAb,cAA2C,kBAAkB;CAC3D,AAAgB,OAAO;;;AAIzB,IAAa,sBAAb,cAAyC,kBAAkB;CACzD,AAAgB,OAAwC;CAExD,AAAO,YACL,SACA,MAMA;AACA,QAAM,SAAS,KAAK;AACpB,MAAI,KAAK,YAAY,OACnB,MAAK,OAAO,KAAK;;;;AAMvB,IAAa,oBAAb,cAAuC,kBAAkB;CACvD,AAAgB,OAAO;;;AAIzB,IAAa,wBAAb,cAA2C,kBAAkB;CAC3D,AAAgB,OAAO;;;;;;;;AASzB,IAAa,gCAAb,cAAmD,kBAAkB;CACnE,AAAgB,OACd;CAEF,AAAO,YACL,SACA,MAMA;AACA,QAAM,SAAS,KAAK;AACpB,MAAI,KAAK,YAAY,OACnB,MAAK,OAAO,KAAK"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/errors/index.ts"],"sourcesContent":["/**\n * Typed error union for the `@graphorin/mcp` package.\n *\n * Every error carries a stable lowercase {@link MCPErrorKind}\n * discriminator, an actionable {@link MCPError.hint} field where\n * applicable, and an optional structured `metadata` bag the audit\n * emitter persists alongside the standard `runId` / `sessionId`\n * context.\n *\n * @packageDocumentation\n */\n\n/**\n * Discriminator union for every error class produced by\n * `@graphorin/mcp`. New kinds extend this union; never throw plain\n * `Error` from framework code.\n *\n * @stable\n */\nexport type MCPErrorKind =\n | 'connection-failed'\n | 'protocol-error'\n | 'auth-error'\n | 'tool-not-found'\n | 'tool-execution'\n | 'pin-mismatch'\n | 'call-timeout'\n | 'cancelled'\n | 'invalid-config'\n | 'session-lost'\n | 'transport-not-supported'\n | 'transport-resumable-not-supported';\n\n/** Common metadata bag attached to every {@link MCPError}. */\nexport interface MCPErrorMetadata {\n readonly server?: string;\n readonly tool?: string;\n readonly transport?: 'stdio' | 'streamable-http' | 'sse';\n readonly cause?: unknown;\n readonly httpStatus?: number;\n readonly sessionId?: string;\n readonly lastEventId?: string;\n}\n\n/**\n * Base class for every typed error produced by `@graphorin/mcp`.\n *\n * @stable\n */\nexport abstract class GraphorinMCPError extends Error {\n /** Lowercase discriminator. */\n public abstract readonly kind: MCPErrorKind;\n /** Optional remediation hint surfaced in CLI output. */\n public readonly hint?: string;\n /** Sanitized metadata; never carries secret material. */\n public readonly metadata: Readonly<MCPErrorMetadata>;\n /** Underlying cause (chained errors). */\n public override readonly cause?: unknown;\n\n public constructor(\n message: string,\n opts: {\n readonly hint?: string;\n readonly metadata?: MCPErrorMetadata;\n readonly cause?: unknown;\n } = {},\n ) {\n super(message);\n this.name = new.target.name;\n if (opts.hint !== undefined) this.hint = opts.hint;\n this.metadata = Object.freeze({ ...(opts.metadata ?? {}) });\n if (opts.cause !== undefined) this.cause = opts.cause;\n }\n}\n\n/** Raised when a transport fails to connect or is dropped unexpectedly. */\nexport class MCPConnectionError extends GraphorinMCPError {\n public readonly kind = 'connection-failed' as const;\n}\n\n/** Raised on JSON-RPC / MCP protocol-level errors. */\nexport class MCPProtocolError extends GraphorinMCPError {\n public readonly kind = 'protocol-error' as const;\n}\n\n/** Raised when an authentication / authorization step fails. */\nexport class MCPAuthError extends GraphorinMCPError {\n public readonly kind = 'auth-error' as const;\n}\n\n/** Raised when {@link MCPClient.callTool} is invoked for an unknown tool. */\nexport class MCPToolNotFoundError extends GraphorinMCPError {\n public readonly kind = 'tool-not-found' as const;\n}\n\n/**\n * Raised when a pinned tool-definition fingerprint does not match the\n * server's current definition and `onPinMismatch: 'reject'` is set\n * (MC-6) - the approve-then-swap rug-pull posture.\n */\nexport class MCPToolPinningError extends GraphorinMCPError {\n public readonly kind = 'pin-mismatch' as const;\n}\n\n/**\n * Raised when the MCP server reports a tool-level failure\n * (`CallToolResult.isError === true`, MC-4). The server's content text\n * rides in the message so the model keeps its self-correction signal -\n * while the executor records a real tool FAILURE (audit, retry and\n * error policies all engage) instead of a fake success.\n */\nexport class MCPToolExecutionError extends GraphorinMCPError {\n public readonly kind = 'tool-execution' as const;\n}\n\n/** Raised when a tool call exceeds its configured timeout / aborts. */\nexport class MCPCallTimeoutError extends GraphorinMCPError {\n public readonly kind: 'call-timeout' | 'session-lost' = 'call-timeout';\n\n public constructor(\n message: string,\n opts: {\n readonly hint?: string;\n readonly metadata?: MCPErrorMetadata;\n readonly cause?: unknown;\n readonly variant?: 'call-timeout' | 'session-lost';\n },\n ) {\n super(message, opts);\n if (opts.variant !== undefined) {\n this.kind = opts.variant;\n }\n }\n}\n\n/** Raised when an in-flight call is cancelled by an `AbortSignal`. */\nexport class MCPCancelledError extends GraphorinMCPError {\n public readonly kind = 'cancelled' as const;\n}\n\n/** Raised on invalid `createMCPClient(...)` configuration. */\nexport class MCPInvalidConfigError extends GraphorinMCPError {\n public readonly kind = 'invalid-config' as const;\n}\n\n/**\n * Raised when an operator requests a transport / capability that the\n * runtime does not support (e.g. `resumable: true` on `stdio`).\n *\n * @stable\n */\nexport class MCPTransportNotSupportedError extends GraphorinMCPError {\n public readonly kind: 'transport-not-supported' | 'transport-resumable-not-supported' =\n 'transport-not-supported';\n\n public constructor(\n message: string,\n opts: {\n readonly hint?: string;\n readonly metadata?: MCPErrorMetadata;\n readonly cause?: unknown;\n readonly variant?: 'transport-not-supported' | 'transport-resumable-not-supported';\n },\n ) {\n super(message, opts);\n if (opts.variant !== undefined) {\n this.kind = opts.variant;\n }\n }\n}\n"],"mappings":";;;;;;AAiDA,IAAsB,oBAAtB,cAAgD,MAAM;;CAIpD,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAyB;CAEzB,AAAO,YACL,SACA,OAII,EAAE,EACN;AACA,QAAM,QAAQ;AACd,OAAK,OAAO,IAAI,OAAO;AACvB,MAAI,KAAK,SAAS,OAAW,MAAK,OAAO,KAAK;AAC9C,OAAK,WAAW,OAAO,OAAO,EAAE,GAAI,KAAK,YAAY,EAAE,EAAG,CAAC;AAC3D,MAAI,KAAK,UAAU,OAAW,MAAK,QAAQ,KAAK;;;;AAKpD,IAAa,qBAAb,cAAwC,kBAAkB;CACxD,AAAgB,OAAO;;;AAIzB,IAAa,mBAAb,cAAsC,kBAAkB;CACtD,AAAgB,OAAO;;;AAIzB,IAAa,eAAb,cAAkC,kBAAkB;CAClD,AAAgB,OAAO;;;AAIzB,IAAa,uBAAb,cAA0C,kBAAkB;CAC1D,AAAgB,OAAO;;;;;;;AAQzB,IAAa,sBAAb,cAAyC,kBAAkB;CACzD,AAAgB,OAAO;;;;;;;;;AAUzB,IAAa,wBAAb,cAA2C,kBAAkB;CAC3D,AAAgB,OAAO;;;AAIzB,IAAa,sBAAb,cAAyC,kBAAkB;CACzD,AAAgB,OAAwC;CAExD,AAAO,YACL,SACA,MAMA;AACA,QAAM,SAAS,KAAK;AACpB,MAAI,KAAK,YAAY,OACnB,MAAK,OAAO,KAAK;;;;AAMvB,IAAa,oBAAb,cAAuC,kBAAkB;CACvD,AAAgB,OAAO;;;AAIzB,IAAa,wBAAb,cAA2C,kBAAkB;CAC3D,AAAgB,OAAO;;;;;;;;AASzB,IAAa,gCAAb,cAAmD,kBAAkB;CACnE,AAAgB,OACd;CAEF,AAAO,YACL,SACA,MAMA;AACA,QAAM,SAAS,KAAK;AACpB,MAAI,KAAK,YAAY,OACnB,MAAK,OAAO,KAAK"}
@@ -2,7 +2,7 @@ import { MCPInvalidConfigError, MCPTransportNotSupportedError } from "../errors/
2
2
 
3
3
  //#region src/helpers/validate-config.ts
4
4
  /**
5
- * `validateMCPServerConfig(...)` sanity-check a transport configuration
5
+ * `validateMCPServerConfig(...)` - sanity-check a transport configuration
6
6
  * before {@link createMCPClient} runs the network-level handshake.
7
7
  *
8
8
  * The validator catches the most common configuration mistakes
@@ -1 +1 @@
1
- {"version":3,"file":"validate-config.js","names":[],"sources":["../../src/helpers/validate-config.ts"],"sourcesContent":["/**\n * `validateMCPServerConfig(...)` sanity-check a transport configuration\n * before {@link createMCPClient} runs the network-level handshake.\n *\n * The validator catches the most common configuration mistakes\n * (missing url for HTTP transports, empty command for stdio,\n * unsupported `resumable: true` for stdio / sse, …) and surfaces them\n * as {@link MCPInvalidConfigError} so the operator does not have to\n * read a transport-specific stack trace.\n *\n * @packageDocumentation\n */\n\nimport { MCPInvalidConfigError, MCPTransportNotSupportedError } from '../errors/index.js';\nimport type { MCPTransportConfig } from '../transport/types.js';\n\n/**\n * Throws {@link MCPInvalidConfigError} on invalid configuration.\n *\n * @stable\n */\nexport function validateMCPServerConfig(opts: {\n readonly transport: MCPTransportConfig;\n readonly resumable?: boolean;\n}): void {\n validateTransport(opts.transport);\n if (opts.resumable === true) {\n if (opts.transport.kind === 'stdio') {\n throw new MCPTransportNotSupportedError(\n 'Resumable streaming sessions are not supported on the stdio transport.',\n {\n variant: 'transport-resumable-not-supported',\n metadata: { transport: 'stdio' },\n hint: 'Use the streamable-http transport when you need Mcp-Session-Id + Last-Event-ID resume semantics.',\n },\n );\n }\n if (opts.transport.kind === 'sse') {\n throw new MCPTransportNotSupportedError(\n 'Resumable streaming sessions are not supported on the deprecated sse transport.',\n {\n variant: 'transport-resumable-not-supported',\n metadata: { transport: 'sse' },\n hint: 'Migrate to the streamable-http transport for resumable session support.',\n },\n );\n }\n }\n}\n\nfunction validateTransport(transport: MCPTransportConfig): void {\n switch (transport.kind) {\n case 'stdio': {\n if (typeof transport.command !== 'string' || transport.command.length === 0) {\n throw new MCPInvalidConfigError('stdio transport requires a non-empty `command`.', {\n metadata: { transport: 'stdio' },\n });\n }\n if (transport.args !== undefined && !Array.isArray(transport.args)) {\n throw new MCPInvalidConfigError('stdio transport `args` must be an array of strings.', {\n metadata: { transport: 'stdio' },\n });\n }\n return;\n }\n case 'streamable-http':\n case 'sse': {\n if (transport.url === undefined || transport.url === null || transport.url === '') {\n throw new MCPInvalidConfigError(\n `${transport.kind} transport requires a non-empty \\`url\\`.`,\n {\n metadata: { transport: transport.kind },\n },\n );\n }\n try {\n const candidate =\n typeof transport.url === 'string' ? new URL(transport.url) : transport.url;\n if (candidate.protocol !== 'http:' && candidate.protocol !== 'https:') {\n throw new MCPInvalidConfigError(\n `${transport.kind} transport \\`url\\` must use http: or https: (got ${candidate.protocol}).`,\n { metadata: { transport: transport.kind } },\n );\n }\n } catch (cause) {\n if (cause instanceof MCPInvalidConfigError) throw cause;\n throw new MCPInvalidConfigError(`${transport.kind} transport \\`url\\` is not a valid URL.`, {\n metadata: { transport: transport.kind },\n cause,\n });\n }\n return;\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,wBAAwB,MAG/B;AACP,mBAAkB,KAAK,UAAU;AACjC,KAAI,KAAK,cAAc,MAAM;AAC3B,MAAI,KAAK,UAAU,SAAS,QAC1B,OAAM,IAAI,8BACR,0EACA;GACE,SAAS;GACT,UAAU,EAAE,WAAW,SAAS;GAChC,MAAM;GACP,CACF;AAEH,MAAI,KAAK,UAAU,SAAS,MAC1B,OAAM,IAAI,8BACR,mFACA;GACE,SAAS;GACT,UAAU,EAAE,WAAW,OAAO;GAC9B,MAAM;GACP,CACF;;;AAKP,SAAS,kBAAkB,WAAqC;AAC9D,SAAQ,UAAU,MAAlB;EACE,KAAK;AACH,OAAI,OAAO,UAAU,YAAY,YAAY,UAAU,QAAQ,WAAW,EACxE,OAAM,IAAI,sBAAsB,mDAAmD,EACjF,UAAU,EAAE,WAAW,SAAS,EACjC,CAAC;AAEJ,OAAI,UAAU,SAAS,UAAa,CAAC,MAAM,QAAQ,UAAU,KAAK,CAChE,OAAM,IAAI,sBAAsB,uDAAuD,EACrF,UAAU,EAAE,WAAW,SAAS,EACjC,CAAC;AAEJ;EAEF,KAAK;EACL,KAAK;AACH,OAAI,UAAU,QAAQ,UAAa,UAAU,QAAQ,QAAQ,UAAU,QAAQ,GAC7E,OAAM,IAAI,sBACR,GAAG,UAAU,KAAK,2CAClB,EACE,UAAU,EAAE,WAAW,UAAU,MAAM,EACxC,CACF;AAEH,OAAI;IACF,MAAM,YACJ,OAAO,UAAU,QAAQ,WAAW,IAAI,IAAI,UAAU,IAAI,GAAG,UAAU;AACzE,QAAI,UAAU,aAAa,WAAW,UAAU,aAAa,SAC3D,OAAM,IAAI,sBACR,GAAG,UAAU,KAAK,mDAAmD,UAAU,SAAS,KACxF,EAAE,UAAU,EAAE,WAAW,UAAU,MAAM,EAAE,CAC5C;YAEI,OAAO;AACd,QAAI,iBAAiB,sBAAuB,OAAM;AAClD,UAAM,IAAI,sBAAsB,GAAG,UAAU,KAAK,yCAAyC;KACzF,UAAU,EAAE,WAAW,UAAU,MAAM;KACvC;KACD,CAAC;;AAEJ"}
1
+ {"version":3,"file":"validate-config.js","names":[],"sources":["../../src/helpers/validate-config.ts"],"sourcesContent":["/**\n * `validateMCPServerConfig(...)` - sanity-check a transport configuration\n * before {@link createMCPClient} runs the network-level handshake.\n *\n * The validator catches the most common configuration mistakes\n * (missing url for HTTP transports, empty command for stdio,\n * unsupported `resumable: true` for stdio / sse, …) and surfaces them\n * as {@link MCPInvalidConfigError} so the operator does not have to\n * read a transport-specific stack trace.\n *\n * @packageDocumentation\n */\n\nimport { MCPInvalidConfigError, MCPTransportNotSupportedError } from '../errors/index.js';\nimport type { MCPTransportConfig } from '../transport/types.js';\n\n/**\n * Throws {@link MCPInvalidConfigError} on invalid configuration.\n *\n * @stable\n */\nexport function validateMCPServerConfig(opts: {\n readonly transport: MCPTransportConfig;\n readonly resumable?: boolean;\n}): void {\n validateTransport(opts.transport);\n if (opts.resumable === true) {\n if (opts.transport.kind === 'stdio') {\n throw new MCPTransportNotSupportedError(\n 'Resumable streaming sessions are not supported on the stdio transport.',\n {\n variant: 'transport-resumable-not-supported',\n metadata: { transport: 'stdio' },\n hint: 'Use the streamable-http transport when you need Mcp-Session-Id + Last-Event-ID resume semantics.',\n },\n );\n }\n if (opts.transport.kind === 'sse') {\n throw new MCPTransportNotSupportedError(\n 'Resumable streaming sessions are not supported on the deprecated sse transport.',\n {\n variant: 'transport-resumable-not-supported',\n metadata: { transport: 'sse' },\n hint: 'Migrate to the streamable-http transport for resumable session support.',\n },\n );\n }\n }\n}\n\nfunction validateTransport(transport: MCPTransportConfig): void {\n switch (transport.kind) {\n case 'stdio': {\n if (typeof transport.command !== 'string' || transport.command.length === 0) {\n throw new MCPInvalidConfigError('stdio transport requires a non-empty `command`.', {\n metadata: { transport: 'stdio' },\n });\n }\n if (transport.args !== undefined && !Array.isArray(transport.args)) {\n throw new MCPInvalidConfigError('stdio transport `args` must be an array of strings.', {\n metadata: { transport: 'stdio' },\n });\n }\n return;\n }\n case 'streamable-http':\n case 'sse': {\n if (transport.url === undefined || transport.url === null || transport.url === '') {\n throw new MCPInvalidConfigError(\n `${transport.kind} transport requires a non-empty \\`url\\`.`,\n {\n metadata: { transport: transport.kind },\n },\n );\n }\n try {\n const candidate =\n typeof transport.url === 'string' ? new URL(transport.url) : transport.url;\n if (candidate.protocol !== 'http:' && candidate.protocol !== 'https:') {\n throw new MCPInvalidConfigError(\n `${transport.kind} transport \\`url\\` must use http: or https: (got ${candidate.protocol}).`,\n { metadata: { transport: transport.kind } },\n );\n }\n } catch (cause) {\n if (cause instanceof MCPInvalidConfigError) throw cause;\n throw new MCPInvalidConfigError(`${transport.kind} transport \\`url\\` is not a valid URL.`, {\n metadata: { transport: transport.kind },\n cause,\n });\n }\n return;\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,wBAAwB,MAG/B;AACP,mBAAkB,KAAK,UAAU;AACjC,KAAI,KAAK,cAAc,MAAM;AAC3B,MAAI,KAAK,UAAU,SAAS,QAC1B,OAAM,IAAI,8BACR,0EACA;GACE,SAAS;GACT,UAAU,EAAE,WAAW,SAAS;GAChC,MAAM;GACP,CACF;AAEH,MAAI,KAAK,UAAU,SAAS,MAC1B,OAAM,IAAI,8BACR,mFACA;GACE,SAAS;GACT,UAAU,EAAE,WAAW,OAAO;GAC9B,MAAM;GACP,CACF;;;AAKP,SAAS,kBAAkB,WAAqC;AAC9D,SAAQ,UAAU,MAAlB;EACE,KAAK;AACH,OAAI,OAAO,UAAU,YAAY,YAAY,UAAU,QAAQ,WAAW,EACxE,OAAM,IAAI,sBAAsB,mDAAmD,EACjF,UAAU,EAAE,WAAW,SAAS,EACjC,CAAC;AAEJ,OAAI,UAAU,SAAS,UAAa,CAAC,MAAM,QAAQ,UAAU,KAAK,CAChE,OAAM,IAAI,sBAAsB,uDAAuD,EACrF,UAAU,EAAE,WAAW,SAAS,EACjC,CAAC;AAEJ;EAEF,KAAK;EACL,KAAK;AACH,OAAI,UAAU,QAAQ,UAAa,UAAU,QAAQ,QAAQ,UAAU,QAAQ,GAC7E,OAAM,IAAI,sBACR,GAAG,UAAU,KAAK,2CAClB,EACE,UAAU,EAAE,WAAW,UAAU,MAAM,EACxC,CACF;AAEH,OAAI;IACF,MAAM,YACJ,OAAO,UAAU,QAAQ,WAAW,IAAI,IAAI,UAAU,IAAI,GAAG,UAAU;AACzE,QAAI,UAAU,aAAa,WAAW,UAAU,aAAa,SAC3D,OAAM,IAAI,sBACR,GAAG,UAAU,KAAK,mDAAmD,UAAU,SAAS,KACxF,EAAE,UAAU,EAAE,WAAW,UAAU,MAAM,EAAE,CAC5C;YAEI,OAAO;AACd,QAAI,iBAAiB,sBAAuB,OAAM;AAClD,UAAM,IAAI,sBAAsB,GAAG,UAAU,KAAK,yCAAyC;KACzF,UAAU,EAAE,WAAW,UAAU,MAAM;KACvC;KACD,CAAC;;AAEJ"}
package/dist/index.d.ts CHANGED
@@ -16,7 +16,7 @@ import "./oauth/index.js";
16
16
  //#region src/index.d.ts
17
17
 
18
18
  /**
19
- * `@graphorin/mcp` Model Context Protocol client for the Graphorin
19
+ * `@graphorin/mcp` - Model Context Protocol client for the Graphorin
20
20
  * framework.
21
21
  *
22
22
  * The package owns:
@@ -53,8 +53,7 @@ import "./oauth/index.js";
53
53
  *
54
54
  * @packageDocumentation
55
55
  */
56
- /** Canonical version constant. Mirrors the `package.json` version. */
57
- declare const VERSION = "0.5.0";
56
+ declare const VERSION: string;
58
57
  //#endregion
59
58
  export { CreateMCPClientOptions, DEFAULT_DEFER_LOADING_THRESHOLD, GraphorinMCPError, MCPAuthError, MCPCallTimeoutError, MCPCallToolResult, MCPCancelledError, MCPClient, MCPConnectionError, MCPContentPart, MCPElicitationHandler, MCPElicitationRequest, MCPElicitationResult, MCPErrorKind, MCPErrorMetadata, MCPInvalidConfigError, MCPPromptDefinition, MCPPromptMessage, MCPProtocolError, MCPResourceContent, MCPResourceDefinition, MCPSamplingContent, MCPSamplingHandler, MCPSamplingMessage, MCPSamplingRequest, MCPSamplingResult, MCPToToolsOptions, MCPToolDefinition, MCPToolExecutionError, MCPToolNotFoundError, MCPToolPinningError, MCPTransportConfig, MCPTransportNotSupportedError, McpResourceReaderOptions, OAuthAuthorizationProvider, OAuthAuthorizationProviderOptions, ServerIdentity, SseTransportConfig, StdioTransportConfig, StreamableHttpTransportConfig, VERSION, _resetMcpAdapterDedupForTesting, _resetSseWarnDedupForTesting, adaptCallResult, adaptMCPTools, createMCPClient, createMcpResourceReader, createOAuthAuthorizationProvider, deriveServerIdentity, formatMCPServerName, mcpAuthListSessions, mcpAuthLogin, mcpAuthRefresh, mcpAuthRevoke, mcpAuthStatus, validateMCPServerConfig };
60
59
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA;;;;;;;;;;;;;;;;;;;;;;;cAAa,OAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA;;;;;;;;;;;;;;;;;;;;;;cAAa"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { version } from "./package.js";
1
2
  import { GraphorinMCPError, MCPAuthError, MCPCallTimeoutError, MCPCancelledError, MCPConnectionError, MCPInvalidConfigError, MCPProtocolError, MCPToolExecutionError, MCPToolNotFoundError, MCPToolPinningError, MCPTransportNotSupportedError } from "./errors/index.js";
2
3
  import { deriveServerIdentity, formatMCPServerName } from "./helpers/identity.js";
3
4
  import { validateMCPServerConfig } from "./helpers/validate-config.js";
@@ -13,7 +14,7 @@ import "./oauth/index.js";
13
14
 
14
15
  //#region src/index.ts
15
16
  /**
16
- * `@graphorin/mcp` Model Context Protocol client for the Graphorin
17
+ * `@graphorin/mcp` - Model Context Protocol client for the Graphorin
17
18
  * framework.
18
19
  *
19
20
  * The package owns:
@@ -50,8 +51,8 @@ import "./oauth/index.js";
50
51
  *
51
52
  * @packageDocumentation
52
53
  */
53
- /** Canonical version constant. Mirrors the `package.json` version. */
54
- const VERSION = "0.5.0";
54
+ /** Canonical version constant, derived from `package.json` at build time. */
55
+ const VERSION = version;
55
56
 
56
57
  //#endregion
57
58
  export { DEFAULT_DEFER_LOADING_THRESHOLD, GraphorinMCPError, MCPAuthError, MCPCallTimeoutError, MCPCancelledError, MCPConnectionError, MCPInvalidConfigError, MCPProtocolError, MCPToolExecutionError, MCPToolNotFoundError, MCPToolPinningError, MCPTransportNotSupportedError, VERSION, _resetMcpAdapterDedupForTesting, _resetSseWarnDedupForTesting, adaptCallResult, adaptMCPTools, createMCPClient, createMcpResourceReader, createOAuthAuthorizationProvider, deriveServerIdentity, formatMCPServerName, mcpAuthListSessions, mcpAuthLogin, mcpAuthRefresh, mcpAuthRevoke, mcpAuthStatus, validateMCPServerConfig };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@graphorin/mcp` Model Context Protocol client for the Graphorin\n * framework.\n *\n * The package owns:\n *\n * - The {@link createMCPClient} factory that opens a typed MCP\n * connection over stdio, Streamable HTTP, or the deprecated SSE\n * transport.\n * - The {@link MCPClient} surface (`listTools` / `listResources` /\n * `listPrompts` / `callTool` / `readResource` / `getPrompt` /\n * `close`) plus the strategy-aware {@link MCPClient.toTools}\n * adapter that bridges MCP tool descriptors into Graphorin\n * {@link Tool} records.\n * - The OAuth bridge that resolves bearer headers from the\n * {@link OAuthAuthorizationProvider} backed by\n * `@graphorin/security/oauth`.\n * - Library helpers (`mcpAuthLogin`, `mcpAuthListSessions`,\n * `mcpAuthRefresh`, `mcpAuthRevoke`, `mcpAuthStatus`) consumed by\n * the upcoming `graphorin auth` CLI surface.\n * - Typed errors ({@link MCPConnectionError},\n * {@link MCPProtocolError}, {@link MCPAuthError},\n * {@link MCPToolNotFoundError}, {@link MCPCallTimeoutError},\n * {@link MCPCancelledError}, {@link MCPInvalidConfigError},\n * {@link MCPTransportNotSupportedError}, {@link GraphorinMCPError}).\n *\n * Stable sub-paths:\n *\n * ```ts\n * import { createMCPClient } from '@graphorin/mcp/client';\n * import { createOAuthAuthorizationProvider } from '@graphorin/mcp/oauth';\n * import { formatMCPServerName, validateMCPServerConfig } from '@graphorin/mcp/helpers';\n * import { MCPConnectionError } from '@graphorin/mcp/errors';\n * import type { MCPTransportConfig, ServerIdentity } from '@graphorin/mcp/transport';\n * ```\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant. Mirrors the `package.json` version. */\nexport const VERSION = '0.5.0';\n\nexport * from './client/index.js';\nexport * from './errors/index.js';\nexport * from './helpers/index.js';\nexport * from './oauth/index.js';\nexport * from './transport/index.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,MAAa,UAAU"}
1
+ {"version":3,"file":"index.js","names":["VERSION: string","pkg.version"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@graphorin/mcp` - Model Context Protocol client for the Graphorin\n * framework.\n *\n * The package owns:\n *\n * - The {@link createMCPClient} factory that opens a typed MCP\n * connection over stdio, Streamable HTTP, or the deprecated SSE\n * transport.\n * - The {@link MCPClient} surface (`listTools` / `listResources` /\n * `listPrompts` / `callTool` / `readResource` / `getPrompt` /\n * `close`) plus the strategy-aware {@link MCPClient.toTools}\n * adapter that bridges MCP tool descriptors into Graphorin\n * {@link Tool} records.\n * - The OAuth bridge that resolves bearer headers from the\n * {@link OAuthAuthorizationProvider} backed by\n * `@graphorin/security/oauth`.\n * - Library helpers (`mcpAuthLogin`, `mcpAuthListSessions`,\n * `mcpAuthRefresh`, `mcpAuthRevoke`, `mcpAuthStatus`) consumed by\n * the upcoming `graphorin auth` CLI surface.\n * - Typed errors ({@link MCPConnectionError},\n * {@link MCPProtocolError}, {@link MCPAuthError},\n * {@link MCPToolNotFoundError}, {@link MCPCallTimeoutError},\n * {@link MCPCancelledError}, {@link MCPInvalidConfigError},\n * {@link MCPTransportNotSupportedError}, {@link GraphorinMCPError}).\n *\n * Stable sub-paths:\n *\n * ```ts\n * import { createMCPClient } from '@graphorin/mcp/client';\n * import { createOAuthAuthorizationProvider } from '@graphorin/mcp/oauth';\n * import { formatMCPServerName, validateMCPServerConfig } from '@graphorin/mcp/helpers';\n * import { MCPConnectionError } from '@graphorin/mcp/errors';\n * import type { MCPTransportConfig, ServerIdentity } from '@graphorin/mcp/transport';\n * ```\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant, derived from `package.json` at build time. */\nimport pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION: string = pkg.version;\n\nexport * from './client/index.js';\nexport * from './errors/index.js';\nexport * from './helpers/index.js';\nexport * from './oauth/index.js';\nexport * from './transport/index.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,MAAaA,UAAkBC"}
@@ -7,7 +7,7 @@ import { OAuthServerStore } from "@graphorin/core/contracts";
7
7
  /** Options accepted by {@link createOAuthAuthorizationProvider}. */
8
8
  interface OAuthAuthorizationProviderOptions {
9
9
  /**
10
- * Secrets store the persisted tokens resolve from (SPL-1) with it,
10
+ * Secrets store the persisted tokens resolve from (SPL-1) - with it,
11
11
  * the bridge issues Authorization headers across process restarts.
12
12
  */
13
13
  readonly secretsStore?: _graphorin_core_contracts0.SecretsStore;
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.js","names":["activeSession: OAuthSession | undefined"],"sources":["../../src/oauth/bridge.ts"],"sourcesContent":["/**\n * Bridge between the MCP transport's bearer-token requirement and the\n * outbound OAuth subsystem in `@graphorin/security/oauth`.\n *\n * The bridge resolves an authorization header from a stored\n * {@link OAuthSession}, refreshes the token automatically when it is\n * within the configured pre-expiry window, and surfaces every\n * lifecycle transition (`oauth.granted` / `oauth.refreshed` /\n * `oauth.revoked` / `mcp.auth.expired`) to subscribers.\n *\n * @packageDocumentation\n */\n\nimport type { OAuthServerStore } from '@graphorin/core/contracts';\nimport type { OAuthSession } from '@graphorin/security/oauth';\nimport {\n emitOAuthLifecycle,\n GraphorinOAuthError,\n refreshOAuthSession,\n} from '@graphorin/security/oauth';\n\nimport { MCPAuthError } from '../errors/index.js';\n\n/** Options accepted by {@link createOAuthAuthorizationProvider}. */\nexport interface OAuthAuthorizationProviderOptions {\n /**\n * Secrets store the persisted tokens resolve from (SPL-1) with it,\n * the bridge issues Authorization headers across process restarts.\n */\n readonly secretsStore?: import('@graphorin/core/contracts').SecretsStore;\n /** Stable identifier of the persisted OAuth server (`serverId`). */\n readonly serverId: string;\n /** Persistent storage. */\n readonly storage: OAuthServerStore;\n /**\n * Time-to-refresh window in milliseconds. When the session is\n * within `refreshAheadMs` of expiry the provider triggers a\n * refresh on the next request. Defaults to 5 minutes.\n */\n readonly refreshAheadMs?: number;\n /** Optional per-request `AbortSignal` (forwarded to refresh). */\n readonly signal?: AbortSignal;\n}\n\n/**\n * Live authorization-header provider returned by\n * {@link createOAuthAuthorizationProvider}.\n *\n * @stable\n */\nexport interface OAuthAuthorizationProvider {\n /** Resolve an `Authorization: Bearer ...` header. */\n resolveHeader(): Promise<string>;\n /** Force a refresh, regardless of expiry. */\n refresh(): Promise<OAuthSession>;\n /** Persist the most recently observed expiry timestamp. */\n readonly serverId: string;\n}\n\n/**\n * Build a provider that resolves the `Authorization` header value the\n * Streamable HTTP / SSE MCP transports send on every request.\n *\n * The provider:\n *\n * 1. Loads the persisted session metadata from the supplied store.\n * 2. Refreshes the session when it is within `refreshAheadMs` of\n * expiry.\n * 3. Wraps every refresh failure in {@link MCPAuthError} carrying a\n * `hint` that points the operator to the upcoming\n * `graphorin auth refresh` CLI.\n *\n * @stable\n */\nexport function createOAuthAuthorizationProvider(\n options: OAuthAuthorizationProviderOptions,\n): OAuthAuthorizationProvider {\n const refreshAheadMs = options.refreshAheadMs ?? 5 * 60_000;\n let activeSession: OAuthSession | undefined;\n\n async function loadOrRefresh(force: boolean): Promise<OAuthSession> {\n const record = await options.storage.get(options.serverId);\n if (record === null) {\n throw new MCPAuthError(`OAuth server '${options.serverId}' is not registered.`, {\n hint: `run 'graphorin auth login --mcp ${options.serverId} --url <server-url>' to register the OAuth server.`,\n metadata: { server: options.serverId },\n });\n }\n const expiresAt = record.expiresAt;\n const now = Date.now();\n const stale =\n force ||\n activeSession === undefined ||\n (expiresAt !== undefined && expiresAt - now < refreshAheadMs);\n if (!stale && activeSession !== undefined) return activeSession;\n try {\n const session = await refreshOAuthSession(options.storage, options.serverId, {\n ...(options.signal === undefined ? {} : { signal: options.signal }),\n ...(options.secretsStore === undefined ? {} : { secretsStore: options.secretsStore }),\n });\n activeSession = session;\n return session;\n } catch (cause) {\n if (cause instanceof GraphorinOAuthError) {\n emitOAuthLifecycle({\n type: 'mcp.auth.expired',\n serverId: options.serverId,\n ts: Date.now(),\n reason: cause.kind,\n metadata: { serverUrl: record.serverUrl },\n });\n throw new MCPAuthError(\n `MCP OAuth session for '${options.serverId}' could not be refreshed: ${cause.message}`,\n {\n hint: `run 'graphorin auth refresh --mcp ${options.serverId}' to recover.`,\n metadata: { server: options.serverId },\n cause,\n },\n );\n }\n throw new MCPAuthError(\n `MCP OAuth session for '${options.serverId}' could not be refreshed.`,\n {\n hint: `run 'graphorin auth refresh --mcp ${options.serverId}' to recover.`,\n metadata: { server: options.serverId },\n cause,\n },\n );\n }\n }\n\n return Object.freeze({\n serverId: options.serverId,\n async resolveHeader(): Promise<string> {\n const session = await loadOrRefresh(false);\n const tokenType = session.tokenType.length === 0 ? 'Bearer' : session.tokenType;\n return session.accessToken.use((raw) => `${tokenType} ${raw}`);\n },\n async refresh(): Promise<OAuthSession> {\n return loadOrRefresh(true);\n },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA0EA,SAAgB,iCACd,SAC4B;CAC5B,MAAM,iBAAiB,QAAQ,kBAAkB,IAAI;CACrD,IAAIA;CAEJ,eAAe,cAAc,OAAuC;EAClE,MAAM,SAAS,MAAM,QAAQ,QAAQ,IAAI,QAAQ,SAAS;AAC1D,MAAI,WAAW,KACb,OAAM,IAAI,aAAa,iBAAiB,QAAQ,SAAS,uBAAuB;GAC9E,MAAM,mCAAmC,QAAQ,SAAS;GAC1D,UAAU,EAAE,QAAQ,QAAQ,UAAU;GACvC,CAAC;EAEJ,MAAM,YAAY,OAAO;AAMzB,MAAI,EAHF,SACA,kBAAkB,UACjB,cAAc,UAAa,YAJlB,KAAK,KAAK,GAI0B,mBAClC,kBAAkB,OAAW,QAAO;AAClD,MAAI;GACF,MAAM,UAAU,MAAM,oBAAoB,QAAQ,SAAS,QAAQ,UAAU;IAC3E,GAAI,QAAQ,WAAW,SAAY,EAAE,GAAG,EAAE,QAAQ,QAAQ,QAAQ;IAClE,GAAI,QAAQ,iBAAiB,SAAY,EAAE,GAAG,EAAE,cAAc,QAAQ,cAAc;IACrF,CAAC;AACF,mBAAgB;AAChB,UAAO;WACA,OAAO;AACd,OAAI,iBAAiB,qBAAqB;AACxC,uBAAmB;KACjB,MAAM;KACN,UAAU,QAAQ;KAClB,IAAI,KAAK,KAAK;KACd,QAAQ,MAAM;KACd,UAAU,EAAE,WAAW,OAAO,WAAW;KAC1C,CAAC;AACF,UAAM,IAAI,aACR,0BAA0B,QAAQ,SAAS,4BAA4B,MAAM,WAC7E;KACE,MAAM,qCAAqC,QAAQ,SAAS;KAC5D,UAAU,EAAE,QAAQ,QAAQ,UAAU;KACtC;KACD,CACF;;AAEH,SAAM,IAAI,aACR,0BAA0B,QAAQ,SAAS,4BAC3C;IACE,MAAM,qCAAqC,QAAQ,SAAS;IAC5D,UAAU,EAAE,QAAQ,QAAQ,UAAU;IACtC;IACD,CACF;;;AAIL,QAAO,OAAO,OAAO;EACnB,UAAU,QAAQ;EAClB,MAAM,gBAAiC;GACrC,MAAM,UAAU,MAAM,cAAc,MAAM;GAC1C,MAAM,YAAY,QAAQ,UAAU,WAAW,IAAI,WAAW,QAAQ;AACtE,UAAO,QAAQ,YAAY,KAAK,QAAQ,GAAG,UAAU,GAAG,MAAM;;EAEhE,MAAM,UAAiC;AACrC,UAAO,cAAc,KAAK;;EAE7B,CAAC"}
1
+ {"version":3,"file":"bridge.js","names":["activeSession: OAuthSession | undefined"],"sources":["../../src/oauth/bridge.ts"],"sourcesContent":["/**\n * Bridge between the MCP transport's bearer-token requirement and the\n * outbound OAuth subsystem in `@graphorin/security/oauth`.\n *\n * The bridge resolves an authorization header from a stored\n * {@link OAuthSession}, refreshes the token automatically when it is\n * within the configured pre-expiry window, and surfaces every\n * lifecycle transition (`oauth.granted` / `oauth.refreshed` /\n * `oauth.revoked` / `mcp.auth.expired`) to subscribers.\n *\n * @packageDocumentation\n */\n\nimport type { OAuthServerStore } from '@graphorin/core/contracts';\nimport type { OAuthSession } from '@graphorin/security/oauth';\nimport {\n emitOAuthLifecycle,\n GraphorinOAuthError,\n refreshOAuthSession,\n} from '@graphorin/security/oauth';\n\nimport { MCPAuthError } from '../errors/index.js';\n\n/** Options accepted by {@link createOAuthAuthorizationProvider}. */\nexport interface OAuthAuthorizationProviderOptions {\n /**\n * Secrets store the persisted tokens resolve from (SPL-1) - with it,\n * the bridge issues Authorization headers across process restarts.\n */\n readonly secretsStore?: import('@graphorin/core/contracts').SecretsStore;\n /** Stable identifier of the persisted OAuth server (`serverId`). */\n readonly serverId: string;\n /** Persistent storage. */\n readonly storage: OAuthServerStore;\n /**\n * Time-to-refresh window in milliseconds. When the session is\n * within `refreshAheadMs` of expiry the provider triggers a\n * refresh on the next request. Defaults to 5 minutes.\n */\n readonly refreshAheadMs?: number;\n /** Optional per-request `AbortSignal` (forwarded to refresh). */\n readonly signal?: AbortSignal;\n}\n\n/**\n * Live authorization-header provider returned by\n * {@link createOAuthAuthorizationProvider}.\n *\n * @stable\n */\nexport interface OAuthAuthorizationProvider {\n /** Resolve an `Authorization: Bearer ...` header. */\n resolveHeader(): Promise<string>;\n /** Force a refresh, regardless of expiry. */\n refresh(): Promise<OAuthSession>;\n /** Persist the most recently observed expiry timestamp. */\n readonly serverId: string;\n}\n\n/**\n * Build a provider that resolves the `Authorization` header value the\n * Streamable HTTP / SSE MCP transports send on every request.\n *\n * The provider:\n *\n * 1. Loads the persisted session metadata from the supplied store.\n * 2. Refreshes the session when it is within `refreshAheadMs` of\n * expiry.\n * 3. Wraps every refresh failure in {@link MCPAuthError} carrying a\n * `hint` that points the operator to the upcoming\n * `graphorin auth refresh` CLI.\n *\n * @stable\n */\nexport function createOAuthAuthorizationProvider(\n options: OAuthAuthorizationProviderOptions,\n): OAuthAuthorizationProvider {\n const refreshAheadMs = options.refreshAheadMs ?? 5 * 60_000;\n let activeSession: OAuthSession | undefined;\n\n async function loadOrRefresh(force: boolean): Promise<OAuthSession> {\n const record = await options.storage.get(options.serverId);\n if (record === null) {\n throw new MCPAuthError(`OAuth server '${options.serverId}' is not registered.`, {\n hint: `run 'graphorin auth login --mcp ${options.serverId} --url <server-url>' to register the OAuth server.`,\n metadata: { server: options.serverId },\n });\n }\n const expiresAt = record.expiresAt;\n const now = Date.now();\n const stale =\n force ||\n activeSession === undefined ||\n (expiresAt !== undefined && expiresAt - now < refreshAheadMs);\n if (!stale && activeSession !== undefined) return activeSession;\n try {\n const session = await refreshOAuthSession(options.storage, options.serverId, {\n ...(options.signal === undefined ? {} : { signal: options.signal }),\n ...(options.secretsStore === undefined ? {} : { secretsStore: options.secretsStore }),\n });\n activeSession = session;\n return session;\n } catch (cause) {\n if (cause instanceof GraphorinOAuthError) {\n emitOAuthLifecycle({\n type: 'mcp.auth.expired',\n serverId: options.serverId,\n ts: Date.now(),\n reason: cause.kind,\n metadata: { serverUrl: record.serverUrl },\n });\n throw new MCPAuthError(\n `MCP OAuth session for '${options.serverId}' could not be refreshed: ${cause.message}`,\n {\n hint: `run 'graphorin auth refresh --mcp ${options.serverId}' to recover.`,\n metadata: { server: options.serverId },\n cause,\n },\n );\n }\n throw new MCPAuthError(\n `MCP OAuth session for '${options.serverId}' could not be refreshed.`,\n {\n hint: `run 'graphorin auth refresh --mcp ${options.serverId}' to recover.`,\n metadata: { server: options.serverId },\n cause,\n },\n );\n }\n }\n\n return Object.freeze({\n serverId: options.serverId,\n async resolveHeader(): Promise<string> {\n const session = await loadOrRefresh(false);\n const tokenType = session.tokenType.length === 0 ? 'Bearer' : session.tokenType;\n return session.accessToken.use((raw) => `${tokenType} ${raw}`);\n },\n async refresh(): Promise<OAuthSession> {\n return loadOrRefresh(true);\n },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA0EA,SAAgB,iCACd,SAC4B;CAC5B,MAAM,iBAAiB,QAAQ,kBAAkB,IAAI;CACrD,IAAIA;CAEJ,eAAe,cAAc,OAAuC;EAClE,MAAM,SAAS,MAAM,QAAQ,QAAQ,IAAI,QAAQ,SAAS;AAC1D,MAAI,WAAW,KACb,OAAM,IAAI,aAAa,iBAAiB,QAAQ,SAAS,uBAAuB;GAC9E,MAAM,mCAAmC,QAAQ,SAAS;GAC1D,UAAU,EAAE,QAAQ,QAAQ,UAAU;GACvC,CAAC;EAEJ,MAAM,YAAY,OAAO;AAMzB,MAAI,EAHF,SACA,kBAAkB,UACjB,cAAc,UAAa,YAJlB,KAAK,KAAK,GAI0B,mBAClC,kBAAkB,OAAW,QAAO;AAClD,MAAI;GACF,MAAM,UAAU,MAAM,oBAAoB,QAAQ,SAAS,QAAQ,UAAU;IAC3E,GAAI,QAAQ,WAAW,SAAY,EAAE,GAAG,EAAE,QAAQ,QAAQ,QAAQ;IAClE,GAAI,QAAQ,iBAAiB,SAAY,EAAE,GAAG,EAAE,cAAc,QAAQ,cAAc;IACrF,CAAC;AACF,mBAAgB;AAChB,UAAO;WACA,OAAO;AACd,OAAI,iBAAiB,qBAAqB;AACxC,uBAAmB;KACjB,MAAM;KACN,UAAU,QAAQ;KAClB,IAAI,KAAK,KAAK;KACd,QAAQ,MAAM;KACd,UAAU,EAAE,WAAW,OAAO,WAAW;KAC1C,CAAC;AACF,UAAM,IAAI,aACR,0BAA0B,QAAQ,SAAS,4BAA4B,MAAM,WAC7E;KACE,MAAM,qCAAqC,QAAQ,SAAS;KAC5D,UAAU,EAAE,QAAQ,QAAQ,UAAU;KACtC;KACD,CACF;;AAEH,SAAM,IAAI,aACR,0BAA0B,QAAQ,SAAS,4BAC3C;IACE,MAAM,qCAAqC,QAAQ,SAAS;IAC5D,UAAU,EAAE,QAAQ,QAAQ,UAAU;IACtC;IACD,CACF;;;AAIL,QAAO,OAAO,OAAO;EACnB,UAAU,QAAQ;EAClB,MAAM,gBAAiC;GACrC,MAAM,UAAU,MAAM,cAAc,MAAM;GAC1C,MAAM,YAAY,QAAQ,UAAU,WAAW,IAAI,WAAW,QAAQ;AACtE,UAAO,QAAQ,YAAY,KAAK,QAAQ,GAAG,UAAU,GAAG,MAAM;;EAEhE,MAAM,UAAiC;AACrC,UAAO,cAAc,KAAK;;EAE7B,CAAC"}
@@ -0,0 +1,6 @@
1
+ //#region package.json
2
+ var version = "0.6.1";
3
+
4
+ //#endregion
5
+ export { version };
6
+ //# sourceMappingURL=package.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package.js","names":[],"sources":["../package.json"],"sourcesContent":["{\n \"name\": \"@graphorin/mcp\",\n \"version\": \"0.6.1\",\n \"description\": \"Model Context Protocol client for the Graphorin framework: stdio + Streamable HTTP + SSE transports, typed `MCPClient` (`listTools` / `listResources` / `listPrompts` / `callTool`), `toTools()` adapter (per-server inbound prompt-injection sanitization, deferred-loading auto-default at the 10-tool threshold, structured-content + outputSchema round-trip with backward-compatible TextContent mirror, per-server result envelope overrides, per-server call timeout, mcp provenance stamping for the dataflow policy, per-server preferredModel and side-effect class overrides), OAuth integration backed by @graphorin/security/oauth, typed errors, and library helpers consumed by the graphorin auth CLI.\",\n \"license\": \"MIT\",\n \"author\": \"Oleksiy Stepurenko\",\n \"homepage\": \"https://github.com/o-stepper/graphorin/tree/main/packages/mcp\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/o-stepper/graphorin.git\",\n \"directory\": \"packages/mcp\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/o-stepper/graphorin/issues\"\n },\n \"keywords\": [\n \"graphorin\",\n \"ai\",\n \"agents\",\n \"framework\",\n \"mcp\",\n \"model-context-protocol\",\n \"mcp-client\",\n \"stdio\",\n \"streamable-http\",\n \"sse\",\n \"oauth\",\n \"structured-content\",\n \"resumable-sessions\",\n \"tool-collision\",\n \"deferred-loading\",\n \"inbound-sanitization\"\n ],\n \"type\": \"module\",\n \"engines\": {\n \"node\": \">=22.0.0\"\n },\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./client\": {\n \"types\": \"./dist/client/index.d.ts\",\n \"import\": \"./dist/client/index.js\"\n },\n \"./transport\": {\n \"types\": \"./dist/transport/index.d.ts\",\n \"import\": \"./dist/transport/index.js\"\n },\n \"./oauth\": {\n \"types\": \"./dist/oauth/index.d.ts\",\n \"import\": \"./dist/oauth/index.js\"\n },\n \"./helpers\": {\n \"types\": \"./dist/helpers/index.d.ts\",\n \"import\": \"./dist/helpers/index.js\"\n },\n \"./errors\": {\n \"types\": \"./dist/errors/index.d.ts\",\n \"import\": \"./dist/errors/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"CHANGELOG.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsdown\",\n \"typecheck\": \"tsc --noEmit && tsc -p tsconfig.tests.json\",\n \"test\": \"vitest run\",\n \"lint\": \"biome check .\",\n \"clean\": \"rimraf dist .turbo *.tsbuildinfo\"\n },\n \"dependencies\": {\n \"@graphorin/core\": \"workspace:*\",\n \"@graphorin/observability\": \"workspace:*\",\n \"@graphorin/security\": \"workspace:*\",\n \"@graphorin/tools\": \"workspace:*\",\n \"@modelcontextprotocol/sdk\": \"^1.29.0\"\n },\n \"peerDependencies\": {\n \"zod\": \"^3.23.0 || ^4.0.0\"\n },\n \"peerDependenciesMeta\": {\n \"zod\": {\n \"optional\": false\n }\n },\n \"publishConfig\": {\n \"access\": \"public\",\n \"provenance\": true\n },\n \"devDependencies\": {\n \"fast-check\": \"^3.23.0\",\n \"hono\": \"^4.12.21\",\n \"zod\": \"^3.25.0\"\n }\n}\n"],"mappings":";cAEa"}
@@ -5,6 +5,13 @@
5
5
  * contract from `@graphorin/core` (a `parse` method that throws + a
6
6
  * `safeParse` method that returns a `ZodLikeSafeParseResult`).
7
7
  *
8
+ * The validator also retains the **source JSON Schema** and exposes it
9
+ * via `toJSON()` (tools-01): `toolToDefinition` and the code-mode
10
+ * signature projection honour `toJSON()`, so without it an MCP tool's
11
+ * parameters serialise to `{}` on the provider wire body - the model
12
+ * would see an argument-less tool. Boolean schemas normalise to their
13
+ * record equivalents (`true` → `{}`, `false` → `{ not: {} }`).
14
+ *
8
15
  * @stable
9
16
  */
10
17
  function buildJsonSchemaValidator(schema) {
@@ -24,9 +31,15 @@ function buildJsonSchemaValidator(schema) {
24
31
  error: buildError(issues)
25
32
  };
26
33
  }
34
+ function toJSON() {
35
+ if (schema === true) return {};
36
+ if (schema === false) return { not: {} };
37
+ return schema;
38
+ }
27
39
  return Object.freeze({
28
40
  parse,
29
- safeParse
41
+ safeParse,
42
+ toJSON
30
43
  });
31
44
  }
32
45
  function validate(value, schema, path) {
@@ -139,14 +152,30 @@ function validateString(value, schema, path) {
139
152
  path,
140
153
  message: `expected at most ${schema.maxLength} characters`
141
154
  });
142
- if (typeof schema.pattern === "string") try {
143
- if (!new RegExp(schema.pattern).test(value)) issues.push({
144
- path,
145
- message: `did not match pattern ${schema.pattern}`
146
- });
147
- } catch {}
155
+ if (typeof schema.pattern === "string") {
156
+ if (schema.pattern.length <= MAX_PATTERN_LENGTH && value.length <= MAX_PATTERN_TEST_LENGTH && !looksCatastrophic(schema.pattern)) try {
157
+ if (!new RegExp(schema.pattern).test(value)) issues.push({
158
+ path,
159
+ message: `did not match pattern ${schema.pattern}`
160
+ });
161
+ } catch {}
162
+ }
148
163
  return issues;
149
164
  }
165
+ /** mcp-skills-07: hard caps on server-supplied `pattern` evaluation. */
166
+ const MAX_PATTERN_LENGTH = 1e3;
167
+ const MAX_PATTERN_TEST_LENGTH = 1e4;
168
+ /**
169
+ * Cheap heuristic for the classic catastrophic-backtracking shape: a
170
+ * group whose inner expression ends with a quantifier and which is
171
+ * itself quantified (`(a+)+`, `(a*)*`, `(a+){2,}`, `(?:x+)*`). A
172
+ * linear-time engine (re2) would make this exact; the heuristic errs
173
+ * on the safe side for untrusted input, and a rejected pattern simply
174
+ * degrades to permissive.
175
+ */
176
+ function looksCatastrophic(pattern) {
177
+ return /\)[*+]|\)\{\d+,(?:\d+)?\}/.test(pattern) && /[*+}]\s*\)/.test(pattern);
178
+ }
150
179
  function validateNumber(value, schema, path) {
151
180
  if (schema === true || schema === false) return [];
152
181
  const issues = [];
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema.js","names":["issues: Issue[]","types: ReadonlyArray<string>"],"sources":["../../src/registry/json-schema.ts"],"sourcesContent":["/**\n * Lightweight JSON-Schema -> {@link ZodLikeSchema} adapter.\n *\n * The Model Context Protocol carries tool input + output schemas as\n * JSON Schema documents; the Graphorin tool registry types its\n * schemas via the `ZodLikeSchema` structural contract from\n * `@graphorin/core`. This module bridges the two without pulling\n * `zod` into the MCP boundary or relying on code generation +\n * runtime `eval` (the popular `json-schema-to-zod` package generates\n * source code that needs `new Function(...)` to execute).\n *\n * The adapter validates the canonical subset of JSON Schema the MCP\n * spec uses: `object` (with `properties`, `required`,\n * `additionalProperties`), `array` (with `items`, `minItems`,\n * `maxItems`), `string` (with `enum`, `minLength`, `maxLength`,\n * `pattern`), `number` / `integer` (with `enum`, `minimum`,\n * `maximum`), `boolean`, `null`, and the primitive composition\n * keywords `enum`, `const`, `oneOf`, `anyOf`, `allOf`. Unknown keys\n * are accepted permissively so newer MCP server schemas that ship\n * additional vocabulary do not break the adapter.\n *\n * @packageDocumentation\n */\n\nimport type { ZodLikeError, ZodLikeSafeParseResult, ZodLikeSchema } from '@graphorin/core';\n\n/** JSON Schema document subset accepted by {@link buildJsonSchemaValidator}. */\nexport type JsonSchemaLike =\n | boolean\n | (Readonly<Record<string, unknown>> & {\n readonly type?: string | ReadonlyArray<string>;\n readonly properties?: Readonly<Record<string, JsonSchemaLike>>;\n readonly required?: ReadonlyArray<string>;\n readonly additionalProperties?: boolean | JsonSchemaLike;\n readonly items?: JsonSchemaLike | ReadonlyArray<JsonSchemaLike>;\n readonly minItems?: number;\n readonly maxItems?: number;\n readonly minimum?: number;\n readonly maximum?: number;\n readonly minLength?: number;\n readonly maxLength?: number;\n readonly pattern?: string;\n readonly enum?: ReadonlyArray<unknown>;\n readonly const?: unknown;\n readonly oneOf?: ReadonlyArray<JsonSchemaLike>;\n readonly anyOf?: ReadonlyArray<JsonSchemaLike>;\n readonly allOf?: ReadonlyArray<JsonSchemaLike>;\n });\n\n/**\n * Build a {@link ZodLikeSchema} that validates `data` against\n * `schema`. The returned instance follows the structural Zod\n * contract from `@graphorin/core` (a `parse` method that throws + a\n * `safeParse` method that returns a `ZodLikeSafeParseResult`).\n *\n * @stable\n */\nexport function buildJsonSchemaValidator<T = unknown>(schema: JsonSchemaLike): ZodLikeSchema<T> {\n function parse(data: unknown): T {\n const issues = validate(data, schema, []);\n if (issues.length === 0) return data as T;\n throw buildError(issues);\n }\n function safeParse(data: unknown): ZodLikeSafeParseResult<T, unknown> {\n const issues = validate(data, schema, []);\n if (issues.length === 0) {\n return { success: true, data: data as T };\n }\n return { success: false, error: buildError(issues) };\n }\n return Object.freeze({ parse, safeParse });\n}\n\ninterface Issue {\n readonly path: ReadonlyArray<string | number>;\n readonly message: string;\n}\n\nfunction validate(\n value: unknown,\n schema: JsonSchemaLike,\n path: ReadonlyArray<string | number>,\n): Issue[] {\n if (schema === true) return [];\n if (schema === false) return [{ path, message: 'rejected by schema (false)' }];\n\n const issues: Issue[] = [];\n\n if ('const' in schema && schema.const !== undefined) {\n if (!equalsDeep(value, schema.const)) {\n issues.push({ path, message: `expected const ${formatValue(schema.const)}` });\n }\n }\n if (Array.isArray(schema.enum)) {\n if (!schema.enum.some((candidate) => equalsDeep(value, candidate))) {\n issues.push({ path, message: 'value does not match any enum entry' });\n }\n }\n if (Array.isArray(schema.allOf)) {\n for (const sub of schema.allOf) {\n issues.push(...validate(value, sub, path));\n }\n }\n if (Array.isArray(schema.anyOf)) {\n const anyOk = schema.anyOf.some((sub) => validate(value, sub, path).length === 0);\n if (!anyOk) issues.push({ path, message: 'value did not match any of the anyOf branches' });\n }\n if (Array.isArray(schema.oneOf)) {\n const matchCount = schema.oneOf.filter((sub) => validate(value, sub, path).length === 0).length;\n if (matchCount !== 1) {\n issues.push({\n path,\n message: `expected exactly one oneOf branch to match (got ${matchCount})`,\n });\n }\n }\n\n if (schema.type !== undefined) {\n const typeIssues = validateType(value, schema, path);\n issues.push(...typeIssues);\n }\n\n return issues;\n}\n\nfunction validateType(\n value: unknown,\n schema: JsonSchemaLike,\n path: ReadonlyArray<string | number>,\n): Issue[] {\n if (schema === true || schema === false) return [];\n const types: ReadonlyArray<string> = Array.isArray(schema.type)\n ? schema.type\n : schema.type === undefined\n ? []\n : [schema.type];\n if (types.length === 0) return [];\n if (!types.some((t) => matchesType(value, t))) {\n return [{ path, message: `expected type ${types.join(' | ')}` }];\n }\n\n if (\n matchesType(value, 'object') &&\n (schema.properties !== undefined ||\n schema.required !== undefined ||\n schema.additionalProperties !== undefined)\n ) {\n return validateObject(value as Record<string, unknown>, schema, path);\n }\n if (matchesType(value, 'array')) {\n return validateArray(value as unknown[], schema, path);\n }\n if (matchesType(value, 'string')) {\n return validateString(value as string, schema, path);\n }\n if (matchesType(value, 'number') || matchesType(value, 'integer')) {\n return validateNumber(value as number, schema, path);\n }\n return [];\n}\n\nfunction validateObject(\n value: Record<string, unknown>,\n schema: JsonSchemaLike & { readonly type?: unknown },\n path: ReadonlyArray<string | number>,\n): Issue[] {\n if (schema === true || schema === false) return [];\n const issues: Issue[] = [];\n const required = Array.isArray(schema.required) ? schema.required : [];\n for (const key of required) {\n if (!(key in value)) {\n issues.push({ path: [...path, key], message: 'is required' });\n }\n }\n const properties = schema.properties ?? {};\n for (const [key, subSchema] of Object.entries(properties)) {\n if (!(key in value)) continue;\n issues.push(...validate(value[key], subSchema, [...path, key]));\n }\n if (schema.additionalProperties === false) {\n for (const key of Object.keys(value)) {\n if (!(key in properties)) {\n issues.push({ path: [...path, key], message: 'unexpected additional property' });\n }\n }\n } else if (\n typeof schema.additionalProperties === 'object' &&\n schema.additionalProperties !== null\n ) {\n for (const key of Object.keys(value)) {\n if (key in properties) continue;\n issues.push(\n ...validate(value[key], schema.additionalProperties as JsonSchemaLike, [...path, key]),\n );\n }\n }\n return issues;\n}\n\nfunction validateArray(\n value: unknown[],\n schema: JsonSchemaLike & { readonly type?: unknown },\n path: ReadonlyArray<string | number>,\n): Issue[] {\n if (schema === true || schema === false) return [];\n const issues: Issue[] = [];\n if (typeof schema.minItems === 'number' && value.length < schema.minItems) {\n issues.push({ path, message: `expected at least ${schema.minItems} items` });\n }\n if (typeof schema.maxItems === 'number' && value.length > schema.maxItems) {\n issues.push({ path, message: `expected at most ${schema.maxItems} items` });\n }\n const items = schema.items;\n if (items === undefined) return issues;\n if (Array.isArray(items)) {\n for (let i = 0; i < items.length; i++) {\n if (i >= value.length) break;\n const sub = items[i];\n if (sub === undefined) continue;\n issues.push(...validate(value[i], sub, [...path, i]));\n }\n } else {\n for (let i = 0; i < value.length; i++) {\n issues.push(...validate(value[i], items as JsonSchemaLike, [...path, i]));\n }\n }\n return issues;\n}\n\nfunction validateString(\n value: string,\n schema: JsonSchemaLike & { readonly type?: unknown },\n path: ReadonlyArray<string | number>,\n): Issue[] {\n if (schema === true || schema === false) return [];\n const issues: Issue[] = [];\n if (typeof schema.minLength === 'number' && value.length < schema.minLength) {\n issues.push({ path, message: `expected at least ${schema.minLength} characters` });\n }\n if (typeof schema.maxLength === 'number' && value.length > schema.maxLength) {\n issues.push({ path, message: `expected at most ${schema.maxLength} characters` });\n }\n if (typeof schema.pattern === 'string') {\n try {\n const re = new RegExp(schema.pattern);\n if (!re.test(value))\n issues.push({ path, message: `did not match pattern ${schema.pattern}` });\n } catch {\n // Treat malformed patterns as permissive (mirrors Ajv default).\n }\n }\n return issues;\n}\n\nfunction validateNumber(\n value: number,\n schema: JsonSchemaLike & { readonly type?: unknown },\n path: ReadonlyArray<string | number>,\n): Issue[] {\n if (schema === true || schema === false) return [];\n const issues: Issue[] = [];\n const types: ReadonlyArray<string> = Array.isArray(schema.type)\n ? schema.type\n : schema.type === undefined\n ? []\n : [schema.type];\n if (types.includes('integer') && !Number.isInteger(value)) {\n issues.push({ path, message: 'expected an integer' });\n }\n if (typeof schema.minimum === 'number' && value < schema.minimum) {\n issues.push({ path, message: `expected >= ${schema.minimum}` });\n }\n if (typeof schema.maximum === 'number' && value > schema.maximum) {\n issues.push({ path, message: `expected <= ${schema.maximum}` });\n }\n return issues;\n}\n\nfunction matchesType(value: unknown, type: string): boolean {\n switch (type) {\n case 'string':\n return typeof value === 'string';\n case 'number':\n return typeof value === 'number' && Number.isFinite(value);\n case 'integer':\n return typeof value === 'number' && Number.isFinite(value);\n case 'boolean':\n return typeof value === 'boolean';\n case 'null':\n return value === null;\n case 'array':\n return Array.isArray(value);\n case 'object':\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n default:\n return true;\n }\n}\n\nfunction equalsDeep(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === null || b === null) return a === b;\n if (typeof a !== typeof b) return false;\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n return a.every((item, i) => equalsDeep(item, b[i]));\n }\n if (typeof a === 'object' && typeof b === 'object') {\n const ak = Object.keys(a as Record<string, unknown>);\n const bk = Object.keys(b as Record<string, unknown>);\n if (ak.length !== bk.length) return false;\n return ak.every((key) =>\n equalsDeep((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key]),\n );\n }\n return false;\n}\n\nfunction formatValue(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return Object.prototype.toString.call(value);\n }\n}\n\nfunction buildError(issues: ReadonlyArray<Issue>): ZodLikeError {\n return {\n name: 'GraphorinMCPSchemaError',\n message: issues\n .map((i) => `${i.path.length === 0 ? '.' : i.path.join('.')}: ${i.message}`)\n .join('; '),\n issues: Object.freeze(\n issues.map((i) => ({ path: Object.freeze([...i.path]), message: i.message })),\n ),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"json-schema.js","names":["issues: Issue[]","types: ReadonlyArray<string>"],"sources":["../../src/registry/json-schema.ts"],"sourcesContent":["/**\n * Lightweight JSON-Schema -> {@link ZodLikeSchema} adapter.\n *\n * The Model Context Protocol carries tool input + output schemas as\n * JSON Schema documents; the Graphorin tool registry types its\n * schemas via the `ZodLikeSchema` structural contract from\n * `@graphorin/core`. This module bridges the two without pulling\n * `zod` into the MCP boundary or relying on code generation +\n * runtime `eval` (the popular `json-schema-to-zod` package generates\n * source code that needs `new Function(...)` to execute).\n *\n * The adapter validates the canonical subset of JSON Schema the MCP\n * spec uses: `object` (with `properties`, `required`,\n * `additionalProperties`), `array` (with `items`, `minItems`,\n * `maxItems`), `string` (with `enum`, `minLength`, `maxLength`,\n * `pattern`), `number` / `integer` (with `enum`, `minimum`,\n * `maximum`), `boolean`, `null`, and the primitive composition\n * keywords `enum`, `const`, `oneOf`, `anyOf`, `allOf`. Unknown keys\n * are accepted permissively so newer MCP server schemas that ship\n * additional vocabulary do not break the adapter.\n *\n * @packageDocumentation\n */\n\nimport type { ZodLikeError, ZodLikeSafeParseResult, ZodLikeSchema } from '@graphorin/core';\n\n/** JSON Schema document subset accepted by {@link buildJsonSchemaValidator}. */\nexport type JsonSchemaLike =\n | boolean\n | (Readonly<Record<string, unknown>> & {\n readonly type?: string | ReadonlyArray<string>;\n readonly properties?: Readonly<Record<string, JsonSchemaLike>>;\n readonly required?: ReadonlyArray<string>;\n readonly additionalProperties?: boolean | JsonSchemaLike;\n readonly items?: JsonSchemaLike | ReadonlyArray<JsonSchemaLike>;\n readonly minItems?: number;\n readonly maxItems?: number;\n readonly minimum?: number;\n readonly maximum?: number;\n readonly minLength?: number;\n readonly maxLength?: number;\n readonly pattern?: string;\n readonly enum?: ReadonlyArray<unknown>;\n readonly const?: unknown;\n readonly oneOf?: ReadonlyArray<JsonSchemaLike>;\n readonly anyOf?: ReadonlyArray<JsonSchemaLike>;\n readonly allOf?: ReadonlyArray<JsonSchemaLike>;\n });\n\n/**\n * Build a {@link ZodLikeSchema} that validates `data` against\n * `schema`. The returned instance follows the structural Zod\n * contract from `@graphorin/core` (a `parse` method that throws + a\n * `safeParse` method that returns a `ZodLikeSafeParseResult`).\n *\n * The validator also retains the **source JSON Schema** and exposes it\n * via `toJSON()` (tools-01): `toolToDefinition` and the code-mode\n * signature projection honour `toJSON()`, so without it an MCP tool's\n * parameters serialise to `{}` on the provider wire body - the model\n * would see an argument-less tool. Boolean schemas normalise to their\n * record equivalents (`true` → `{}`, `false` → `{ not: {} }`).\n *\n * @stable\n */\nexport function buildJsonSchemaValidator<T = unknown>(schema: JsonSchemaLike): ZodLikeSchema<T> {\n function parse(data: unknown): T {\n const issues = validate(data, schema, []);\n if (issues.length === 0) return data as T;\n throw buildError(issues);\n }\n function safeParse(data: unknown): ZodLikeSafeParseResult<T, unknown> {\n const issues = validate(data, schema, []);\n if (issues.length === 0) {\n return { success: true, data: data as T };\n }\n return { success: false, error: buildError(issues) };\n }\n function toJSON(): Readonly<Record<string, unknown>> {\n if (schema === true) return {};\n if (schema === false) return { not: {} };\n return schema;\n }\n return Object.freeze({ parse, safeParse, toJSON });\n}\n\ninterface Issue {\n readonly path: ReadonlyArray<string | number>;\n readonly message: string;\n}\n\nfunction validate(\n value: unknown,\n schema: JsonSchemaLike,\n path: ReadonlyArray<string | number>,\n): Issue[] {\n if (schema === true) return [];\n if (schema === false) return [{ path, message: 'rejected by schema (false)' }];\n\n const issues: Issue[] = [];\n\n if ('const' in schema && schema.const !== undefined) {\n if (!equalsDeep(value, schema.const)) {\n issues.push({ path, message: `expected const ${formatValue(schema.const)}` });\n }\n }\n if (Array.isArray(schema.enum)) {\n if (!schema.enum.some((candidate) => equalsDeep(value, candidate))) {\n issues.push({ path, message: 'value does not match any enum entry' });\n }\n }\n if (Array.isArray(schema.allOf)) {\n for (const sub of schema.allOf) {\n issues.push(...validate(value, sub, path));\n }\n }\n if (Array.isArray(schema.anyOf)) {\n const anyOk = schema.anyOf.some((sub) => validate(value, sub, path).length === 0);\n if (!anyOk) issues.push({ path, message: 'value did not match any of the anyOf branches' });\n }\n if (Array.isArray(schema.oneOf)) {\n const matchCount = schema.oneOf.filter((sub) => validate(value, sub, path).length === 0).length;\n if (matchCount !== 1) {\n issues.push({\n path,\n message: `expected exactly one oneOf branch to match (got ${matchCount})`,\n });\n }\n }\n\n if (schema.type !== undefined) {\n const typeIssues = validateType(value, schema, path);\n issues.push(...typeIssues);\n }\n\n return issues;\n}\n\nfunction validateType(\n value: unknown,\n schema: JsonSchemaLike,\n path: ReadonlyArray<string | number>,\n): Issue[] {\n if (schema === true || schema === false) return [];\n const types: ReadonlyArray<string> = Array.isArray(schema.type)\n ? schema.type\n : schema.type === undefined\n ? []\n : [schema.type];\n if (types.length === 0) return [];\n if (!types.some((t) => matchesType(value, t))) {\n return [{ path, message: `expected type ${types.join(' | ')}` }];\n }\n\n if (\n matchesType(value, 'object') &&\n (schema.properties !== undefined ||\n schema.required !== undefined ||\n schema.additionalProperties !== undefined)\n ) {\n return validateObject(value as Record<string, unknown>, schema, path);\n }\n if (matchesType(value, 'array')) {\n return validateArray(value as unknown[], schema, path);\n }\n if (matchesType(value, 'string')) {\n return validateString(value as string, schema, path);\n }\n if (matchesType(value, 'number') || matchesType(value, 'integer')) {\n return validateNumber(value as number, schema, path);\n }\n return [];\n}\n\nfunction validateObject(\n value: Record<string, unknown>,\n schema: JsonSchemaLike & { readonly type?: unknown },\n path: ReadonlyArray<string | number>,\n): Issue[] {\n if (schema === true || schema === false) return [];\n const issues: Issue[] = [];\n const required = Array.isArray(schema.required) ? schema.required : [];\n for (const key of required) {\n if (!(key in value)) {\n issues.push({ path: [...path, key], message: 'is required' });\n }\n }\n const properties = schema.properties ?? {};\n for (const [key, subSchema] of Object.entries(properties)) {\n if (!(key in value)) continue;\n issues.push(...validate(value[key], subSchema, [...path, key]));\n }\n if (schema.additionalProperties === false) {\n for (const key of Object.keys(value)) {\n if (!(key in properties)) {\n issues.push({ path: [...path, key], message: 'unexpected additional property' });\n }\n }\n } else if (\n typeof schema.additionalProperties === 'object' &&\n schema.additionalProperties !== null\n ) {\n for (const key of Object.keys(value)) {\n if (key in properties) continue;\n issues.push(\n ...validate(value[key], schema.additionalProperties as JsonSchemaLike, [...path, key]),\n );\n }\n }\n return issues;\n}\n\nfunction validateArray(\n value: unknown[],\n schema: JsonSchemaLike & { readonly type?: unknown },\n path: ReadonlyArray<string | number>,\n): Issue[] {\n if (schema === true || schema === false) return [];\n const issues: Issue[] = [];\n if (typeof schema.minItems === 'number' && value.length < schema.minItems) {\n issues.push({ path, message: `expected at least ${schema.minItems} items` });\n }\n if (typeof schema.maxItems === 'number' && value.length > schema.maxItems) {\n issues.push({ path, message: `expected at most ${schema.maxItems} items` });\n }\n const items = schema.items;\n if (items === undefined) return issues;\n if (Array.isArray(items)) {\n for (let i = 0; i < items.length; i++) {\n if (i >= value.length) break;\n const sub = items[i];\n if (sub === undefined) continue;\n issues.push(...validate(value[i], sub, [...path, i]));\n }\n } else {\n for (let i = 0; i < value.length; i++) {\n issues.push(...validate(value[i], items as JsonSchemaLike, [...path, i]));\n }\n }\n return issues;\n}\n\nfunction validateString(\n value: string,\n schema: JsonSchemaLike & { readonly type?: unknown },\n path: ReadonlyArray<string | number>,\n): Issue[] {\n if (schema === true || schema === false) return [];\n const issues: Issue[] = [];\n if (typeof schema.minLength === 'number' && value.length < schema.minLength) {\n issues.push({ path, message: `expected at least ${schema.minLength} characters` });\n }\n if (typeof schema.maxLength === 'number' && value.length > schema.maxLength) {\n issues.push({ path, message: `expected at most ${schema.maxLength} characters` });\n }\n if (typeof schema.pattern === 'string') {\n // mcp-skills-07: the pattern comes VERBATIM from the (untrusted)\n // MCP server and runs on every validated input - a\n // catastrophic-backtracking pattern (`(a+)+$`) plus a long\n // model-generated string stalls the agent's event loop. Guards:\n // cap the pattern and the tested-string length, and reject the\n // classic nested-quantifier shapes heuristically. A guarded-out\n // pattern degrades to permissive (same as a malformed one).\n if (\n schema.pattern.length <= MAX_PATTERN_LENGTH &&\n value.length <= MAX_PATTERN_TEST_LENGTH &&\n !looksCatastrophic(schema.pattern)\n ) {\n try {\n const re = new RegExp(schema.pattern);\n if (!re.test(value))\n issues.push({ path, message: `did not match pattern ${schema.pattern}` });\n } catch {\n // Treat malformed patterns as permissive (mirrors Ajv default).\n }\n }\n }\n return issues;\n}\n\n/** mcp-skills-07: hard caps on server-supplied `pattern` evaluation. */\nconst MAX_PATTERN_LENGTH = 1_000;\nconst MAX_PATTERN_TEST_LENGTH = 10_000;\n\n/**\n * Cheap heuristic for the classic catastrophic-backtracking shape: a\n * group whose inner expression ends with a quantifier and which is\n * itself quantified (`(a+)+`, `(a*)*`, `(a+){2,}`, `(?:x+)*`). A\n * linear-time engine (re2) would make this exact; the heuristic errs\n * on the safe side for untrusted input, and a rejected pattern simply\n * degrades to permissive.\n */\nfunction looksCatastrophic(pattern: string): boolean {\n return /\\)[*+]|\\)\\{\\d+,(?:\\d+)?\\}/.test(pattern) && /[*+}]\\s*\\)/.test(pattern);\n}\n\nfunction validateNumber(\n value: number,\n schema: JsonSchemaLike & { readonly type?: unknown },\n path: ReadonlyArray<string | number>,\n): Issue[] {\n if (schema === true || schema === false) return [];\n const issues: Issue[] = [];\n const types: ReadonlyArray<string> = Array.isArray(schema.type)\n ? schema.type\n : schema.type === undefined\n ? []\n : [schema.type];\n if (types.includes('integer') && !Number.isInteger(value)) {\n issues.push({ path, message: 'expected an integer' });\n }\n if (typeof schema.minimum === 'number' && value < schema.minimum) {\n issues.push({ path, message: `expected >= ${schema.minimum}` });\n }\n if (typeof schema.maximum === 'number' && value > schema.maximum) {\n issues.push({ path, message: `expected <= ${schema.maximum}` });\n }\n return issues;\n}\n\nfunction matchesType(value: unknown, type: string): boolean {\n switch (type) {\n case 'string':\n return typeof value === 'string';\n case 'number':\n return typeof value === 'number' && Number.isFinite(value);\n case 'integer':\n return typeof value === 'number' && Number.isFinite(value);\n case 'boolean':\n return typeof value === 'boolean';\n case 'null':\n return value === null;\n case 'array':\n return Array.isArray(value);\n case 'object':\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n default:\n return true;\n }\n}\n\nfunction equalsDeep(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === null || b === null) return a === b;\n if (typeof a !== typeof b) return false;\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n return a.every((item, i) => equalsDeep(item, b[i]));\n }\n if (typeof a === 'object' && typeof b === 'object') {\n const ak = Object.keys(a as Record<string, unknown>);\n const bk = Object.keys(b as Record<string, unknown>);\n if (ak.length !== bk.length) return false;\n return ak.every((key) =>\n equalsDeep((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key]),\n );\n }\n return false;\n}\n\nfunction formatValue(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return Object.prototype.toString.call(value);\n }\n}\n\nfunction buildError(issues: ReadonlyArray<Issue>): ZodLikeError {\n return {\n name: 'GraphorinMCPSchemaError',\n message: issues\n .map((i) => `${i.path.length === 0 ? '.' : i.path.join('.')}: ${i.message}`)\n .join('; '),\n issues: Object.freeze(\n issues.map((i) => ({ path: Object.freeze([...i.path]), message: i.message })),\n ),\n };\n}\n"],"mappings":"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"}
@@ -5,16 +5,16 @@
5
5
  * The discriminated union mirrors the three transports the
6
6
  * `@modelcontextprotocol/sdk@^1.29.0` package exports:
7
7
  *
8
- * - `'stdio'` the primary transport for local MCP servers
8
+ * - `'stdio'` - the primary transport for local MCP servers
9
9
  * started as a child process. The transport spawns the configured
10
10
  * command, pipes JSON-RPC over stdio, and tears the child down on
11
11
  * `client.close()`.
12
- * - `'streamable-http'` the current default transport for remote MCP
12
+ * - `'streamable-http'` - the current default transport for remote MCP
13
13
  * servers (the spec-recommended replacement for the legacy SSE
14
14
  * transport). Supports server-assigned `Mcp-Session-Id` + the
15
15
  * `Last-Event-ID` resume handshake when the server advertises it on
16
16
  * `initialize`.
17
- * - `'sse'` the deprecated legacy transport. Kept for
17
+ * - `'sse'` - the deprecated legacy transport. Kept for
18
18
  * back-compat with MCP servers that have not yet migrated to the
19
19
  * streamable HTTP transport. The runtime emits one WARN-per-process
20
20
  * on transport selection; the transport is not eligible for the
@@ -45,7 +45,7 @@ interface StreamableHttpTransportConfig {
45
45
  readonly headers?: Readonly<Record<string, string>>;
46
46
  /**
47
47
  * Optional pre-existing session id. Most operators leave this
48
- * unset the server assigns one on `initialize` and the client
48
+ * unset - the server assigns one on `initialize` and the client
49
49
  * persists it for the lifetime of the connection.
50
50
  */
51
51
  readonly sessionId?: string;