@graphorin/mcp 0.6.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.
- package/CHANGELOG.md +12 -0
- package/README.md +11 -4
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +2 -1
- package/dist/client/client.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/package.js +6 -0
- package/dist/package.js.map +1 -0
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @graphorin/mcp
|
|
2
2
|
|
|
3
|
+
## 0.6.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#142](https://github.com/o-stepper/graphorin/pull/142) [`436d6ca`](https://github.com/o-stepper/graphorin/commit/436d6ca5ebbd16df094e915682d3915c279a8430) Thanks [@o-stepper](https://github.com/o-stepper)! - Version constants and version-bearing strings now derive from each package's manifest at build time (`VERSION = pkg.version`; writer ids, client/server info, OTLP framework attributes, build-info metrics interpolate it). No behavioral change at the current version: the rendered strings are byte-identical. A release bump no longer edits source; the new `check-version-consistency` gate fails any reintroduced hardcoded framework version.
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`826ee59`](https://github.com/o-stepper/graphorin/commit/826ee5933ad38693b0dd2f20a110abfecba7d23d), [`436d6ca`](https://github.com/o-stepper/graphorin/commit/436d6ca5ebbd16df094e915682d3915c279a8430)]:
|
|
10
|
+
- @graphorin/observability@0.6.1
|
|
11
|
+
- @graphorin/core@0.6.1
|
|
12
|
+
- @graphorin/security@0.6.1
|
|
13
|
+
- @graphorin/tools@0.6.1
|
|
14
|
+
|
|
3
15
|
## 0.6.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -26,8 +26,15 @@ the existing outbound OAuth subsystem in `@graphorin/security`.
|
|
|
26
26
|
that have not migrated yet (the runtime emits one WARN per
|
|
27
27
|
process on selection).
|
|
28
28
|
- **Typed `MCPClient` surface.** `listTools` / `listResources` /
|
|
29
|
-
`listPrompts` / `callTool` / `readResource` /
|
|
29
|
+
`listPrompts` / `callTool` / `readResource` /
|
|
30
|
+
`readResourceContents` (multi-content resources) / `getPrompt` /
|
|
30
31
|
`close` plus the strategy-aware `toTools(...)` adapter.
|
|
32
|
+
- **Tool pinning (TOFU rug-pull defense).** `toTools({ pinStore })`
|
|
33
|
+
records a fingerprint of each tool's name / description / schema on
|
|
34
|
+
first use and, when a pin store is present, **rejects** silent drift
|
|
35
|
+
by default (`onPinMismatch: 'warn'` downgrades to a warning);
|
|
36
|
+
description-injection heuristics at registration feed the
|
|
37
|
+
`mcp.tool-description.injection-flagged.total` counter.
|
|
31
38
|
- **Strict default for MCP-derived tools.** Every `Tool` produced
|
|
32
39
|
by `MCPClient.toTools()` defaults to the
|
|
33
40
|
`'detect-and-strip-and-wrap'` inbound prompt-injection
|
|
@@ -267,8 +274,8 @@ field where applicable:
|
|
|
267
274
|
|
|
268
275
|
## Acceptance & testing
|
|
269
276
|
|
|
270
|
-
-
|
|
271
|
-
no network calls (verified by the workspace
|
|
277
|
+
- The package's unit + integration + property tests run under Vitest
|
|
278
|
+
with no network calls (verified by the workspace
|
|
272
279
|
`pnpm run check-no-network` script).
|
|
273
280
|
- Integration tests use the SDK's `InMemoryTransport` linked-pair
|
|
274
281
|
+ a configurable in-process `Server` fixture; no child
|
|
@@ -293,4 +300,4 @@ MIT - © 2026 Oleksiy Stepurenko.
|
|
|
293
300
|
|
|
294
301
|
---
|
|
295
302
|
|
|
296
|
-
**Project Graphorin** · v0.6.
|
|
303
|
+
**Project Graphorin** · v0.6.1 · MIT License · © 2026 Oleksiy Stepurenko · <https://github.com/o-stepper/graphorin>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","names":[],"sources":["../../src/client/client.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","names":[],"sources":["../../src/client/client.ts"],"sourcesContent":[],"mappings":";;;;;;;AAuEA;AASA;;;AAAwE,iBATxD,4BAAA,CAAA,CASwD,EAAA,IAAA;;;;;;iBAAlD,eAAA,UAAyB,yBAAyB,QAAQ"}
|
package/dist/client/client.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { version } from "../package.js";
|
|
1
2
|
import { MCPCallTimeoutError, MCPCancelledError, MCPConnectionError, MCPInvalidConfigError, MCPProtocolError, MCPToolNotFoundError, MCPToolPinningError } from "../errors/index.js";
|
|
2
3
|
import { deriveServerIdentity } from "../helpers/identity.js";
|
|
3
4
|
import { validateMCPServerConfig } from "../helpers/validate-config.js";
|
|
@@ -10,7 +11,7 @@ import { ToolListChangedNotificationSchema } from "@modelcontextprotocol/sdk/typ
|
|
|
10
11
|
|
|
11
12
|
//#region src/client/client.ts
|
|
12
13
|
const DEFAULT_CLIENT_NAME = "graphorin-mcp-client";
|
|
13
|
-
const DEFAULT_CLIENT_VERSION =
|
|
14
|
+
const DEFAULT_CLIENT_VERSION = version;
|
|
14
15
|
/**
|
|
15
16
|
* Process-scoped dedup flag for the deprecated SSE transport WARN.
|
|
16
17
|
* Once set, subsequent {@link createMCPClient} calls with the SSE
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["collisionStrategy: CollisionStrategy","tools: SdkTool[]","cursor: string | undefined","result: Awaited<ReturnType<typeof sdkClient.listTools>>","items: SdkResource[]","result: Awaited<ReturnType<typeof sdkClient.listResources>>","items: SdkPrompt[]","result: Awaited<ReturnType<typeof sdkClient.listPrompts>>","result: Awaited<ReturnType<typeof sdkClient.callTool>>","result: Awaited<ReturnType<typeof sdkClient.readResource>>","result: Awaited<ReturnType<typeof sdkClient.getPrompt>>","lastToolFingerprints: ReadonlyMap<string, string> | undefined","recorded: Record<string, string>","clientApi: MCPClient"],"sources":["../../src/client/client.ts"],"sourcesContent":["/**\n * `createMCPClient(...)` - the entry point for opening a typed MCP\n * client connection.\n *\n * The returned {@link MCPClient}:\n *\n * - Wraps the `@modelcontextprotocol/sdk` `Client` instance and the\n * selected SDK transport.\n * - Exposes `listTools` / `listResources` / `listPrompts` /\n * `callTool` / `readResource` / `getPrompt` / `close` plus the\n * strategy-aware `toTools(...)` adapter.\n * - Emits one INFO-log per server when the connected transport is\n * the deprecated SSE transport, on the resolved resumable\n * capability, and when the structured-content + outputSchema\n * round-trip first succeeds.\n *\n * @packageDocumentation\n */\n\nimport type { Tool } from '@graphorin/core';\nimport { incrementCounter } from '@graphorin/tools/audit';\nimport type { CollisionStrategy } from '@graphorin/tools/registry';\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { ToolListChangedNotificationSchema } from '@modelcontextprotocol/sdk/types.js';\nimport {\n MCPCallTimeoutError,\n MCPCancelledError,\n MCPConnectionError,\n MCPInvalidConfigError,\n MCPProtocolError,\n MCPToolNotFoundError,\n MCPToolPinningError,\n} from '../errors/index.js';\nimport { deriveServerIdentity } from '../helpers/identity.js';\nimport { validateMCPServerConfig } from '../helpers/validate-config.js';\nimport type { ServerIdentity } from '../transport/types.js';\nimport { computeClientCapabilities, registerClientRequestHandlers } from './client-handlers.js';\nimport { adaptMCPTools } from './to-tools.js';\nimport { buildTransport, type TransportAuthSource } from './transport-factory.js';\nimport type {\n CreateMCPClientOptions,\n MCPCallToolResult,\n MCPClient,\n MCPContentPart,\n MCPPromptDefinition,\n MCPPromptMessage,\n MCPResourceContent,\n MCPResourceDefinition,\n MCPToolDefinition,\n MCPToToolsOptions,\n} from './types.js';\n\nconst DEFAULT_CLIENT_NAME = 'graphorin-mcp-client';\nconst DEFAULT_CLIENT_VERSION = '0.6.0';\n\n/**\n * Process-scoped dedup flag for the deprecated SSE transport WARN.\n * Once set, subsequent {@link createMCPClient} calls with the SSE\n * transport do not re-emit the warning. Tests reset via\n * {@link _resetSseWarnDedupForTesting}.\n */\nlet sseWarnEmittedThisProcess = false;\n\n/**\n * Reset the SSE WARN dedup flag. Used by tests.\n *\n * @experimental\n */\nexport function _resetSseWarnDedupForTesting(): void {\n sseWarnEmittedThisProcess = false;\n}\n\n/**\n * Open a typed MCP client connection.\n *\n * @stable\n */\nexport async function createMCPClient(options: CreateMCPClientOptions): Promise<MCPClient> {\n validateMCPServerConfig({ transport: options.transport });\n\n // Mutually exclusive (documented on `CreateMCPClientOptions`): a live\n // OAuth provider and a static pre-shared token cannot both drive the\n // outbound `Authorization` header.\n if (options.authProvider !== undefined && options.bearerToken !== undefined) {\n throw new MCPInvalidConfigError(\n '`authProvider` and `bearerToken` are mutually exclusive; supply at most one.',\n { metadata: { transport: options.transport.kind } },\n );\n }\n const auth = resolveTransportAuth(options);\n if (auth !== undefined && options.transport.kind === 'stdio') {\n throw new MCPInvalidConfigError(\n 'authProvider / bearerToken require an HTTP transport (streamable-http or sse); the stdio transport carries no Authorization header.',\n { metadata: { transport: 'stdio' } },\n );\n }\n\n if (\n options.transport.kind === 'sse' &&\n options.suppressDeprecatedTransportWarning !== true &&\n !sseWarnEmittedThisProcess\n ) {\n sseWarnEmittedThisProcess = true;\n if (options.logger !== undefined) {\n options.logger(\n 'warn',\n 'MCP SSE transport is deprecated; migrate the server to the streamable-http transport when possible.',\n );\n }\n incrementCounter('mcp.transport.deprecated.warn.total', { transport: 'sse' });\n }\n\n const built = buildTransport(options.transport, auth === undefined ? undefined : { auth });\n return createMCPClientFromSdkTransport({\n transport: built.transport,\n transportConfig: options.transport,\n ...(options.collisionStrategy === undefined\n ? {}\n : { collisionStrategy: options.collisionStrategy }),\n ...(options.priority === undefined ? {} : { priority: options.priority }),\n ...(options.serverInfoName === undefined ? {} : { serverInfoName: options.serverInfoName }),\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n ...(options.clientName === undefined ? {} : { clientName: options.clientName }),\n ...(options.clientVersion === undefined ? {} : { clientVersion: options.clientVersion }),\n ...(options.elicitation === undefined ? {} : { elicitation: options.elicitation }),\n ...(options.sampling === undefined ? {} : { sampling: options.sampling }),\n ...(options.onTransportClose === undefined\n ? {}\n : { onTransportClose: options.onTransportClose }),\n ...(options.onTransportError === undefined\n ? {}\n : { onTransportError: options.onTransportError }),\n });\n}\n\n/**\n * Internal factory that takes a pre-built SDK `Transport`. Exposed\n * for the test seam - production code uses {@link createMCPClient}.\n *\n * @internal\n */\nexport interface CreateMCPClientFromSdkTransportOptions {\n readonly transport: Transport;\n readonly transportConfig: import('../transport/types.js').MCPTransportConfig;\n readonly collisionStrategy?: CollisionStrategy;\n readonly priority?: number;\n readonly serverInfoName?: string;\n readonly logger?: CreateMCPClientOptions['logger'];\n readonly clientName?: string;\n readonly clientVersion?: string;\n readonly elicitation?: CreateMCPClientOptions['elicitation'];\n readonly sampling?: CreateMCPClientOptions['sampling'];\n readonly onTransportClose?: CreateMCPClientOptions['onTransportClose'];\n readonly onTransportError?: CreateMCPClientOptions['onTransportError'];\n}\n\n/**\n * Build an {@link MCPClient} from a pre-built SDK transport. The\n * production {@link createMCPClient} delegates here after building\n * the SDK transport from a {@link MCPTransportConfig}.\n *\n * @internal\n */\nexport async function createMCPClientFromSdkTransport(\n options: CreateMCPClientFromSdkTransportOptions,\n): Promise<MCPClient> {\n const sdkClient = new Client({\n name: options.clientName ?? DEFAULT_CLIENT_NAME,\n version: options.clientVersion ?? DEFAULT_CLIENT_VERSION,\n });\n\n // WI-13 (P2-2): advertise + register the server-initiated request\n // handlers (elicitation / sampling) before connecting, so they are in\n // place when the session starts. Both are gated - capabilities are\n // advertised only when the operator supplied the matching callback.\n const clientCapabilities = computeClientCapabilities({\n elicitation: options.elicitation,\n sampling: options.sampling,\n });\n if (clientCapabilities !== undefined) {\n sdkClient.registerCapabilities(clientCapabilities);\n }\n const serverIdRef = { current: 'unknown' };\n registerClientRequestHandlers(sdkClient, {\n ...(options.elicitation === undefined ? {} : { elicitation: options.elicitation }),\n ...(options.sampling === undefined ? {} : { sampling: options.sampling }),\n serverIdRef,\n });\n // MC-6: surface server-side catalogue churn - at minimum an audit\n // counter + log line; operators re-run toTools() to refresh and\n // re-sanitize the catalogue (which also re-runs the drift diff).\n sdkClient.setNotificationHandler(ToolListChangedNotificationSchema, async () => {\n incrementCounter('mcp.tools.list-changed.total', {\n server: serverIdRef.current ?? 'unknown',\n });\n options.logger?.(\n 'warn',\n 'mcp.tools.list_changed received - re-run toTools() to refresh + re-sanitize the catalogue',\n { server: serverIdRef.current },\n );\n });\n\n try {\n await sdkClient.connect(options.transport);\n } catch (cause) {\n throw new MCPConnectionError(\n `MCP transport could not be established: ${(cause as Error).message ?? String(cause)}`,\n {\n metadata: { transport: options.transportConfig.kind },\n cause,\n },\n );\n }\n\n const serverInfo = sdkClient.getServerVersion() ?? {\n name: 'unknown',\n version: '0.0.0',\n };\n const serverIdentity = deriveServerIdentity(\n options.transportConfig,\n options.serverInfoName ?? serverInfo.name,\n );\n // Backfill the server id so the client-side request handlers\n // (registered before connect) label their counters with it.\n serverIdRef.current = serverIdentity.id;\n // mcp-skills-10: surface transport lifecycle. Without these a dead\n // stdio child / dropped HTTP session is observable only as\n // MCPProtocolErrors on subsequent calls. The SDK Protocol base\n // exposes assignable onclose/onerror callbacks (the transport's own\n // handlers are managed by the SDK - never overwrite those).\n sdkClient.onclose = () => {\n incrementCounter('mcp.transport.closed.total', { server: serverIdentity.id });\n options.logger?.('warn', 'MCP transport closed - rebuild the client to reconnect', {\n server: serverIdentity.id,\n });\n options.onTransportClose?.({ server: serverIdentity.id });\n };\n sdkClient.onerror = (error: Error) => {\n incrementCounter('mcp.transport.error.total', { server: serverIdentity.id });\n options.onTransportError?.(error, { server: serverIdentity.id });\n };\n const collisionStrategy: CollisionStrategy = options.collisionStrategy ?? 'auto-prefix';\n\n // MC-1: the client-side eventStore option was removed - per the\n // Streamable HTTP spec event replay is the SERVER's responsibility,\n // and the SDK transport auto-reconnects with Last-Event-ID on its\n // own. Warn legacy callers instead of silently ignoring the option.\n if ((options as { readonly eventStore?: unknown }).eventStore !== undefined) {\n options.logger?.(\n 'warn',\n \"the client 'eventStore' option was removed (MC-1): event replay is a server responsibility; the SDK transport auto-reconnects with Last-Event-ID. Remove the option.\",\n { server: serverIdentity.id },\n );\n }\n // MC-9: a session id means stateful routing, NOT a replay guarantee.\n const sessionIdPresent =\n isStreamableHttp(options.transport) &&\n (options.transport as StreamableHTTPClientTransport).sessionId !== undefined;\n const resumable = sessionIdPresent;\n if (options.logger !== undefined) {\n options.logger('info', 'mcp.session.session-id.resolved', {\n server: serverIdentity.id,\n value: sessionIdPresent,\n source: sessionIdPresent ? 'session-id-present' : 'transport-default',\n });\n }\n let structuredContentSeenLogged = false;\n\n /**\n * mcp-skills-02: MCP list operations are cursor-paginated (protocol\n * 2024-11-05+) and the SDK does NOT auto-paginate - a single call\n * returns page 1 only. Ignoring `nextCursor` silently truncated the\n * catalogue: tools beyond the first page never reached `toTools()`,\n * defer-loading thresholds counted a partial catalogue, pin\n * fingerprints covered a partial catalogue, and resources/prompts were\n * under-listed. Every list surface now drains the cursor chain, with a\n * defensive page cap against buggy/adversarial servers that never\n * terminate it.\n */\n const MAX_LIST_PAGES = 100;\n\n function warnListPageCap(what: 'tools' | 'resources' | 'prompts', collected: number): void {\n options.logger?.(\n 'warn',\n `mcp.list.${what}.page-cap-reached: server kept returning nextCursor after ${MAX_LIST_PAGES} pages; the ${what} catalogue is truncated at ${collected} entries.`,\n { server: serverIdentity.id },\n );\n }\n\n async function listTools(opts?: {\n signal?: AbortSignal;\n }): Promise<ReadonlyArray<MCPToolDefinition>> {\n const requestOptions = opts?.signal === undefined ? {} : { signal: opts.signal };\n type SdkTool = {\n name: string;\n description?: string;\n inputSchema: Readonly<Record<string, unknown>>;\n outputSchema?: Readonly<Record<string, unknown>>;\n title?: string;\n };\n const tools: SdkTool[] = [];\n let cursor: string | undefined;\n for (let page = 0; ; page++) {\n if (page >= MAX_LIST_PAGES) {\n warnListPageCap('tools', tools.length);\n break;\n }\n let result: Awaited<ReturnType<typeof sdkClient.listTools>>;\n try {\n result = await sdkClient.listTools(cursor === undefined ? {} : { cursor }, requestOptions);\n } catch (cause) {\n throw mapSdkError(cause, {});\n }\n tools.push(...((result.tools ?? []) as ReadonlyArray<SdkTool>));\n cursor = (result as { nextCursor?: string }).nextCursor;\n if (cursor === undefined) break;\n }\n if (!structuredContentSeenLogged && tools.some((t) => t.outputSchema !== undefined)) {\n structuredContentSeenLogged = true;\n if (options.logger !== undefined) {\n options.logger('info', 'mcp.server.structured-content.detected', {\n server: serverIdentity.id,\n });\n }\n }\n return Object.freeze(\n tools.map((t) =>\n Object.freeze({\n name: t.name,\n description: t.description ?? '',\n inputSchema: t.inputSchema,\n ...(t.outputSchema === undefined ? {} : { outputSchema: t.outputSchema }),\n ...(t.title === undefined ? {} : { title: t.title }),\n }),\n ),\n );\n }\n\n async function listResources(opts?: {\n signal?: AbortSignal;\n }): Promise<ReadonlyArray<MCPResourceDefinition>> {\n const requestOptions = opts?.signal === undefined ? {} : { signal: opts.signal };\n type SdkResource = {\n uri: string;\n name?: string;\n description?: string;\n mimeType?: string;\n };\n const items: SdkResource[] = [];\n let cursor: string | undefined;\n for (let page = 0; ; page++) {\n if (page >= MAX_LIST_PAGES) {\n warnListPageCap('resources', items.length);\n break;\n }\n let result: Awaited<ReturnType<typeof sdkClient.listResources>>;\n try {\n result = await sdkClient.listResources(\n cursor === undefined ? {} : { cursor },\n requestOptions,\n );\n } catch (cause) {\n throw mapSdkError(cause, {});\n }\n items.push(...((result.resources ?? []) as ReadonlyArray<SdkResource>));\n cursor = (result as { nextCursor?: string }).nextCursor;\n if (cursor === undefined) break;\n }\n return Object.freeze(\n items.map((r) =>\n Object.freeze({\n uri: r.uri,\n ...(r.name === undefined ? {} : { name: r.name }),\n ...(r.description === undefined ? {} : { description: r.description }),\n ...(r.mimeType === undefined ? {} : { mimeType: r.mimeType }),\n }),\n ),\n );\n }\n\n async function listPrompts(opts?: {\n signal?: AbortSignal;\n }): Promise<ReadonlyArray<MCPPromptDefinition>> {\n const requestOptions = opts?.signal === undefined ? {} : { signal: opts.signal };\n type SdkPrompt = {\n name: string;\n description?: string;\n arguments?: ReadonlyArray<{ name: string; description?: string; required?: boolean }>;\n };\n const items: SdkPrompt[] = [];\n let cursor: string | undefined;\n for (let page = 0; ; page++) {\n if (page >= MAX_LIST_PAGES) {\n warnListPageCap('prompts', items.length);\n break;\n }\n let result: Awaited<ReturnType<typeof sdkClient.listPrompts>>;\n try {\n result = await sdkClient.listPrompts(\n cursor === undefined ? {} : { cursor },\n requestOptions,\n );\n } catch (cause) {\n throw mapSdkError(cause, {});\n }\n items.push(...((result.prompts ?? []) as ReadonlyArray<SdkPrompt>));\n cursor = (result as { nextCursor?: string }).nextCursor;\n if (cursor === undefined) break;\n }\n return Object.freeze(\n items.map((p) =>\n Object.freeze({\n name: p.name,\n ...(p.description === undefined ? {} : { description: p.description }),\n ...(p.arguments === undefined\n ? {}\n : {\n arguments: Object.freeze(\n p.arguments.map((a) =>\n Object.freeze({\n name: a.name,\n ...(a.description === undefined ? {} : { description: a.description }),\n ...(a.required === undefined ? {} : { required: a.required }),\n }),\n ),\n ),\n }),\n }),\n ),\n );\n }\n\n async function callTool(\n name: string,\n args: unknown,\n opts?: { signal?: AbortSignal; timeoutMs?: number },\n ): Promise<MCPCallToolResult> {\n // MC-3: `timeoutMs` maps onto the SDK's RequestOptions - both the\n // per-attempt and the total ceiling, so progress notifications\n // cannot extend past the caller's budget.\n const requestOptions = {\n ...(opts?.signal === undefined ? {} : { signal: opts.signal }),\n ...(opts?.timeoutMs === undefined\n ? {}\n : { timeout: opts.timeoutMs, maxTotalTimeout: opts.timeoutMs }),\n };\n incrementCounter('mcp.call.invoked.total', { server: serverIdentity.id, tool: name });\n let result: Awaited<ReturnType<typeof sdkClient.callTool>>;\n try {\n result = await sdkClient.callTool(\n { name, arguments: args as Record<string, unknown> },\n undefined,\n requestOptions,\n );\n } catch (cause) {\n const mapped = mapSdkError(cause, { tool: name });\n if (mapped instanceof MCPCancelledError) {\n incrementCounter('mcp.call.cancelled.total', { server: serverIdentity.id, tool: name });\n } else {\n incrementCounter('mcp.call.failed.total', { server: serverIdentity.id, tool: name });\n }\n throw mapped;\n }\n const content = (result.content ?? []) as ReadonlyArray<MCPContentPart>;\n return Object.freeze({\n content: Object.freeze([...content]),\n ...(result.structuredContent === undefined\n ? {}\n : { structuredContent: result.structuredContent as Readonly<Record<string, unknown>> }),\n ...(result.isError === undefined ? {} : { isError: Boolean(result.isError) }),\n });\n }\n\n async function readResourceContents(\n uri: string,\n opts?: { signal?: AbortSignal },\n ): Promise<ReadonlyArray<MCPResourceContent>> {\n const requestOptions = opts?.signal === undefined ? {} : { signal: opts.signal };\n let result: Awaited<ReturnType<typeof sdkClient.readResource>>;\n try {\n result = await sdkClient.readResource({ uri }, requestOptions);\n } catch (cause) {\n throw mapSdkError(cause, {});\n }\n const contents = (result.contents ?? []) as ReadonlyArray<MCPResourceContent>;\n if (contents.length === 0) {\n throw new MCPProtocolError(`MCP server returned no contents for resource '${uri}'.`, {\n metadata: { server: serverIdentity.id },\n });\n }\n return Object.freeze(contents.map((c) => Object.freeze(c)));\n }\n\n async function readResource(\n uri: string,\n opts?: { signal?: AbortSignal },\n ): Promise<MCPResourceContent> {\n const contents = await readResourceContents(uri, opts);\n // mcp-skills-11: the `resources/read` result is an ARRAY precisely\n // because one URI can yield multiple contents. The single-content\n // convenience keeps its shape, but a silent truncation is now a\n // visible one.\n if (contents.length > 1) {\n incrementCounter('mcp.resource.multi-content-truncated.total', {\n server: serverIdentity.id,\n });\n options.logger?.(\n 'warn',\n `resource '${uri}' returned ${contents.length} content items; readResource() surfaces only the first - use readResourceContents() for all of them`,\n { server: serverIdentity.id },\n );\n }\n return contents[0] as MCPResourceContent;\n }\n\n async function getPrompt(\n name: string,\n args?: unknown,\n opts?: { signal?: AbortSignal },\n ): Promise<{ readonly messages: ReadonlyArray<MCPPromptMessage> }> {\n const requestOptions = opts?.signal === undefined ? {} : { signal: opts.signal };\n let result: Awaited<ReturnType<typeof sdkClient.getPrompt>>;\n try {\n result = await sdkClient.getPrompt(\n {\n name,\n ...(args === undefined ? {} : { arguments: args as Record<string, string> }),\n },\n requestOptions,\n );\n } catch (cause) {\n throw mapSdkError(cause, {});\n }\n const messages = (result.messages ?? []).map(\n (m): MCPPromptMessage =>\n Object.freeze({\n role: m.role,\n content: m.content as MCPContentPart,\n }),\n );\n return Object.freeze({ messages: Object.freeze(messages) });\n }\n\n let lastToolFingerprints: ReadonlyMap<string, string> | undefined;\n\n async function toTools(toolsOpts?: MCPToToolsOptions): Promise<ReadonlyArray<Tool>> {\n const catalogue = await listTools();\n const adapted = adaptMCPTools({\n client: clientApi,\n serverIdentity,\n catalogue,\n ...(toolsOpts === undefined ? {} : { options: toolsOpts }),\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n });\n // MC-6: cross-snapshot drift - a definition changing behind an\n // already-seen name within this client's lifetime is audited.\n if (lastToolFingerprints !== undefined) {\n for (const [name, hash] of adapted.fingerprints) {\n const previous = lastToolFingerprints.get(name);\n if (previous !== undefined && previous !== hash) {\n incrementCounter('mcp.tools.changed.total', {\n server: serverIdentity.id,\n tool: name,\n });\n options.logger?.('warn', 'mcp.tools.changed: definition drifted between snapshots', {\n server: serverIdentity.id,\n tool: name,\n previous,\n current: hash,\n });\n }\n }\n }\n lastToolFingerprints = adapted.fingerprints;\n // MC-6: operator pins from a previously approved snapshot - the\n // rug-pull (approve-then-swap across restarts) posture. C6 extends it\n // with durable trust-on-first-use via `pinStore`: the first snapshot\n // is RECORDED, later snapshots are COMPARED, and a store-backed\n // mismatch defaults to 'reject' (a persisted first approval is an\n // explicit trust decision).\n let pins = toolsOpts?.pinnedFingerprints;\n let mismatchAction = toolsOpts?.onPinMismatch ?? 'warn';\n const pinStore = toolsOpts?.pinStore;\n if (pins === undefined && pinStore !== undefined) {\n const stored = await pinStore.get(serverIdentity.id);\n if (stored === undefined) {\n const recorded: Record<string, string> = {};\n for (const [name, hash] of adapted.fingerprints) recorded[name] = hash;\n await pinStore.set(serverIdentity.id, recorded);\n incrementCounter('mcp.tools.pins-recorded.total', { server: serverIdentity.id });\n options.logger?.('info', 'mcp.tools.pins-recorded: first-use fingerprints stored', {\n server: serverIdentity.id,\n tools: Object.keys(recorded).length,\n });\n } else {\n pins = stored;\n mismatchAction = toolsOpts?.onPinMismatch ?? 'reject';\n }\n }\n if (pins !== undefined) {\n for (const [name, pinned] of Object.entries(pins)) {\n const current = adapted.fingerprints.get(name);\n if (current !== undefined && current !== pinned) {\n if (mismatchAction === 'reject') {\n throw new MCPToolPinningError(\n `MCP tool '${name}' no longer matches its pinned definition fingerprint - the server changed the definition behind an approved name.`,\n { metadata: { server: serverIdentity.id, tool: name } },\n );\n }\n incrementCounter('mcp.tools.pin-mismatch.total', {\n server: serverIdentity.id,\n tool: name,\n });\n options.logger?.('warn', 'mcp.tools.pin-mismatch: pinned fingerprint diverged', {\n server: serverIdentity.id,\n tool: name,\n });\n }\n }\n }\n return adapted.tools;\n }\n\n async function close(): Promise<void> {\n try {\n await sdkClient.close();\n } catch (cause) {\n // Treat double-close as a no-op; surface other failures.\n if (cause instanceof Error && cause.message.toLowerCase().includes('already')) return;\n throw new MCPConnectionError('MCP transport could not be closed cleanly.', {\n metadata: { transport: options.transportConfig.kind, server: serverIdentity.id },\n cause,\n });\n }\n }\n\n const clientApi: MCPClient = Object.freeze({\n id: serverIdentity.id,\n serverInfo,\n serverIdentity,\n collisionStrategy,\n ...(options.priority === undefined ? {} : { priority: options.priority }),\n sessionIdPresent,\n resumable,\n listTools,\n listResources,\n listPrompts,\n callTool,\n readResource,\n readResourceContents,\n getPrompt,\n toTools,\n close,\n });\n return clientApi;\n}\n\n/**\n * Resolve the live {@link TransportAuthSource} for the outbound\n * `Authorization` header from the mutually-exclusive `authProvider` /\n * `bearerToken` options. `authProvider.resolveHeader()` already returns\n * the full header value (`Bearer …`); a static `bearerToken` is wrapped\n * into a constant `Bearer`-prefixed resolver. Returns `undefined` when\n * neither is supplied (no header injection).\n */\nfunction resolveTransportAuth(options: CreateMCPClientOptions): TransportAuthSource | undefined {\n const provider = options.authProvider;\n if (provider !== undefined) {\n return { resolveHeader: () => provider.resolveHeader() };\n }\n if (options.bearerToken !== undefined) {\n const token = options.bearerToken;\n const header = /^bearer\\s/i.test(token) ? token : `Bearer ${token}`;\n return { resolveHeader: () => header };\n }\n return undefined;\n}\n\nfunction mapSdkError(cause: unknown, ctx: { readonly tool?: string }): Error {\n const metadata = ctx.tool === undefined ? {} : { tool: ctx.tool };\n if (cause instanceof Error) {\n const name = cause.name;\n const message = cause.message ?? '';\n if (name === 'AbortError' || /aborted|cancell/i.test(message)) {\n return new MCPCancelledError('MCP request was cancelled.', { metadata, cause });\n }\n // MC-3: the SDK reports request-timeout as a plain McpError - map it\n // onto the advertised typed class instead of MCPProtocolError.\n if (/request timed out|timed out/i.test(message)) {\n return new MCPCallTimeoutError(\n `MCP request timed out${ctx.tool ? ` (tool: ${ctx.tool})` : ''}.`,\n {\n metadata,\n cause,\n },\n );\n }\n if (/unknown\\s+tool|tool\\s+not\\s+found|method\\s+not\\s+found/i.test(message)) {\n return new MCPToolNotFoundError(`MCP tool not found${ctx.tool ? `: ${ctx.tool}` : ''}.`, {\n metadata,\n cause,\n });\n }\n return new MCPProtocolError(message.length === 0 ? cause.toString() : message, {\n metadata,\n cause,\n });\n }\n return new MCPProtocolError(`MCP request failed: ${String(cause)}`, { metadata, cause });\n}\n\nfunction isStreamableHttp(transport: unknown): transport is StreamableHTTPClientTransport {\n return (\n typeof transport === 'object' &&\n transport !== null &&\n transport.constructor !== undefined &&\n transport.constructor.name === 'StreamableHTTPClientTransport'\n );\n}\n\nexport type { ServerIdentity };\n"],"mappings":";;;;;;;;;;;AAsDA,MAAM,sBAAsB;AAC5B,MAAM,yBAAyB;;;;;;;AAQ/B,IAAI,4BAA4B;;;;;;AAOhC,SAAgB,+BAAqC;AACnD,6BAA4B;;;;;;;AAQ9B,eAAsB,gBAAgB,SAAqD;AACzF,yBAAwB,EAAE,WAAW,QAAQ,WAAW,CAAC;AAKzD,KAAI,QAAQ,iBAAiB,UAAa,QAAQ,gBAAgB,OAChE,OAAM,IAAI,sBACR,gFACA,EAAE,UAAU,EAAE,WAAW,QAAQ,UAAU,MAAM,EAAE,CACpD;CAEH,MAAM,OAAO,qBAAqB,QAAQ;AAC1C,KAAI,SAAS,UAAa,QAAQ,UAAU,SAAS,QACnD,OAAM,IAAI,sBACR,uIACA,EAAE,UAAU,EAAE,WAAW,SAAS,EAAE,CACrC;AAGH,KACE,QAAQ,UAAU,SAAS,SAC3B,QAAQ,uCAAuC,QAC/C,CAAC,2BACD;AACA,8BAA4B;AAC5B,MAAI,QAAQ,WAAW,OACrB,SAAQ,OACN,QACA,sGACD;AAEH,mBAAiB,uCAAuC,EAAE,WAAW,OAAO,CAAC;;AAI/E,QAAO,gCAAgC;EACrC,WAFY,eAAe,QAAQ,WAAW,SAAS,SAAY,SAAY,EAAE,MAAM,CAAC,CAEvE;EACjB,iBAAiB,QAAQ;EACzB,GAAI,QAAQ,sBAAsB,SAC9B,EAAE,GACF,EAAE,mBAAmB,QAAQ,mBAAmB;EACpD,GAAI,QAAQ,aAAa,SAAY,EAAE,GAAG,EAAE,UAAU,QAAQ,UAAU;EACxE,GAAI,QAAQ,mBAAmB,SAAY,EAAE,GAAG,EAAE,gBAAgB,QAAQ,gBAAgB;EAC1F,GAAI,QAAQ,WAAW,SAAY,EAAE,GAAG,EAAE,QAAQ,QAAQ,QAAQ;EAClE,GAAI,QAAQ,eAAe,SAAY,EAAE,GAAG,EAAE,YAAY,QAAQ,YAAY;EAC9E,GAAI,QAAQ,kBAAkB,SAAY,EAAE,GAAG,EAAE,eAAe,QAAQ,eAAe;EACvF,GAAI,QAAQ,gBAAgB,SAAY,EAAE,GAAG,EAAE,aAAa,QAAQ,aAAa;EACjF,GAAI,QAAQ,aAAa,SAAY,EAAE,GAAG,EAAE,UAAU,QAAQ,UAAU;EACxE,GAAI,QAAQ,qBAAqB,SAC7B,EAAE,GACF,EAAE,kBAAkB,QAAQ,kBAAkB;EAClD,GAAI,QAAQ,qBAAqB,SAC7B,EAAE,GACF,EAAE,kBAAkB,QAAQ,kBAAkB;EACnD,CAAC;;;;;;;;;AA+BJ,eAAsB,gCACpB,SACoB;CACpB,MAAM,YAAY,IAAI,OAAO;EAC3B,MAAM,QAAQ,cAAc;EAC5B,SAAS,QAAQ,iBAAiB;EACnC,CAAC;CAMF,MAAM,qBAAqB,0BAA0B;EACnD,aAAa,QAAQ;EACrB,UAAU,QAAQ;EACnB,CAAC;AACF,KAAI,uBAAuB,OACzB,WAAU,qBAAqB,mBAAmB;CAEpD,MAAM,cAAc,EAAE,SAAS,WAAW;AAC1C,+BAA8B,WAAW;EACvC,GAAI,QAAQ,gBAAgB,SAAY,EAAE,GAAG,EAAE,aAAa,QAAQ,aAAa;EACjF,GAAI,QAAQ,aAAa,SAAY,EAAE,GAAG,EAAE,UAAU,QAAQ,UAAU;EACxE;EACD,CAAC;AAIF,WAAU,uBAAuB,mCAAmC,YAAY;AAC9E,mBAAiB,gCAAgC,EAC/C,QAAQ,YAAY,WAAW,WAChC,CAAC;AACF,UAAQ,SACN,QACA,6FACA,EAAE,QAAQ,YAAY,SAAS,CAChC;GACD;AAEF,KAAI;AACF,QAAM,UAAU,QAAQ,QAAQ,UAAU;UACnC,OAAO;AACd,QAAM,IAAI,mBACR,2CAA4C,MAAgB,WAAW,OAAO,MAAM,IACpF;GACE,UAAU,EAAE,WAAW,QAAQ,gBAAgB,MAAM;GACrD;GACD,CACF;;CAGH,MAAM,aAAa,UAAU,kBAAkB,IAAI;EACjD,MAAM;EACN,SAAS;EACV;CACD,MAAM,iBAAiB,qBACrB,QAAQ,iBACR,QAAQ,kBAAkB,WAAW,KACtC;AAGD,aAAY,UAAU,eAAe;AAMrC,WAAU,gBAAgB;AACxB,mBAAiB,8BAA8B,EAAE,QAAQ,eAAe,IAAI,CAAC;AAC7E,UAAQ,SAAS,QAAQ,0DAA0D,EACjF,QAAQ,eAAe,IACxB,CAAC;AACF,UAAQ,mBAAmB,EAAE,QAAQ,eAAe,IAAI,CAAC;;AAE3D,WAAU,WAAW,UAAiB;AACpC,mBAAiB,6BAA6B,EAAE,QAAQ,eAAe,IAAI,CAAC;AAC5E,UAAQ,mBAAmB,OAAO,EAAE,QAAQ,eAAe,IAAI,CAAC;;CAElE,MAAMA,oBAAuC,QAAQ,qBAAqB;AAM1E,KAAK,QAA8C,eAAe,OAChE,SAAQ,SACN,QACA,wKACA,EAAE,QAAQ,eAAe,IAAI,CAC9B;CAGH,MAAM,mBACJ,iBAAiB,QAAQ,UAAU,IAClC,QAAQ,UAA4C,cAAc;CACrE,MAAM,YAAY;AAClB,KAAI,QAAQ,WAAW,OACrB,SAAQ,OAAO,QAAQ,mCAAmC;EACxD,QAAQ,eAAe;EACvB,OAAO;EACP,QAAQ,mBAAmB,uBAAuB;EACnD,CAAC;CAEJ,IAAI,8BAA8B;;;;;;;;;;;;CAalC,MAAM,iBAAiB;CAEvB,SAAS,gBAAgB,MAAyC,WAAyB;AACzF,UAAQ,SACN,QACA,YAAY,KAAK,4DAA4D,eAAe,cAAc,KAAK,6BAA6B,UAAU,YACtJ,EAAE,QAAQ,eAAe,IAAI,CAC9B;;CAGH,eAAe,UAAU,MAEqB;EAC5C,MAAM,iBAAiB,MAAM,WAAW,SAAY,EAAE,GAAG,EAAE,QAAQ,KAAK,QAAQ;EAQhF,MAAMC,QAAmB,EAAE;EAC3B,IAAIC;AACJ,OAAK,IAAI,OAAO,IAAK,QAAQ;AAC3B,OAAI,QAAQ,gBAAgB;AAC1B,oBAAgB,SAAS,MAAM,OAAO;AACtC;;GAEF,IAAIC;AACJ,OAAI;AACF,aAAS,MAAM,UAAU,UAAU,WAAW,SAAY,EAAE,GAAG,EAAE,QAAQ,EAAE,eAAe;YACnF,OAAO;AACd,UAAM,YAAY,OAAO,EAAE,CAAC;;AAE9B,SAAM,KAAK,GAAK,OAAO,SAAS,EAAE,CAA6B;AAC/D,YAAU,OAAmC;AAC7C,OAAI,WAAW,OAAW;;AAE5B,MAAI,CAAC,+BAA+B,MAAM,MAAM,MAAM,EAAE,iBAAiB,OAAU,EAAE;AACnF,iCAA8B;AAC9B,OAAI,QAAQ,WAAW,OACrB,SAAQ,OAAO,QAAQ,0CAA0C,EAC/D,QAAQ,eAAe,IACxB,CAAC;;AAGN,SAAO,OAAO,OACZ,MAAM,KAAK,MACT,OAAO,OAAO;GACZ,MAAM,EAAE;GACR,aAAa,EAAE,eAAe;GAC9B,aAAa,EAAE;GACf,GAAI,EAAE,iBAAiB,SAAY,EAAE,GAAG,EAAE,cAAc,EAAE,cAAc;GACxE,GAAI,EAAE,UAAU,SAAY,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO;GACpD,CAAC,CACH,CACF;;CAGH,eAAe,cAAc,MAEqB;EAChD,MAAM,iBAAiB,MAAM,WAAW,SAAY,EAAE,GAAG,EAAE,QAAQ,KAAK,QAAQ;EAOhF,MAAMC,QAAuB,EAAE;EAC/B,IAAIF;AACJ,OAAK,IAAI,OAAO,IAAK,QAAQ;AAC3B,OAAI,QAAQ,gBAAgB;AAC1B,oBAAgB,aAAa,MAAM,OAAO;AAC1C;;GAEF,IAAIG;AACJ,OAAI;AACF,aAAS,MAAM,UAAU,cACvB,WAAW,SAAY,EAAE,GAAG,EAAE,QAAQ,EACtC,eACD;YACM,OAAO;AACd,UAAM,YAAY,OAAO,EAAE,CAAC;;AAE9B,SAAM,KAAK,GAAK,OAAO,aAAa,EAAE,CAAiC;AACvE,YAAU,OAAmC;AAC7C,OAAI,WAAW,OAAW;;AAE5B,SAAO,OAAO,OACZ,MAAM,KAAK,MACT,OAAO,OAAO;GACZ,KAAK,EAAE;GACP,GAAI,EAAE,SAAS,SAAY,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM;GAChD,GAAI,EAAE,gBAAgB,SAAY,EAAE,GAAG,EAAE,aAAa,EAAE,aAAa;GACrE,GAAI,EAAE,aAAa,SAAY,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU;GAC7D,CAAC,CACH,CACF;;CAGH,eAAe,YAAY,MAEqB;EAC9C,MAAM,iBAAiB,MAAM,WAAW,SAAY,EAAE,GAAG,EAAE,QAAQ,KAAK,QAAQ;EAMhF,MAAMC,QAAqB,EAAE;EAC7B,IAAIJ;AACJ,OAAK,IAAI,OAAO,IAAK,QAAQ;AAC3B,OAAI,QAAQ,gBAAgB;AAC1B,oBAAgB,WAAW,MAAM,OAAO;AACxC;;GAEF,IAAIK;AACJ,OAAI;AACF,aAAS,MAAM,UAAU,YACvB,WAAW,SAAY,EAAE,GAAG,EAAE,QAAQ,EACtC,eACD;YACM,OAAO;AACd,UAAM,YAAY,OAAO,EAAE,CAAC;;AAE9B,SAAM,KAAK,GAAK,OAAO,WAAW,EAAE,CAA+B;AACnE,YAAU,OAAmC;AAC7C,OAAI,WAAW,OAAW;;AAE5B,SAAO,OAAO,OACZ,MAAM,KAAK,MACT,OAAO,OAAO;GACZ,MAAM,EAAE;GACR,GAAI,EAAE,gBAAgB,SAAY,EAAE,GAAG,EAAE,aAAa,EAAE,aAAa;GACrE,GAAI,EAAE,cAAc,SAChB,EAAE,GACF,EACE,WAAW,OAAO,OAChB,EAAE,UAAU,KAAK,MACf,OAAO,OAAO;IACZ,MAAM,EAAE;IACR,GAAI,EAAE,gBAAgB,SAAY,EAAE,GAAG,EAAE,aAAa,EAAE,aAAa;IACrE,GAAI,EAAE,aAAa,SAAY,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU;IAC7D,CAAC,CACH,CACF,EACF;GACN,CAAC,CACH,CACF;;CAGH,eAAe,SACb,MACA,MACA,MAC4B;EAI5B,MAAM,iBAAiB;GACrB,GAAI,MAAM,WAAW,SAAY,EAAE,GAAG,EAAE,QAAQ,KAAK,QAAQ;GAC7D,GAAI,MAAM,cAAc,SACpB,EAAE,GACF;IAAE,SAAS,KAAK;IAAW,iBAAiB,KAAK;IAAW;GACjE;AACD,mBAAiB,0BAA0B;GAAE,QAAQ,eAAe;GAAI,MAAM;GAAM,CAAC;EACrF,IAAIC;AACJ,MAAI;AACF,YAAS,MAAM,UAAU,SACvB;IAAE;IAAM,WAAW;IAAiC,EACpD,QACA,eACD;WACM,OAAO;GACd,MAAM,SAAS,YAAY,OAAO,EAAE,MAAM,MAAM,CAAC;AACjD,OAAI,kBAAkB,kBACpB,kBAAiB,4BAA4B;IAAE,QAAQ,eAAe;IAAI,MAAM;IAAM,CAAC;OAEvF,kBAAiB,yBAAyB;IAAE,QAAQ,eAAe;IAAI,MAAM;IAAM,CAAC;AAEtF,SAAM;;EAER,MAAM,UAAW,OAAO,WAAW,EAAE;AACrC,SAAO,OAAO,OAAO;GACnB,SAAS,OAAO,OAAO,CAAC,GAAG,QAAQ,CAAC;GACpC,GAAI,OAAO,sBAAsB,SAC7B,EAAE,GACF,EAAE,mBAAmB,OAAO,mBAAwD;GACxF,GAAI,OAAO,YAAY,SAAY,EAAE,GAAG,EAAE,SAAS,QAAQ,OAAO,QAAQ,EAAE;GAC7E,CAAC;;CAGJ,eAAe,qBACb,KACA,MAC4C;EAC5C,MAAM,iBAAiB,MAAM,WAAW,SAAY,EAAE,GAAG,EAAE,QAAQ,KAAK,QAAQ;EAChF,IAAIC;AACJ,MAAI;AACF,YAAS,MAAM,UAAU,aAAa,EAAE,KAAK,EAAE,eAAe;WACvD,OAAO;AACd,SAAM,YAAY,OAAO,EAAE,CAAC;;EAE9B,MAAM,WAAY,OAAO,YAAY,EAAE;AACvC,MAAI,SAAS,WAAW,EACtB,OAAM,IAAI,iBAAiB,iDAAiD,IAAI,KAAK,EACnF,UAAU,EAAE,QAAQ,eAAe,IAAI,EACxC,CAAC;AAEJ,SAAO,OAAO,OAAO,SAAS,KAAK,MAAM,OAAO,OAAO,EAAE,CAAC,CAAC;;CAG7D,eAAe,aACb,KACA,MAC6B;EAC7B,MAAM,WAAW,MAAM,qBAAqB,KAAK,KAAK;AAKtD,MAAI,SAAS,SAAS,GAAG;AACvB,oBAAiB,8CAA8C,EAC7D,QAAQ,eAAe,IACxB,CAAC;AACF,WAAQ,SACN,QACA,aAAa,IAAI,aAAa,SAAS,OAAO,sGAC9C,EAAE,QAAQ,eAAe,IAAI,CAC9B;;AAEH,SAAO,SAAS;;CAGlB,eAAe,UACb,MACA,MACA,MACiE;EACjE,MAAM,iBAAiB,MAAM,WAAW,SAAY,EAAE,GAAG,EAAE,QAAQ,KAAK,QAAQ;EAChF,IAAIC;AACJ,MAAI;AACF,YAAS,MAAM,UAAU,UACvB;IACE;IACA,GAAI,SAAS,SAAY,EAAE,GAAG,EAAE,WAAW,MAAgC;IAC5E,EACD,eACD;WACM,OAAO;AACd,SAAM,YAAY,OAAO,EAAE,CAAC;;EAE9B,MAAM,YAAY,OAAO,YAAY,EAAE,EAAE,KACtC,MACC,OAAO,OAAO;GACZ,MAAM,EAAE;GACR,SAAS,EAAE;GACZ,CAAC,CACL;AACD,SAAO,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,SAAS,EAAE,CAAC;;CAG7D,IAAIC;CAEJ,eAAe,QAAQ,WAA6D;EAElF,MAAM,UAAU,cAAc;GAC5B,QAAQ;GACR;GACA,WAJgB,MAAM,WAAW;GAKjC,GAAI,cAAc,SAAY,EAAE,GAAG,EAAE,SAAS,WAAW;GACzD,GAAI,QAAQ,WAAW,SAAY,EAAE,GAAG,EAAE,QAAQ,QAAQ,QAAQ;GACnE,CAAC;AAGF,MAAI,yBAAyB,OAC3B,MAAK,MAAM,CAAC,MAAM,SAAS,QAAQ,cAAc;GAC/C,MAAM,WAAW,qBAAqB,IAAI,KAAK;AAC/C,OAAI,aAAa,UAAa,aAAa,MAAM;AAC/C,qBAAiB,2BAA2B;KAC1C,QAAQ,eAAe;KACvB,MAAM;KACP,CAAC;AACF,YAAQ,SAAS,QAAQ,2DAA2D;KAClF,QAAQ,eAAe;KACvB,MAAM;KACN;KACA,SAAS;KACV,CAAC;;;AAIR,yBAAuB,QAAQ;EAO/B,IAAI,OAAO,WAAW;EACtB,IAAI,iBAAiB,WAAW,iBAAiB;EACjD,MAAM,WAAW,WAAW;AAC5B,MAAI,SAAS,UAAa,aAAa,QAAW;GAChD,MAAM,SAAS,MAAM,SAAS,IAAI,eAAe,GAAG;AACpD,OAAI,WAAW,QAAW;IACxB,MAAMC,WAAmC,EAAE;AAC3C,SAAK,MAAM,CAAC,MAAM,SAAS,QAAQ,aAAc,UAAS,QAAQ;AAClE,UAAM,SAAS,IAAI,eAAe,IAAI,SAAS;AAC/C,qBAAiB,iCAAiC,EAAE,QAAQ,eAAe,IAAI,CAAC;AAChF,YAAQ,SAAS,QAAQ,0DAA0D;KACjF,QAAQ,eAAe;KACvB,OAAO,OAAO,KAAK,SAAS,CAAC;KAC9B,CAAC;UACG;AACL,WAAO;AACP,qBAAiB,WAAW,iBAAiB;;;AAGjD,MAAI,SAAS,OACX,MAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,KAAK,EAAE;GACjD,MAAM,UAAU,QAAQ,aAAa,IAAI,KAAK;AAC9C,OAAI,YAAY,UAAa,YAAY,QAAQ;AAC/C,QAAI,mBAAmB,SACrB,OAAM,IAAI,oBACR,aAAa,KAAK,qHAClB,EAAE,UAAU;KAAE,QAAQ,eAAe;KAAI,MAAM;KAAM,EAAE,CACxD;AAEH,qBAAiB,gCAAgC;KAC/C,QAAQ,eAAe;KACvB,MAAM;KACP,CAAC;AACF,YAAQ,SAAS,QAAQ,uDAAuD;KAC9E,QAAQ,eAAe;KACvB,MAAM;KACP,CAAC;;;AAIR,SAAO,QAAQ;;CAGjB,eAAe,QAAuB;AACpC,MAAI;AACF,SAAM,UAAU,OAAO;WAChB,OAAO;AAEd,OAAI,iBAAiB,SAAS,MAAM,QAAQ,aAAa,CAAC,SAAS,UAAU,CAAE;AAC/E,SAAM,IAAI,mBAAmB,8CAA8C;IACzE,UAAU;KAAE,WAAW,QAAQ,gBAAgB;KAAM,QAAQ,eAAe;KAAI;IAChF;IACD,CAAC;;;CAIN,MAAMC,YAAuB,OAAO,OAAO;EACzC,IAAI,eAAe;EACnB;EACA;EACA;EACA,GAAI,QAAQ,aAAa,SAAY,EAAE,GAAG,EAAE,UAAU,QAAQ,UAAU;EACxE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;AACF,QAAO;;;;;;;;;;AAWT,SAAS,qBAAqB,SAAkE;CAC9F,MAAM,WAAW,QAAQ;AACzB,KAAI,aAAa,OACf,QAAO,EAAE,qBAAqB,SAAS,eAAe,EAAE;AAE1D,KAAI,QAAQ,gBAAgB,QAAW;EACrC,MAAM,QAAQ,QAAQ;EACtB,MAAM,SAAS,aAAa,KAAK,MAAM,GAAG,QAAQ,UAAU;AAC5D,SAAO,EAAE,qBAAqB,QAAQ;;;AAK1C,SAAS,YAAY,OAAgB,KAAwC;CAC3E,MAAM,WAAW,IAAI,SAAS,SAAY,EAAE,GAAG,EAAE,MAAM,IAAI,MAAM;AACjE,KAAI,iBAAiB,OAAO;EAC1B,MAAM,OAAO,MAAM;EACnB,MAAM,UAAU,MAAM,WAAW;AACjC,MAAI,SAAS,gBAAgB,mBAAmB,KAAK,QAAQ,CAC3D,QAAO,IAAI,kBAAkB,8BAA8B;GAAE;GAAU;GAAO,CAAC;AAIjF,MAAI,+BAA+B,KAAK,QAAQ,CAC9C,QAAO,IAAI,oBACT,wBAAwB,IAAI,OAAO,WAAW,IAAI,KAAK,KAAK,GAAG,IAC/D;GACE;GACA;GACD,CACF;AAEH,MAAI,0DAA0D,KAAK,QAAQ,CACzE,QAAO,IAAI,qBAAqB,qBAAqB,IAAI,OAAO,KAAK,IAAI,SAAS,GAAG,IAAI;GACvF;GACA;GACD,CAAC;AAEJ,SAAO,IAAI,iBAAiB,QAAQ,WAAW,IAAI,MAAM,UAAU,GAAG,SAAS;GAC7E;GACA;GACD,CAAC;;AAEJ,QAAO,IAAI,iBAAiB,uBAAuB,OAAO,MAAM,IAAI;EAAE;EAAU;EAAO,CAAC;;AAG1F,SAAS,iBAAiB,WAAgE;AACxF,QACE,OAAO,cAAc,YACrB,cAAc,QACd,UAAU,gBAAgB,UAC1B,UAAU,YAAY,SAAS"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["DEFAULT_CLIENT_VERSION: string","pkg.version","collisionStrategy: CollisionStrategy","tools: SdkTool[]","cursor: string | undefined","result: Awaited<ReturnType<typeof sdkClient.listTools>>","items: SdkResource[]","result: Awaited<ReturnType<typeof sdkClient.listResources>>","items: SdkPrompt[]","result: Awaited<ReturnType<typeof sdkClient.listPrompts>>","result: Awaited<ReturnType<typeof sdkClient.callTool>>","result: Awaited<ReturnType<typeof sdkClient.readResource>>","result: Awaited<ReturnType<typeof sdkClient.getPrompt>>","lastToolFingerprints: ReadonlyMap<string, string> | undefined","recorded: Record<string, string>","clientApi: MCPClient"],"sources":["../../src/client/client.ts"],"sourcesContent":["import pkg from '../../package.json' with { type: 'json' };\n/**\n * `createMCPClient(...)` - the entry point for opening a typed MCP\n * client connection.\n *\n * The returned {@link MCPClient}:\n *\n * - Wraps the `@modelcontextprotocol/sdk` `Client` instance and the\n * selected SDK transport.\n * - Exposes `listTools` / `listResources` / `listPrompts` /\n * `callTool` / `readResource` / `getPrompt` / `close` plus the\n * strategy-aware `toTools(...)` adapter.\n * - Emits one INFO-log per server when the connected transport is\n * the deprecated SSE transport, on the resolved resumable\n * capability, and when the structured-content + outputSchema\n * round-trip first succeeds.\n *\n * @packageDocumentation\n */\n\nimport type { Tool } from '@graphorin/core';\nimport { incrementCounter } from '@graphorin/tools/audit';\nimport type { CollisionStrategy } from '@graphorin/tools/registry';\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { ToolListChangedNotificationSchema } from '@modelcontextprotocol/sdk/types.js';\nimport {\n MCPCallTimeoutError,\n MCPCancelledError,\n MCPConnectionError,\n MCPInvalidConfigError,\n MCPProtocolError,\n MCPToolNotFoundError,\n MCPToolPinningError,\n} from '../errors/index.js';\nimport { deriveServerIdentity } from '../helpers/identity.js';\nimport { validateMCPServerConfig } from '../helpers/validate-config.js';\nimport type { ServerIdentity } from '../transport/types.js';\nimport { computeClientCapabilities, registerClientRequestHandlers } from './client-handlers.js';\nimport { adaptMCPTools } from './to-tools.js';\nimport { buildTransport, type TransportAuthSource } from './transport-factory.js';\nimport type {\n CreateMCPClientOptions,\n MCPCallToolResult,\n MCPClient,\n MCPContentPart,\n MCPPromptDefinition,\n MCPPromptMessage,\n MCPResourceContent,\n MCPResourceDefinition,\n MCPToolDefinition,\n MCPToToolsOptions,\n} from './types.js';\n\nconst DEFAULT_CLIENT_NAME = 'graphorin-mcp-client';\nconst DEFAULT_CLIENT_VERSION: string = pkg.version;\n\n/**\n * Process-scoped dedup flag for the deprecated SSE transport WARN.\n * Once set, subsequent {@link createMCPClient} calls with the SSE\n * transport do not re-emit the warning. Tests reset via\n * {@link _resetSseWarnDedupForTesting}.\n */\nlet sseWarnEmittedThisProcess = false;\n\n/**\n * Reset the SSE WARN dedup flag. Used by tests.\n *\n * @experimental\n */\nexport function _resetSseWarnDedupForTesting(): void {\n sseWarnEmittedThisProcess = false;\n}\n\n/**\n * Open a typed MCP client connection.\n *\n * @stable\n */\nexport async function createMCPClient(options: CreateMCPClientOptions): Promise<MCPClient> {\n validateMCPServerConfig({ transport: options.transport });\n\n // Mutually exclusive (documented on `CreateMCPClientOptions`): a live\n // OAuth provider and a static pre-shared token cannot both drive the\n // outbound `Authorization` header.\n if (options.authProvider !== undefined && options.bearerToken !== undefined) {\n throw new MCPInvalidConfigError(\n '`authProvider` and `bearerToken` are mutually exclusive; supply at most one.',\n { metadata: { transport: options.transport.kind } },\n );\n }\n const auth = resolveTransportAuth(options);\n if (auth !== undefined && options.transport.kind === 'stdio') {\n throw new MCPInvalidConfigError(\n 'authProvider / bearerToken require an HTTP transport (streamable-http or sse); the stdio transport carries no Authorization header.',\n { metadata: { transport: 'stdio' } },\n );\n }\n\n if (\n options.transport.kind === 'sse' &&\n options.suppressDeprecatedTransportWarning !== true &&\n !sseWarnEmittedThisProcess\n ) {\n sseWarnEmittedThisProcess = true;\n if (options.logger !== undefined) {\n options.logger(\n 'warn',\n 'MCP SSE transport is deprecated; migrate the server to the streamable-http transport when possible.',\n );\n }\n incrementCounter('mcp.transport.deprecated.warn.total', { transport: 'sse' });\n }\n\n const built = buildTransport(options.transport, auth === undefined ? undefined : { auth });\n return createMCPClientFromSdkTransport({\n transport: built.transport,\n transportConfig: options.transport,\n ...(options.collisionStrategy === undefined\n ? {}\n : { collisionStrategy: options.collisionStrategy }),\n ...(options.priority === undefined ? {} : { priority: options.priority }),\n ...(options.serverInfoName === undefined ? {} : { serverInfoName: options.serverInfoName }),\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n ...(options.clientName === undefined ? {} : { clientName: options.clientName }),\n ...(options.clientVersion === undefined ? {} : { clientVersion: options.clientVersion }),\n ...(options.elicitation === undefined ? {} : { elicitation: options.elicitation }),\n ...(options.sampling === undefined ? {} : { sampling: options.sampling }),\n ...(options.onTransportClose === undefined\n ? {}\n : { onTransportClose: options.onTransportClose }),\n ...(options.onTransportError === undefined\n ? {}\n : { onTransportError: options.onTransportError }),\n });\n}\n\n/**\n * Internal factory that takes a pre-built SDK `Transport`. Exposed\n * for the test seam - production code uses {@link createMCPClient}.\n *\n * @internal\n */\nexport interface CreateMCPClientFromSdkTransportOptions {\n readonly transport: Transport;\n readonly transportConfig: import('../transport/types.js').MCPTransportConfig;\n readonly collisionStrategy?: CollisionStrategy;\n readonly priority?: number;\n readonly serverInfoName?: string;\n readonly logger?: CreateMCPClientOptions['logger'];\n readonly clientName?: string;\n readonly clientVersion?: string;\n readonly elicitation?: CreateMCPClientOptions['elicitation'];\n readonly sampling?: CreateMCPClientOptions['sampling'];\n readonly onTransportClose?: CreateMCPClientOptions['onTransportClose'];\n readonly onTransportError?: CreateMCPClientOptions['onTransportError'];\n}\n\n/**\n * Build an {@link MCPClient} from a pre-built SDK transport. The\n * production {@link createMCPClient} delegates here after building\n * the SDK transport from a {@link MCPTransportConfig}.\n *\n * @internal\n */\nexport async function createMCPClientFromSdkTransport(\n options: CreateMCPClientFromSdkTransportOptions,\n): Promise<MCPClient> {\n const sdkClient = new Client({\n name: options.clientName ?? DEFAULT_CLIENT_NAME,\n version: options.clientVersion ?? DEFAULT_CLIENT_VERSION,\n });\n\n // WI-13 (P2-2): advertise + register the server-initiated request\n // handlers (elicitation / sampling) before connecting, so they are in\n // place when the session starts. Both are gated - capabilities are\n // advertised only when the operator supplied the matching callback.\n const clientCapabilities = computeClientCapabilities({\n elicitation: options.elicitation,\n sampling: options.sampling,\n });\n if (clientCapabilities !== undefined) {\n sdkClient.registerCapabilities(clientCapabilities);\n }\n const serverIdRef = { current: 'unknown' };\n registerClientRequestHandlers(sdkClient, {\n ...(options.elicitation === undefined ? {} : { elicitation: options.elicitation }),\n ...(options.sampling === undefined ? {} : { sampling: options.sampling }),\n serverIdRef,\n });\n // MC-6: surface server-side catalogue churn - at minimum an audit\n // counter + log line; operators re-run toTools() to refresh and\n // re-sanitize the catalogue (which also re-runs the drift diff).\n sdkClient.setNotificationHandler(ToolListChangedNotificationSchema, async () => {\n incrementCounter('mcp.tools.list-changed.total', {\n server: serverIdRef.current ?? 'unknown',\n });\n options.logger?.(\n 'warn',\n 'mcp.tools.list_changed received - re-run toTools() to refresh + re-sanitize the catalogue',\n { server: serverIdRef.current },\n );\n });\n\n try {\n await sdkClient.connect(options.transport);\n } catch (cause) {\n throw new MCPConnectionError(\n `MCP transport could not be established: ${(cause as Error).message ?? String(cause)}`,\n {\n metadata: { transport: options.transportConfig.kind },\n cause,\n },\n );\n }\n\n const serverInfo = sdkClient.getServerVersion() ?? {\n name: 'unknown',\n version: '0.0.0',\n };\n const serverIdentity = deriveServerIdentity(\n options.transportConfig,\n options.serverInfoName ?? serverInfo.name,\n );\n // Backfill the server id so the client-side request handlers\n // (registered before connect) label their counters with it.\n serverIdRef.current = serverIdentity.id;\n // mcp-skills-10: surface transport lifecycle. Without these a dead\n // stdio child / dropped HTTP session is observable only as\n // MCPProtocolErrors on subsequent calls. The SDK Protocol base\n // exposes assignable onclose/onerror callbacks (the transport's own\n // handlers are managed by the SDK - never overwrite those).\n sdkClient.onclose = () => {\n incrementCounter('mcp.transport.closed.total', { server: serverIdentity.id });\n options.logger?.('warn', 'MCP transport closed - rebuild the client to reconnect', {\n server: serverIdentity.id,\n });\n options.onTransportClose?.({ server: serverIdentity.id });\n };\n sdkClient.onerror = (error: Error) => {\n incrementCounter('mcp.transport.error.total', { server: serverIdentity.id });\n options.onTransportError?.(error, { server: serverIdentity.id });\n };\n const collisionStrategy: CollisionStrategy = options.collisionStrategy ?? 'auto-prefix';\n\n // MC-1: the client-side eventStore option was removed - per the\n // Streamable HTTP spec event replay is the SERVER's responsibility,\n // and the SDK transport auto-reconnects with Last-Event-ID on its\n // own. Warn legacy callers instead of silently ignoring the option.\n if ((options as { readonly eventStore?: unknown }).eventStore !== undefined) {\n options.logger?.(\n 'warn',\n \"the client 'eventStore' option was removed (MC-1): event replay is a server responsibility; the SDK transport auto-reconnects with Last-Event-ID. Remove the option.\",\n { server: serverIdentity.id },\n );\n }\n // MC-9: a session id means stateful routing, NOT a replay guarantee.\n const sessionIdPresent =\n isStreamableHttp(options.transport) &&\n (options.transport as StreamableHTTPClientTransport).sessionId !== undefined;\n const resumable = sessionIdPresent;\n if (options.logger !== undefined) {\n options.logger('info', 'mcp.session.session-id.resolved', {\n server: serverIdentity.id,\n value: sessionIdPresent,\n source: sessionIdPresent ? 'session-id-present' : 'transport-default',\n });\n }\n let structuredContentSeenLogged = false;\n\n /**\n * mcp-skills-02: MCP list operations are cursor-paginated (protocol\n * 2024-11-05+) and the SDK does NOT auto-paginate - a single call\n * returns page 1 only. Ignoring `nextCursor` silently truncated the\n * catalogue: tools beyond the first page never reached `toTools()`,\n * defer-loading thresholds counted a partial catalogue, pin\n * fingerprints covered a partial catalogue, and resources/prompts were\n * under-listed. Every list surface now drains the cursor chain, with a\n * defensive page cap against buggy/adversarial servers that never\n * terminate it.\n */\n const MAX_LIST_PAGES = 100;\n\n function warnListPageCap(what: 'tools' | 'resources' | 'prompts', collected: number): void {\n options.logger?.(\n 'warn',\n `mcp.list.${what}.page-cap-reached: server kept returning nextCursor after ${MAX_LIST_PAGES} pages; the ${what} catalogue is truncated at ${collected} entries.`,\n { server: serverIdentity.id },\n );\n }\n\n async function listTools(opts?: {\n signal?: AbortSignal;\n }): Promise<ReadonlyArray<MCPToolDefinition>> {\n const requestOptions = opts?.signal === undefined ? {} : { signal: opts.signal };\n type SdkTool = {\n name: string;\n description?: string;\n inputSchema: Readonly<Record<string, unknown>>;\n outputSchema?: Readonly<Record<string, unknown>>;\n title?: string;\n };\n const tools: SdkTool[] = [];\n let cursor: string | undefined;\n for (let page = 0; ; page++) {\n if (page >= MAX_LIST_PAGES) {\n warnListPageCap('tools', tools.length);\n break;\n }\n let result: Awaited<ReturnType<typeof sdkClient.listTools>>;\n try {\n result = await sdkClient.listTools(cursor === undefined ? {} : { cursor }, requestOptions);\n } catch (cause) {\n throw mapSdkError(cause, {});\n }\n tools.push(...((result.tools ?? []) as ReadonlyArray<SdkTool>));\n cursor = (result as { nextCursor?: string }).nextCursor;\n if (cursor === undefined) break;\n }\n if (!structuredContentSeenLogged && tools.some((t) => t.outputSchema !== undefined)) {\n structuredContentSeenLogged = true;\n if (options.logger !== undefined) {\n options.logger('info', 'mcp.server.structured-content.detected', {\n server: serverIdentity.id,\n });\n }\n }\n return Object.freeze(\n tools.map((t) =>\n Object.freeze({\n name: t.name,\n description: t.description ?? '',\n inputSchema: t.inputSchema,\n ...(t.outputSchema === undefined ? {} : { outputSchema: t.outputSchema }),\n ...(t.title === undefined ? {} : { title: t.title }),\n }),\n ),\n );\n }\n\n async function listResources(opts?: {\n signal?: AbortSignal;\n }): Promise<ReadonlyArray<MCPResourceDefinition>> {\n const requestOptions = opts?.signal === undefined ? {} : { signal: opts.signal };\n type SdkResource = {\n uri: string;\n name?: string;\n description?: string;\n mimeType?: string;\n };\n const items: SdkResource[] = [];\n let cursor: string | undefined;\n for (let page = 0; ; page++) {\n if (page >= MAX_LIST_PAGES) {\n warnListPageCap('resources', items.length);\n break;\n }\n let result: Awaited<ReturnType<typeof sdkClient.listResources>>;\n try {\n result = await sdkClient.listResources(\n cursor === undefined ? {} : { cursor },\n requestOptions,\n );\n } catch (cause) {\n throw mapSdkError(cause, {});\n }\n items.push(...((result.resources ?? []) as ReadonlyArray<SdkResource>));\n cursor = (result as { nextCursor?: string }).nextCursor;\n if (cursor === undefined) break;\n }\n return Object.freeze(\n items.map((r) =>\n Object.freeze({\n uri: r.uri,\n ...(r.name === undefined ? {} : { name: r.name }),\n ...(r.description === undefined ? {} : { description: r.description }),\n ...(r.mimeType === undefined ? {} : { mimeType: r.mimeType }),\n }),\n ),\n );\n }\n\n async function listPrompts(opts?: {\n signal?: AbortSignal;\n }): Promise<ReadonlyArray<MCPPromptDefinition>> {\n const requestOptions = opts?.signal === undefined ? {} : { signal: opts.signal };\n type SdkPrompt = {\n name: string;\n description?: string;\n arguments?: ReadonlyArray<{ name: string; description?: string; required?: boolean }>;\n };\n const items: SdkPrompt[] = [];\n let cursor: string | undefined;\n for (let page = 0; ; page++) {\n if (page >= MAX_LIST_PAGES) {\n warnListPageCap('prompts', items.length);\n break;\n }\n let result: Awaited<ReturnType<typeof sdkClient.listPrompts>>;\n try {\n result = await sdkClient.listPrompts(\n cursor === undefined ? {} : { cursor },\n requestOptions,\n );\n } catch (cause) {\n throw mapSdkError(cause, {});\n }\n items.push(...((result.prompts ?? []) as ReadonlyArray<SdkPrompt>));\n cursor = (result as { nextCursor?: string }).nextCursor;\n if (cursor === undefined) break;\n }\n return Object.freeze(\n items.map((p) =>\n Object.freeze({\n name: p.name,\n ...(p.description === undefined ? {} : { description: p.description }),\n ...(p.arguments === undefined\n ? {}\n : {\n arguments: Object.freeze(\n p.arguments.map((a) =>\n Object.freeze({\n name: a.name,\n ...(a.description === undefined ? {} : { description: a.description }),\n ...(a.required === undefined ? {} : { required: a.required }),\n }),\n ),\n ),\n }),\n }),\n ),\n );\n }\n\n async function callTool(\n name: string,\n args: unknown,\n opts?: { signal?: AbortSignal; timeoutMs?: number },\n ): Promise<MCPCallToolResult> {\n // MC-3: `timeoutMs` maps onto the SDK's RequestOptions - both the\n // per-attempt and the total ceiling, so progress notifications\n // cannot extend past the caller's budget.\n const requestOptions = {\n ...(opts?.signal === undefined ? {} : { signal: opts.signal }),\n ...(opts?.timeoutMs === undefined\n ? {}\n : { timeout: opts.timeoutMs, maxTotalTimeout: opts.timeoutMs }),\n };\n incrementCounter('mcp.call.invoked.total', { server: serverIdentity.id, tool: name });\n let result: Awaited<ReturnType<typeof sdkClient.callTool>>;\n try {\n result = await sdkClient.callTool(\n { name, arguments: args as Record<string, unknown> },\n undefined,\n requestOptions,\n );\n } catch (cause) {\n const mapped = mapSdkError(cause, { tool: name });\n if (mapped instanceof MCPCancelledError) {\n incrementCounter('mcp.call.cancelled.total', { server: serverIdentity.id, tool: name });\n } else {\n incrementCounter('mcp.call.failed.total', { server: serverIdentity.id, tool: name });\n }\n throw mapped;\n }\n const content = (result.content ?? []) as ReadonlyArray<MCPContentPart>;\n return Object.freeze({\n content: Object.freeze([...content]),\n ...(result.structuredContent === undefined\n ? {}\n : { structuredContent: result.structuredContent as Readonly<Record<string, unknown>> }),\n ...(result.isError === undefined ? {} : { isError: Boolean(result.isError) }),\n });\n }\n\n async function readResourceContents(\n uri: string,\n opts?: { signal?: AbortSignal },\n ): Promise<ReadonlyArray<MCPResourceContent>> {\n const requestOptions = opts?.signal === undefined ? {} : { signal: opts.signal };\n let result: Awaited<ReturnType<typeof sdkClient.readResource>>;\n try {\n result = await sdkClient.readResource({ uri }, requestOptions);\n } catch (cause) {\n throw mapSdkError(cause, {});\n }\n const contents = (result.contents ?? []) as ReadonlyArray<MCPResourceContent>;\n if (contents.length === 0) {\n throw new MCPProtocolError(`MCP server returned no contents for resource '${uri}'.`, {\n metadata: { server: serverIdentity.id },\n });\n }\n return Object.freeze(contents.map((c) => Object.freeze(c)));\n }\n\n async function readResource(\n uri: string,\n opts?: { signal?: AbortSignal },\n ): Promise<MCPResourceContent> {\n const contents = await readResourceContents(uri, opts);\n // mcp-skills-11: the `resources/read` result is an ARRAY precisely\n // because one URI can yield multiple contents. The single-content\n // convenience keeps its shape, but a silent truncation is now a\n // visible one.\n if (contents.length > 1) {\n incrementCounter('mcp.resource.multi-content-truncated.total', {\n server: serverIdentity.id,\n });\n options.logger?.(\n 'warn',\n `resource '${uri}' returned ${contents.length} content items; readResource() surfaces only the first - use readResourceContents() for all of them`,\n { server: serverIdentity.id },\n );\n }\n return contents[0] as MCPResourceContent;\n }\n\n async function getPrompt(\n name: string,\n args?: unknown,\n opts?: { signal?: AbortSignal },\n ): Promise<{ readonly messages: ReadonlyArray<MCPPromptMessage> }> {\n const requestOptions = opts?.signal === undefined ? {} : { signal: opts.signal };\n let result: Awaited<ReturnType<typeof sdkClient.getPrompt>>;\n try {\n result = await sdkClient.getPrompt(\n {\n name,\n ...(args === undefined ? {} : { arguments: args as Record<string, string> }),\n },\n requestOptions,\n );\n } catch (cause) {\n throw mapSdkError(cause, {});\n }\n const messages = (result.messages ?? []).map(\n (m): MCPPromptMessage =>\n Object.freeze({\n role: m.role,\n content: m.content as MCPContentPart,\n }),\n );\n return Object.freeze({ messages: Object.freeze(messages) });\n }\n\n let lastToolFingerprints: ReadonlyMap<string, string> | undefined;\n\n async function toTools(toolsOpts?: MCPToToolsOptions): Promise<ReadonlyArray<Tool>> {\n const catalogue = await listTools();\n const adapted = adaptMCPTools({\n client: clientApi,\n serverIdentity,\n catalogue,\n ...(toolsOpts === undefined ? {} : { options: toolsOpts }),\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n });\n // MC-6: cross-snapshot drift - a definition changing behind an\n // already-seen name within this client's lifetime is audited.\n if (lastToolFingerprints !== undefined) {\n for (const [name, hash] of adapted.fingerprints) {\n const previous = lastToolFingerprints.get(name);\n if (previous !== undefined && previous !== hash) {\n incrementCounter('mcp.tools.changed.total', {\n server: serverIdentity.id,\n tool: name,\n });\n options.logger?.('warn', 'mcp.tools.changed: definition drifted between snapshots', {\n server: serverIdentity.id,\n tool: name,\n previous,\n current: hash,\n });\n }\n }\n }\n lastToolFingerprints = adapted.fingerprints;\n // MC-6: operator pins from a previously approved snapshot - the\n // rug-pull (approve-then-swap across restarts) posture. C6 extends it\n // with durable trust-on-first-use via `pinStore`: the first snapshot\n // is RECORDED, later snapshots are COMPARED, and a store-backed\n // mismatch defaults to 'reject' (a persisted first approval is an\n // explicit trust decision).\n let pins = toolsOpts?.pinnedFingerprints;\n let mismatchAction = toolsOpts?.onPinMismatch ?? 'warn';\n const pinStore = toolsOpts?.pinStore;\n if (pins === undefined && pinStore !== undefined) {\n const stored = await pinStore.get(serverIdentity.id);\n if (stored === undefined) {\n const recorded: Record<string, string> = {};\n for (const [name, hash] of adapted.fingerprints) recorded[name] = hash;\n await pinStore.set(serverIdentity.id, recorded);\n incrementCounter('mcp.tools.pins-recorded.total', { server: serverIdentity.id });\n options.logger?.('info', 'mcp.tools.pins-recorded: first-use fingerprints stored', {\n server: serverIdentity.id,\n tools: Object.keys(recorded).length,\n });\n } else {\n pins = stored;\n mismatchAction = toolsOpts?.onPinMismatch ?? 'reject';\n }\n }\n if (pins !== undefined) {\n for (const [name, pinned] of Object.entries(pins)) {\n const current = adapted.fingerprints.get(name);\n if (current !== undefined && current !== pinned) {\n if (mismatchAction === 'reject') {\n throw new MCPToolPinningError(\n `MCP tool '${name}' no longer matches its pinned definition fingerprint - the server changed the definition behind an approved name.`,\n { metadata: { server: serverIdentity.id, tool: name } },\n );\n }\n incrementCounter('mcp.tools.pin-mismatch.total', {\n server: serverIdentity.id,\n tool: name,\n });\n options.logger?.('warn', 'mcp.tools.pin-mismatch: pinned fingerprint diverged', {\n server: serverIdentity.id,\n tool: name,\n });\n }\n }\n }\n return adapted.tools;\n }\n\n async function close(): Promise<void> {\n try {\n await sdkClient.close();\n } catch (cause) {\n // Treat double-close as a no-op; surface other failures.\n if (cause instanceof Error && cause.message.toLowerCase().includes('already')) return;\n throw new MCPConnectionError('MCP transport could not be closed cleanly.', {\n metadata: { transport: options.transportConfig.kind, server: serverIdentity.id },\n cause,\n });\n }\n }\n\n const clientApi: MCPClient = Object.freeze({\n id: serverIdentity.id,\n serverInfo,\n serverIdentity,\n collisionStrategy,\n ...(options.priority === undefined ? {} : { priority: options.priority }),\n sessionIdPresent,\n resumable,\n listTools,\n listResources,\n listPrompts,\n callTool,\n readResource,\n readResourceContents,\n getPrompt,\n toTools,\n close,\n });\n return clientApi;\n}\n\n/**\n * Resolve the live {@link TransportAuthSource} for the outbound\n * `Authorization` header from the mutually-exclusive `authProvider` /\n * `bearerToken` options. `authProvider.resolveHeader()` already returns\n * the full header value (`Bearer …`); a static `bearerToken` is wrapped\n * into a constant `Bearer`-prefixed resolver. Returns `undefined` when\n * neither is supplied (no header injection).\n */\nfunction resolveTransportAuth(options: CreateMCPClientOptions): TransportAuthSource | undefined {\n const provider = options.authProvider;\n if (provider !== undefined) {\n return { resolveHeader: () => provider.resolveHeader() };\n }\n if (options.bearerToken !== undefined) {\n const token = options.bearerToken;\n const header = /^bearer\\s/i.test(token) ? token : `Bearer ${token}`;\n return { resolveHeader: () => header };\n }\n return undefined;\n}\n\nfunction mapSdkError(cause: unknown, ctx: { readonly tool?: string }): Error {\n const metadata = ctx.tool === undefined ? {} : { tool: ctx.tool };\n if (cause instanceof Error) {\n const name = cause.name;\n const message = cause.message ?? '';\n if (name === 'AbortError' || /aborted|cancell/i.test(message)) {\n return new MCPCancelledError('MCP request was cancelled.', { metadata, cause });\n }\n // MC-3: the SDK reports request-timeout as a plain McpError - map it\n // onto the advertised typed class instead of MCPProtocolError.\n if (/request timed out|timed out/i.test(message)) {\n return new MCPCallTimeoutError(\n `MCP request timed out${ctx.tool ? ` (tool: ${ctx.tool})` : ''}.`,\n {\n metadata,\n cause,\n },\n );\n }\n if (/unknown\\s+tool|tool\\s+not\\s+found|method\\s+not\\s+found/i.test(message)) {\n return new MCPToolNotFoundError(`MCP tool not found${ctx.tool ? `: ${ctx.tool}` : ''}.`, {\n metadata,\n cause,\n });\n }\n return new MCPProtocolError(message.length === 0 ? cause.toString() : message, {\n metadata,\n cause,\n });\n }\n return new MCPProtocolError(`MCP request failed: ${String(cause)}`, { metadata, cause });\n}\n\nfunction isStreamableHttp(transport: unknown): transport is StreamableHTTPClientTransport {\n return (\n typeof transport === 'object' &&\n transport !== null &&\n transport.constructor !== undefined &&\n transport.constructor.name === 'StreamableHTTPClientTransport'\n );\n}\n\nexport type { ServerIdentity };\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -53,8 +53,7 @@ import "./oauth/index.js";
|
|
|
53
53
|
*
|
|
54
54
|
* @packageDocumentation
|
|
55
55
|
*/
|
|
56
|
-
|
|
57
|
-
declare const VERSION = "0.6.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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
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";
|
|
@@ -50,8 +51,8 @@ import "./oauth/index.js";
|
|
|
50
51
|
*
|
|
51
52
|
* @packageDocumentation
|
|
52
53
|
*/
|
|
53
|
-
/** Canonical version constant
|
|
54
|
-
const VERSION =
|
|
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
|
|
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"}
|
package/dist/package.js
ADDED
|
@@ -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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphorin/mcp",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"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.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Oleksiy Stepurenko",
|
|
@@ -73,10 +73,10 @@
|
|
|
73
73
|
],
|
|
74
74
|
"dependencies": {
|
|
75
75
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
76
|
-
"@graphorin/core": "0.6.
|
|
77
|
-
"@graphorin/observability": "0.6.
|
|
78
|
-
"@graphorin/security": "0.6.
|
|
79
|
-
"@graphorin/tools": "0.6.
|
|
76
|
+
"@graphorin/core": "0.6.1",
|
|
77
|
+
"@graphorin/observability": "0.6.1",
|
|
78
|
+
"@graphorin/security": "0.6.1",
|
|
79
|
+
"@graphorin/tools": "0.6.1"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
82
|
"zod": "^3.23.0 || ^4.0.0"
|