@graphorin/mcp 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/LICENSE +21 -0
  3. package/README.md +296 -0
  4. package/dist/client/adapt-result.d.ts +25 -0
  5. package/dist/client/adapt-result.d.ts.map +1 -0
  6. package/dist/client/adapt-result.js +174 -0
  7. package/dist/client/adapt-result.js.map +1 -0
  8. package/dist/client/client-handlers.js +134 -0
  9. package/dist/client/client-handlers.js.map +1 -0
  10. package/dist/client/client.d.ts +21 -0
  11. package/dist/client/client.d.ts.map +1 -0
  12. package/dist/client/client.js +358 -0
  13. package/dist/client/client.js.map +1 -0
  14. package/dist/client/defer-loading.d.ts +7 -0
  15. package/dist/client/defer-loading.d.ts.map +1 -0
  16. package/dist/client/defer-loading.js +81 -0
  17. package/dist/client/defer-loading.js.map +1 -0
  18. package/dist/client/inbound-filters.js +65 -0
  19. package/dist/client/inbound-filters.js.map +1 -0
  20. package/dist/client/index.d.ts +7 -0
  21. package/dist/client/index.js +7 -0
  22. package/dist/client/mcp-resource-reader.d.ts +22 -0
  23. package/dist/client/mcp-resource-reader.d.ts.map +1 -0
  24. package/dist/client/mcp-resource-reader.js +113 -0
  25. package/dist/client/mcp-resource-reader.js.map +1 -0
  26. package/dist/client/pinning.js +41 -0
  27. package/dist/client/pinning.js.map +1 -0
  28. package/dist/client/to-tools.d.ts +41 -0
  29. package/dist/client/to-tools.d.ts.map +1 -0
  30. package/dist/client/to-tools.js +125 -0
  31. package/dist/client/to-tools.js.map +1 -0
  32. package/dist/client/transport-factory.js +86 -0
  33. package/dist/client/transport-factory.js.map +1 -0
  34. package/dist/client/types.d.ts +353 -0
  35. package/dist/client/types.d.ts.map +1 -0
  36. package/dist/errors/index.d.ts +120 -0
  37. package/dist/errors/index.d.ts.map +1 -0
  38. package/dist/errors/index.js +88 -0
  39. package/dist/errors/index.js.map +1 -0
  40. package/dist/helpers/identity.d.ts +22 -0
  41. package/dist/helpers/identity.d.ts.map +1 -0
  42. package/dist/helpers/identity.js +85 -0
  43. package/dist/helpers/identity.js.map +1 -0
  44. package/dist/helpers/index.d.ts +3 -0
  45. package/dist/helpers/index.js +4 -0
  46. package/dist/helpers/validate-config.d.ts +16 -0
  47. package/dist/helpers/validate-config.d.ts.map +1 -0
  48. package/dist/helpers/validate-config.js +61 -0
  49. package/dist/helpers/validate-config.js.map +1 -0
  50. package/dist/index.d.ts +60 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +58 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/oauth/bridge.d.ts +59 -0
  55. package/dist/oauth/bridge.d.ts.map +1 -0
  56. package/dist/oauth/bridge.js +75 -0
  57. package/dist/oauth/bridge.js.map +1 -0
  58. package/dist/oauth/index.d.ts +3 -0
  59. package/dist/oauth/index.js +4 -0
  60. package/dist/oauth/library.d.ts +23 -0
  61. package/dist/oauth/library.d.ts.map +1 -0
  62. package/dist/oauth/library.js +27 -0
  63. package/dist/oauth/library.js.map +1 -0
  64. package/dist/registry/json-schema.js +215 -0
  65. package/dist/registry/json-schema.js.map +1 -0
  66. package/dist/transport/index.d.ts +4 -0
  67. package/dist/transport/index.js +4 -0
  68. package/dist/transport/types.d.ts +93 -0
  69. package/dist/transport/types.d.ts.map +1 -0
  70. package/package.json +105 -0
@@ -0,0 +1,134 @@
1
+ import { incrementCounter } from "@graphorin/tools/audit";
2
+ import { CreateMessageRequestSchema, ElicitRequestSchema } from "@modelcontextprotocol/sdk/types.js";
3
+
4
+ //#region src/client/client-handlers.ts
5
+ /**
6
+ * Client-side request handlers for server-initiated MCP requests
7
+ * (WI-13 / P2-2): **elicitation** (`elicitation/create`) and **sampling**
8
+ * (`sampling/createMessage`).
9
+ *
10
+ * Both are gated: the client advertises a capability and registers a
11
+ * handler *only* when the operator supplies the matching callback on
12
+ * {@link CreateMCPClientOptions}. A conforming server will not issue a
13
+ * request for an un-advertised capability, so the default client is inert
14
+ * (no implicit prompting, no implicit model calls — R4).
15
+ *
16
+ * The SDK request/result schemas are kept inside this module; the public
17
+ * surface speaks only the Graphorin-typed {@link MCPElicitationRequest} /
18
+ * {@link MCPSamplingRequest} boundary.
19
+ *
20
+ * @packageDocumentation
21
+ */
22
+ /**
23
+ * Compute the {@link ClientCapabilities} to advertise on `initialize`,
24
+ * based on which server-initiated handlers the operator supplied.
25
+ * Returns `undefined` when none are configured (advertise nothing).
26
+ */
27
+ function computeClientCapabilities(opts) {
28
+ if (opts.elicitation === void 0 && opts.sampling === void 0) return void 0;
29
+ return {
30
+ ...opts.elicitation === void 0 ? {} : { elicitation: {} },
31
+ ...opts.sampling === void 0 ? {} : { sampling: {} }
32
+ };
33
+ }
34
+ /**
35
+ * Register the elicitation / sampling request handlers on `sdkClient`,
36
+ * one per supplied callback. Must be called **before** `connect(...)` so
37
+ * the handlers are in place when the session begins delivering
38
+ * server-initiated requests.
39
+ */
40
+ function registerClientRequestHandlers(sdkClient, opts) {
41
+ const { serverIdRef } = opts;
42
+ if (opts.elicitation !== void 0) {
43
+ const handler = opts.elicitation;
44
+ sdkClient.setRequestHandler(ElicitRequestSchema, async (request, extra) => {
45
+ incrementCounter("mcp.elicitation.requested.total", { server: serverIdRef.current });
46
+ const params = request.params;
47
+ const requestedSchema = "requestedSchema" in params && params.requestedSchema !== void 0 ? params.requestedSchema : {
48
+ type: "object",
49
+ properties: {}
50
+ };
51
+ const result = await handler({
52
+ message: params.message,
53
+ requestedSchema
54
+ }, { signal: extra.signal });
55
+ if (result.action === "accept") {
56
+ incrementCounter("mcp.elicitation.accepted.total", { server: serverIdRef.current });
57
+ return result.content === void 0 ? { action: "accept" } : {
58
+ action: "accept",
59
+ content: result.content
60
+ };
61
+ }
62
+ incrementCounter("mcp.elicitation.declined.total", {
63
+ server: serverIdRef.current,
64
+ action: result.action
65
+ });
66
+ return { action: result.action };
67
+ });
68
+ }
69
+ if (opts.sampling !== void 0) {
70
+ const handler = opts.sampling;
71
+ sdkClient.setRequestHandler(CreateMessageRequestSchema, async (request, extra) => {
72
+ incrementCounter("mcp.sampling.requested.total", { server: serverIdRef.current });
73
+ const p = request.params;
74
+ const mp = p.modelPreferences;
75
+ const result = await handler({
76
+ messages: p.messages.map((m) => ({
77
+ role: m.role,
78
+ content: contentBlocks(m.content).map((block) => toGraphorinContent(block))
79
+ })),
80
+ maxTokens: p.maxTokens,
81
+ ...p.systemPrompt === void 0 ? {} : { systemPrompt: p.systemPrompt },
82
+ ...p.temperature === void 0 ? {} : { temperature: p.temperature },
83
+ ...p.stopSequences === void 0 ? {} : { stopSequences: p.stopSequences },
84
+ ...mp === void 0 ? {} : { modelPreferences: {
85
+ ...mp.hints === void 0 ? {} : { hints: mp.hints.map((h) => h.name === void 0 ? {} : { name: h.name }) },
86
+ ...mp.costPriority === void 0 ? {} : { costPriority: mp.costPriority },
87
+ ...mp.speedPriority === void 0 ? {} : { speedPriority: mp.speedPriority },
88
+ ...mp.intelligencePriority === void 0 ? {} : { intelligencePriority: mp.intelligencePriority }
89
+ } },
90
+ ...p.includeContext === void 0 ? {} : { includeContext: p.includeContext }
91
+ }, { signal: extra.signal });
92
+ incrementCounter("mcp.sampling.completed.total", { server: serverIdRef.current });
93
+ return {
94
+ role: result.role,
95
+ content: result.content,
96
+ model: result.model,
97
+ ...result.stopReason === void 0 ? {} : { stopReason: result.stopReason }
98
+ };
99
+ });
100
+ }
101
+ }
102
+ /** Normalise SDK message content (block | block[]) to a block array (MC-13). */
103
+ function contentBlocks(content) {
104
+ const blocks = (Array.isArray(content) ? content : [content]).filter((b) => b !== null && typeof b === "object");
105
+ return blocks.length > 0 ? blocks : [{
106
+ type: "text",
107
+ text: ""
108
+ }];
109
+ }
110
+ /** Map an SDK content block to the Graphorin sampling-content union. */
111
+ function toGraphorinContent(block) {
112
+ if (block.type === "image") return {
113
+ type: "image",
114
+ data: String(block.data ?? ""),
115
+ mimeType: String(block.mimeType ?? "application/octet-stream")
116
+ };
117
+ if (block.type === "audio") return {
118
+ type: "audio",
119
+ data: String(block.data ?? ""),
120
+ mimeType: String(block.mimeType ?? "application/octet-stream")
121
+ };
122
+ if (block.type === "text") return {
123
+ type: "text",
124
+ text: String(block.text ?? "")
125
+ };
126
+ return {
127
+ type: "text",
128
+ text: JSON.stringify(block)
129
+ };
130
+ }
131
+
132
+ //#endregion
133
+ export { computeClientCapabilities, registerClientRequestHandlers };
134
+ //# sourceMappingURL=client-handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-handlers.js","names":[],"sources":["../../src/client/client-handlers.ts"],"sourcesContent":["/**\n * Client-side request handlers for server-initiated MCP requests\n * (WI-13 / P2-2): **elicitation** (`elicitation/create`) and **sampling**\n * (`sampling/createMessage`).\n *\n * Both are gated: the client advertises a capability and registers a\n * handler *only* when the operator supplies the matching callback on\n * {@link CreateMCPClientOptions}. A conforming server will not issue a\n * request for an un-advertised capability, so the default client is inert\n * (no implicit prompting, no implicit model calls — R4).\n *\n * The SDK request/result schemas are kept inside this module; the public\n * surface speaks only the Graphorin-typed {@link MCPElicitationRequest} /\n * {@link MCPSamplingRequest} boundary.\n *\n * @packageDocumentation\n */\n\nimport { incrementCounter } from '@graphorin/tools/audit';\nimport type { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport {\n type ClientCapabilities,\n CreateMessageRequestSchema,\n ElicitRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport type {\n MCPElicitationHandler,\n MCPSamplingContent,\n MCPSamplingHandler,\n MCPSamplingRequest,\n} from './types.js';\n\n/** Mutable holder so handlers can label counters with the server id. */\nexport interface ServerIdRef {\n current: string;\n}\n\n/** Options for {@link registerClientRequestHandlers}. */\nexport interface ClientRequestHandlerOptions {\n readonly elicitation?: MCPElicitationHandler;\n readonly sampling?: MCPSamplingHandler;\n readonly serverIdRef: ServerIdRef;\n}\n\n/**\n * Compute the {@link ClientCapabilities} to advertise on `initialize`,\n * based on which server-initiated handlers the operator supplied.\n * Returns `undefined` when none are configured (advertise nothing).\n */\nexport function computeClientCapabilities(opts: {\n readonly elicitation?: unknown;\n readonly sampling?: unknown;\n}): ClientCapabilities | undefined {\n if (opts.elicitation === undefined && opts.sampling === undefined) return undefined;\n return {\n ...(opts.elicitation === undefined ? {} : { elicitation: {} }),\n ...(opts.sampling === undefined ? {} : { sampling: {} }),\n };\n}\n\n/**\n * Register the elicitation / sampling request handlers on `sdkClient`,\n * one per supplied callback. Must be called **before** `connect(...)` so\n * the handlers are in place when the session begins delivering\n * server-initiated requests.\n */\nexport function registerClientRequestHandlers(\n sdkClient: Client,\n opts: ClientRequestHandlerOptions,\n): void {\n const { serverIdRef } = opts;\n\n if (opts.elicitation !== undefined) {\n const handler = opts.elicitation;\n sdkClient.setRequestHandler(ElicitRequestSchema, async (request, extra) => {\n incrementCounter('mcp.elicitation.requested.total', { server: serverIdRef.current });\n const params = request.params;\n const requestedSchema =\n 'requestedSchema' in params && params.requestedSchema !== undefined\n ? (params.requestedSchema as Readonly<Record<string, unknown>>)\n : { type: 'object', properties: {} };\n const result = await handler(\n { message: params.message, requestedSchema },\n { signal: extra.signal },\n );\n if (result.action === 'accept') {\n incrementCounter('mcp.elicitation.accepted.total', { server: serverIdRef.current });\n return result.content === undefined\n ? { action: 'accept' }\n : { action: 'accept', content: result.content };\n }\n incrementCounter('mcp.elicitation.declined.total', {\n server: serverIdRef.current,\n action: result.action,\n });\n return { action: result.action };\n });\n }\n\n if (opts.sampling !== undefined) {\n const handler = opts.sampling;\n sdkClient.setRequestHandler(CreateMessageRequestSchema, async (request, extra) => {\n incrementCounter('mcp.sampling.requested.total', { server: serverIdRef.current });\n const p = request.params;\n const mp = p.modelPreferences;\n const samplingRequest: MCPSamplingRequest = {\n messages: p.messages.map((m) => ({\n role: m.role,\n // MC-13: keep EVERY block — a text+image message must reach the\n // operator's handler whole, not truncated to its first block.\n content: contentBlocks(m.content).map((block) => toGraphorinContent(block)),\n })),\n maxTokens: p.maxTokens,\n ...(p.systemPrompt === undefined ? {} : { systemPrompt: p.systemPrompt }),\n ...(p.temperature === undefined ? {} : { temperature: p.temperature }),\n ...(p.stopSequences === undefined ? {} : { stopSequences: p.stopSequences }),\n ...(mp === undefined\n ? {}\n : {\n modelPreferences: {\n ...(mp.hints === undefined\n ? {}\n : { hints: mp.hints.map((h) => (h.name === undefined ? {} : { name: h.name })) }),\n ...(mp.costPriority === undefined ? {} : { costPriority: mp.costPriority }),\n ...(mp.speedPriority === undefined ? {} : { speedPriority: mp.speedPriority }),\n ...(mp.intelligencePriority === undefined\n ? {}\n : { intelligencePriority: mp.intelligencePriority }),\n },\n }),\n ...(p.includeContext === undefined ? {} : { includeContext: p.includeContext }),\n };\n const result = await handler(samplingRequest, { signal: extra.signal });\n incrementCounter('mcp.sampling.completed.total', { server: serverIdRef.current });\n return {\n role: result.role,\n content: result.content,\n model: result.model,\n ...(result.stopReason === undefined ? {} : { stopReason: result.stopReason }),\n };\n });\n }\n}\n\n/** Normalise SDK message content (block | block[]) to a block array (MC-13). */\nfunction contentBlocks(\n content: unknown,\n): ReadonlyArray<{ readonly type?: unknown; [k: string]: unknown }> {\n const raw = Array.isArray(content) ? content : [content];\n const blocks = raw.filter(\n (b): b is { readonly type?: unknown; [k: string]: unknown } =>\n b !== null && typeof b === 'object',\n );\n return blocks.length > 0 ? blocks : [{ type: 'text', text: '' }];\n}\n\n/** Map an SDK content block to the Graphorin sampling-content union. */\nfunction toGraphorinContent(block: {\n readonly type?: unknown;\n [k: string]: unknown;\n}): MCPSamplingContent {\n if (block.type === 'image') {\n return {\n type: 'image',\n data: String(block.data ?? ''),\n mimeType: String(block.mimeType ?? 'application/octet-stream'),\n };\n }\n if (block.type === 'audio') {\n return {\n type: 'audio',\n data: String(block.data ?? ''),\n mimeType: String(block.mimeType ?? 'application/octet-stream'),\n };\n }\n if (block.type === 'text') {\n return { type: 'text', text: String(block.text ?? '') };\n }\n return { type: 'text', text: JSON.stringify(block) };\n}\n"],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { ServerIdentity } from "../transport/types.js";
2
+ import { CreateMCPClientOptions, MCPClient } from "./types.js";
3
+ import { CollisionStrategy } from "@graphorin/tools/registry";
4
+
5
+ //#region src/client/client.d.ts
6
+
7
+ /**
8
+ * Reset the SSE WARN dedup flag. Used by tests.
9
+ *
10
+ * @experimental
11
+ */
12
+ declare function _resetSseWarnDedupForTesting(): void;
13
+ /**
14
+ * Open a typed MCP client connection.
15
+ *
16
+ * @stable
17
+ */
18
+ declare function createMCPClient(options: CreateMCPClientOptions): Promise<MCPClient>;
19
+ //#endregion
20
+ export { _resetSseWarnDedupForTesting, createMCPClient };
21
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","names":[],"sources":["../../src/client/client.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;iBAsEgB,4BAAA,CAAA;;;;;;iBASM,eAAA,UAAyB,yBAAyB,QAAQ"}
@@ -0,0 +1,358 @@
1
+ import { MCPCallTimeoutError, MCPCancelledError, MCPConnectionError, MCPInvalidConfigError, MCPProtocolError, MCPToolNotFoundError, MCPToolPinningError } from "../errors/index.js";
2
+ import { deriveServerIdentity } from "../helpers/identity.js";
3
+ import { validateMCPServerConfig } from "../helpers/validate-config.js";
4
+ import { computeClientCapabilities, registerClientRequestHandlers } from "./client-handlers.js";
5
+ import { adaptMCPTools } from "./to-tools.js";
6
+ import { buildTransport } from "./transport-factory.js";
7
+ import { incrementCounter } from "@graphorin/tools/audit";
8
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
9
+ import { ToolListChangedNotificationSchema } from "@modelcontextprotocol/sdk/types.js";
10
+
11
+ //#region src/client/client.ts
12
+ const DEFAULT_CLIENT_NAME = "graphorin-mcp-client";
13
+ const DEFAULT_CLIENT_VERSION = "0.5.0";
14
+ /**
15
+ * Process-scoped dedup flag for the deprecated SSE transport WARN.
16
+ * Once set, subsequent {@link createMCPClient} calls with the SSE
17
+ * transport do not re-emit the warning. Tests reset via
18
+ * {@link _resetSseWarnDedupForTesting}.
19
+ */
20
+ let sseWarnEmittedThisProcess = false;
21
+ /**
22
+ * Reset the SSE WARN dedup flag. Used by tests.
23
+ *
24
+ * @experimental
25
+ */
26
+ function _resetSseWarnDedupForTesting() {
27
+ sseWarnEmittedThisProcess = false;
28
+ }
29
+ /**
30
+ * Open a typed MCP client connection.
31
+ *
32
+ * @stable
33
+ */
34
+ async function createMCPClient(options) {
35
+ validateMCPServerConfig({ transport: options.transport });
36
+ if (options.authProvider !== void 0 && options.bearerToken !== void 0) throw new MCPInvalidConfigError("`authProvider` and `bearerToken` are mutually exclusive; supply at most one.", { metadata: { transport: options.transport.kind } });
37
+ const auth = resolveTransportAuth(options);
38
+ if (auth !== void 0 && options.transport.kind === "stdio") throw new MCPInvalidConfigError("authProvider / bearerToken require an HTTP transport (streamable-http or sse); the stdio transport carries no Authorization header.", { metadata: { transport: "stdio" } });
39
+ if (options.transport.kind === "sse" && options.suppressDeprecatedTransportWarning !== true && !sseWarnEmittedThisProcess) {
40
+ sseWarnEmittedThisProcess = true;
41
+ if (options.logger !== void 0) options.logger("warn", "MCP SSE transport is deprecated; migrate the server to the streamable-http transport when possible.");
42
+ incrementCounter("mcp.transport.deprecated.warn.total", { transport: "sse" });
43
+ }
44
+ return createMCPClientFromSdkTransport({
45
+ transport: buildTransport(options.transport, auth === void 0 ? void 0 : { auth }).transport,
46
+ transportConfig: options.transport,
47
+ ...options.collisionStrategy === void 0 ? {} : { collisionStrategy: options.collisionStrategy },
48
+ ...options.priority === void 0 ? {} : { priority: options.priority },
49
+ ...options.serverInfoName === void 0 ? {} : { serverInfoName: options.serverInfoName },
50
+ ...options.logger === void 0 ? {} : { logger: options.logger },
51
+ ...options.clientName === void 0 ? {} : { clientName: options.clientName },
52
+ ...options.clientVersion === void 0 ? {} : { clientVersion: options.clientVersion },
53
+ ...options.elicitation === void 0 ? {} : { elicitation: options.elicitation },
54
+ ...options.sampling === void 0 ? {} : { sampling: options.sampling }
55
+ });
56
+ }
57
+ /**
58
+ * Build an {@link MCPClient} from a pre-built SDK transport. The
59
+ * production {@link createMCPClient} delegates here after building
60
+ * the SDK transport from a {@link MCPTransportConfig}.
61
+ *
62
+ * @internal
63
+ */
64
+ async function createMCPClientFromSdkTransport(options) {
65
+ const sdkClient = new Client({
66
+ name: options.clientName ?? DEFAULT_CLIENT_NAME,
67
+ version: options.clientVersion ?? DEFAULT_CLIENT_VERSION
68
+ });
69
+ const clientCapabilities = computeClientCapabilities({
70
+ elicitation: options.elicitation,
71
+ sampling: options.sampling
72
+ });
73
+ if (clientCapabilities !== void 0) sdkClient.registerCapabilities(clientCapabilities);
74
+ const serverIdRef = { current: "unknown" };
75
+ registerClientRequestHandlers(sdkClient, {
76
+ ...options.elicitation === void 0 ? {} : { elicitation: options.elicitation },
77
+ ...options.sampling === void 0 ? {} : { sampling: options.sampling },
78
+ serverIdRef
79
+ });
80
+ sdkClient.setNotificationHandler(ToolListChangedNotificationSchema, async () => {
81
+ incrementCounter("mcp.tools.list-changed.total", { server: serverIdRef.current ?? "unknown" });
82
+ options.logger?.("warn", "mcp.tools.list_changed received — re-run toTools() to refresh + re-sanitize the catalogue", { server: serverIdRef.current });
83
+ });
84
+ try {
85
+ await sdkClient.connect(options.transport);
86
+ } catch (cause) {
87
+ throw new MCPConnectionError(`MCP transport could not be established: ${cause.message ?? String(cause)}`, {
88
+ metadata: { transport: options.transportConfig.kind },
89
+ cause
90
+ });
91
+ }
92
+ const serverInfo = sdkClient.getServerVersion() ?? {
93
+ name: "unknown",
94
+ version: "0.0.0"
95
+ };
96
+ const serverIdentity = deriveServerIdentity(options.transportConfig, options.serverInfoName ?? serverInfo.name);
97
+ serverIdRef.current = serverIdentity.id;
98
+ const collisionStrategy = options.collisionStrategy ?? "auto-prefix";
99
+ if (options.eventStore !== void 0) options.logger?.("warn", "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.", { server: serverIdentity.id });
100
+ const sessionIdPresent = isStreamableHttp(options.transport) && options.transport.sessionId !== void 0;
101
+ const resumable = sessionIdPresent;
102
+ if (options.logger !== void 0) options.logger("info", "mcp.session.session-id.resolved", {
103
+ server: serverIdentity.id,
104
+ value: sessionIdPresent,
105
+ source: sessionIdPresent ? "session-id-present" : "transport-default"
106
+ });
107
+ let structuredContentSeenLogged = false;
108
+ async function listTools(opts) {
109
+ const requestOptions = opts?.signal === void 0 ? {} : { signal: opts.signal };
110
+ let result;
111
+ try {
112
+ result = await sdkClient.listTools({}, requestOptions);
113
+ } catch (cause) {
114
+ throw mapSdkError(cause, {});
115
+ }
116
+ const tools = result.tools ?? [];
117
+ if (!structuredContentSeenLogged && tools.some((t) => t.outputSchema !== void 0)) {
118
+ structuredContentSeenLogged = true;
119
+ if (options.logger !== void 0) options.logger("info", "mcp.server.structured-content.detected", { server: serverIdentity.id });
120
+ }
121
+ return Object.freeze(tools.map((t) => Object.freeze({
122
+ name: t.name,
123
+ description: t.description ?? "",
124
+ inputSchema: t.inputSchema,
125
+ ...t.outputSchema === void 0 ? {} : { outputSchema: t.outputSchema },
126
+ ...t.title === void 0 ? {} : { title: t.title }
127
+ })));
128
+ }
129
+ async function listResources(opts) {
130
+ const requestOptions = opts?.signal === void 0 ? {} : { signal: opts.signal };
131
+ let result;
132
+ try {
133
+ result = await sdkClient.listResources({}, requestOptions);
134
+ } catch (cause) {
135
+ throw mapSdkError(cause, {});
136
+ }
137
+ const items = result.resources ?? [];
138
+ return Object.freeze(items.map((r) => Object.freeze({
139
+ uri: r.uri,
140
+ ...r.name === void 0 ? {} : { name: r.name },
141
+ ...r.description === void 0 ? {} : { description: r.description },
142
+ ...r.mimeType === void 0 ? {} : { mimeType: r.mimeType }
143
+ })));
144
+ }
145
+ async function listPrompts(opts) {
146
+ const requestOptions = opts?.signal === void 0 ? {} : { signal: opts.signal };
147
+ let result;
148
+ try {
149
+ result = await sdkClient.listPrompts({}, requestOptions);
150
+ } catch (cause) {
151
+ throw mapSdkError(cause, {});
152
+ }
153
+ const items = result.prompts ?? [];
154
+ return Object.freeze(items.map((p) => Object.freeze({
155
+ name: p.name,
156
+ ...p.description === void 0 ? {} : { description: p.description },
157
+ ...p.arguments === void 0 ? {} : { arguments: Object.freeze(p.arguments.map((a) => Object.freeze({
158
+ name: a.name,
159
+ ...a.description === void 0 ? {} : { description: a.description },
160
+ ...a.required === void 0 ? {} : { required: a.required }
161
+ }))) }
162
+ })));
163
+ }
164
+ async function callTool(name, args, opts) {
165
+ const requestOptions = {
166
+ ...opts?.signal === void 0 ? {} : { signal: opts.signal },
167
+ ...opts?.timeoutMs === void 0 ? {} : {
168
+ timeout: opts.timeoutMs,
169
+ maxTotalTimeout: opts.timeoutMs
170
+ }
171
+ };
172
+ incrementCounter("mcp.call.invoked.total", {
173
+ server: serverIdentity.id,
174
+ tool: name
175
+ });
176
+ let result;
177
+ try {
178
+ result = await sdkClient.callTool({
179
+ name,
180
+ arguments: args
181
+ }, void 0, requestOptions);
182
+ } catch (cause) {
183
+ const mapped = mapSdkError(cause, { tool: name });
184
+ if (mapped instanceof MCPCancelledError) incrementCounter("mcp.call.cancelled.total", {
185
+ server: serverIdentity.id,
186
+ tool: name
187
+ });
188
+ else incrementCounter("mcp.call.failed.total", {
189
+ server: serverIdentity.id,
190
+ tool: name
191
+ });
192
+ throw mapped;
193
+ }
194
+ const content = result.content ?? [];
195
+ return Object.freeze({
196
+ content: Object.freeze([...content]),
197
+ ...result.structuredContent === void 0 ? {} : { structuredContent: result.structuredContent },
198
+ ...result.isError === void 0 ? {} : { isError: Boolean(result.isError) }
199
+ });
200
+ }
201
+ async function readResource(uri, opts) {
202
+ const requestOptions = opts?.signal === void 0 ? {} : { signal: opts.signal };
203
+ let result;
204
+ try {
205
+ result = await sdkClient.readResource({ uri }, requestOptions);
206
+ } catch (cause) {
207
+ throw mapSdkError(cause, {});
208
+ }
209
+ const first = (result.contents ?? [])[0];
210
+ if (first === void 0) throw new MCPProtocolError(`MCP server returned no contents for resource '${uri}'.`, { metadata: { server: serverIdentity.id } });
211
+ return Object.freeze(first);
212
+ }
213
+ async function getPrompt(name, args, opts) {
214
+ const requestOptions = opts?.signal === void 0 ? {} : { signal: opts.signal };
215
+ let result;
216
+ try {
217
+ result = await sdkClient.getPrompt({
218
+ name,
219
+ ...args === void 0 ? {} : { arguments: args }
220
+ }, requestOptions);
221
+ } catch (cause) {
222
+ throw mapSdkError(cause, {});
223
+ }
224
+ const messages = (result.messages ?? []).map((m) => Object.freeze({
225
+ role: m.role,
226
+ content: m.content
227
+ }));
228
+ return Object.freeze({ messages: Object.freeze(messages) });
229
+ }
230
+ let lastToolFingerprints;
231
+ async function toTools(toolsOpts) {
232
+ const adapted = adaptMCPTools({
233
+ client: clientApi,
234
+ serverIdentity,
235
+ catalogue: await listTools(),
236
+ ...toolsOpts === void 0 ? {} : { options: toolsOpts },
237
+ ...options.logger === void 0 ? {} : { logger: options.logger }
238
+ });
239
+ if (lastToolFingerprints !== void 0) for (const [name, hash] of adapted.fingerprints) {
240
+ const previous = lastToolFingerprints.get(name);
241
+ if (previous !== void 0 && previous !== hash) {
242
+ incrementCounter("mcp.tools.changed.total", {
243
+ server: serverIdentity.id,
244
+ tool: name
245
+ });
246
+ options.logger?.("warn", "mcp.tools.changed: definition drifted between snapshots", {
247
+ server: serverIdentity.id,
248
+ tool: name,
249
+ previous,
250
+ current: hash
251
+ });
252
+ }
253
+ }
254
+ lastToolFingerprints = adapted.fingerprints;
255
+ const pins = toolsOpts?.pinnedFingerprints;
256
+ if (pins !== void 0) for (const [name, pinned] of Object.entries(pins)) {
257
+ const current = adapted.fingerprints.get(name);
258
+ if (current !== void 0 && current !== pinned) {
259
+ if (toolsOpts?.onPinMismatch === "reject") throw new MCPToolPinningError(`MCP tool '${name}' no longer matches its pinned definition fingerprint — the server changed the definition behind an approved name.`, { metadata: {
260
+ server: serverIdentity.id,
261
+ tool: name
262
+ } });
263
+ incrementCounter("mcp.tools.pin-mismatch.total", {
264
+ server: serverIdentity.id,
265
+ tool: name
266
+ });
267
+ options.logger?.("warn", "mcp.tools.pin-mismatch: pinned fingerprint diverged", {
268
+ server: serverIdentity.id,
269
+ tool: name
270
+ });
271
+ }
272
+ }
273
+ return adapted.tools;
274
+ }
275
+ async function close() {
276
+ try {
277
+ await sdkClient.close();
278
+ } catch (cause) {
279
+ if (cause instanceof Error && cause.message.toLowerCase().includes("already")) return;
280
+ throw new MCPConnectionError("MCP transport could not be closed cleanly.", {
281
+ metadata: {
282
+ transport: options.transportConfig.kind,
283
+ server: serverIdentity.id
284
+ },
285
+ cause
286
+ });
287
+ }
288
+ }
289
+ const clientApi = Object.freeze({
290
+ id: serverIdentity.id,
291
+ serverInfo,
292
+ serverIdentity,
293
+ collisionStrategy,
294
+ ...options.priority === void 0 ? {} : { priority: options.priority },
295
+ sessionIdPresent,
296
+ resumable,
297
+ listTools,
298
+ listResources,
299
+ listPrompts,
300
+ callTool,
301
+ readResource,
302
+ getPrompt,
303
+ toTools,
304
+ close
305
+ });
306
+ return clientApi;
307
+ }
308
+ /**
309
+ * Resolve the live {@link TransportAuthSource} for the outbound
310
+ * `Authorization` header from the mutually-exclusive `authProvider` /
311
+ * `bearerToken` options. `authProvider.resolveHeader()` already returns
312
+ * the full header value (`Bearer …`); a static `bearerToken` is wrapped
313
+ * into a constant `Bearer`-prefixed resolver. Returns `undefined` when
314
+ * neither is supplied (no header injection).
315
+ */
316
+ function resolveTransportAuth(options) {
317
+ const provider = options.authProvider;
318
+ if (provider !== void 0) return { resolveHeader: () => provider.resolveHeader() };
319
+ if (options.bearerToken !== void 0) {
320
+ const token = options.bearerToken;
321
+ const header = /^bearer\s/i.test(token) ? token : `Bearer ${token}`;
322
+ return { resolveHeader: () => header };
323
+ }
324
+ }
325
+ function mapSdkError(cause, ctx) {
326
+ const metadata = ctx.tool === void 0 ? {} : { tool: ctx.tool };
327
+ if (cause instanceof Error) {
328
+ const name = cause.name;
329
+ const message = cause.message ?? "";
330
+ if (name === "AbortError" || /aborted|cancell/i.test(message)) return new MCPCancelledError("MCP request was cancelled.", {
331
+ metadata,
332
+ cause
333
+ });
334
+ if (/request timed out|timed out/i.test(message)) return new MCPCallTimeoutError(`MCP request timed out${ctx.tool ? ` (tool: ${ctx.tool})` : ""}.`, {
335
+ metadata,
336
+ cause
337
+ });
338
+ if (/unknown\s+tool|tool\s+not\s+found|method\s+not\s+found/i.test(message)) return new MCPToolNotFoundError(`MCP tool not found${ctx.tool ? `: ${ctx.tool}` : ""}.`, {
339
+ metadata,
340
+ cause
341
+ });
342
+ return new MCPProtocolError(message.length === 0 ? cause.toString() : message, {
343
+ metadata,
344
+ cause
345
+ });
346
+ }
347
+ return new MCPProtocolError(`MCP request failed: ${String(cause)}`, {
348
+ metadata,
349
+ cause
350
+ });
351
+ }
352
+ function isStreamableHttp(transport) {
353
+ return typeof transport === "object" && transport !== null && transport.constructor !== void 0 && transport.constructor.name === "StreamableHTTPClientTransport";
354
+ }
355
+
356
+ //#endregion
357
+ export { _resetSseWarnDedupForTesting, createMCPClient };
358
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":["collisionStrategy: CollisionStrategy","result: Awaited<ReturnType<typeof sdkClient.listTools>>","result: Awaited<ReturnType<typeof sdkClient.listResources>>","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","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.5.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 });\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}\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 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 async function listTools(opts?: {\n signal?: AbortSignal;\n }): Promise<ReadonlyArray<MCPToolDefinition>> {\n const requestOptions = opts?.signal === undefined ? {} : { signal: opts.signal };\n let result: Awaited<ReturnType<typeof sdkClient.listTools>>;\n try {\n result = await sdkClient.listTools({}, requestOptions);\n } catch (cause) {\n throw mapSdkError(cause, {});\n }\n const tools = (result.tools ?? []) as ReadonlyArray<{\n name: string;\n description?: string;\n inputSchema: Readonly<Record<string, unknown>>;\n outputSchema?: Readonly<Record<string, unknown>>;\n title?: string;\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 let result: Awaited<ReturnType<typeof sdkClient.listResources>>;\n try {\n result = await sdkClient.listResources({}, requestOptions);\n } catch (cause) {\n throw mapSdkError(cause, {});\n }\n const items = (result.resources ?? []) as ReadonlyArray<{\n uri: string;\n name?: string;\n description?: string;\n mimeType?: string;\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 let result: Awaited<ReturnType<typeof sdkClient.listPrompts>>;\n try {\n result = await sdkClient.listPrompts({}, requestOptions);\n } catch (cause) {\n throw mapSdkError(cause, {});\n }\n const items = (result.prompts ?? []) as ReadonlyArray<{\n name: string;\n description?: string;\n arguments?: ReadonlyArray<{ name: string; description?: string; required?: boolean }>;\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 readResource(\n uri: string,\n opts?: { signal?: AbortSignal },\n ): Promise<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 first = ((result.contents ?? []) as ReadonlyArray<MCPResourceContent>)[0];\n if (first === undefined) {\n throw new MCPProtocolError(`MCP server returned no contents for resource '${uri}'.`, {\n metadata: { server: serverIdentity.id },\n });\n }\n return Object.freeze(first);\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.\n const pins = toolsOpts?.pinnedFingerprints;\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 (toolsOpts?.onPinMismatch === '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 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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"}
@@ -0,0 +1,7 @@
1
+ //#region src/client/defer-loading.d.ts
2
+
3
+ /** Default auto-deferral threshold per the operator-facing convention. */
4
+ declare const DEFAULT_DEFER_LOADING_THRESHOLD = 10;
5
+ //#endregion
6
+ export { DEFAULT_DEFER_LOADING_THRESHOLD };
7
+ //# sourceMappingURL=defer-loading.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defer-loading.d.ts","names":[],"sources":["../../src/client/defer-loading.ts"],"sourcesContent":[],"mappings":";;;cAiBa,+BAAA"}