@mcp-z/client 1.2.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/cjs/auth/capability-discovery.js +5 -1
  2. package/dist/cjs/auth/capability-discovery.js.map +1 -1
  3. package/dist/cjs/auth/interactive-oauth-flow.d.cts +9 -2
  4. package/dist/cjs/auth/interactive-oauth-flow.d.ts +9 -2
  5. package/dist/cjs/auth/interactive-oauth-flow.js +29 -11
  6. package/dist/cjs/auth/interactive-oauth-flow.js.map +1 -1
  7. package/dist/cjs/auth/oauth-callback-listener.js +4 -0
  8. package/dist/cjs/auth/oauth-callback-listener.js.map +1 -1
  9. package/dist/cjs/auth/types.d.cts +18 -4
  10. package/dist/cjs/auth/types.d.ts +18 -4
  11. package/dist/cjs/auth/types.js.map +1 -1
  12. package/dist/cjs/client-helpers.d.cts +5 -6
  13. package/dist/cjs/client-helpers.d.ts +5 -6
  14. package/dist/cjs/client-helpers.js +8 -8
  15. package/dist/cjs/client-helpers.js.map +1 -1
  16. package/dist/cjs/connection/connect-client.d.cts +1 -2
  17. package/dist/cjs/connection/connect-client.d.ts +1 -2
  18. package/dist/cjs/connection/connect-client.js +6 -9
  19. package/dist/cjs/connection/connect-client.js.map +1 -1
  20. package/dist/cjs/connection/existing-process-transport.d.cts +1 -2
  21. package/dist/cjs/connection/existing-process-transport.d.ts +1 -2
  22. package/dist/cjs/connection/existing-process-transport.js +3 -3
  23. package/dist/cjs/connection/existing-process-transport.js.map +1 -1
  24. package/dist/cjs/dcr/dcr-authenticator.d.cts +6 -1
  25. package/dist/cjs/dcr/dcr-authenticator.d.ts +6 -1
  26. package/dist/cjs/dcr/dcr-authenticator.js +423 -40
  27. package/dist/cjs/dcr/dcr-authenticator.js.map +1 -1
  28. package/dist/cjs/dcr/dynamic-client-registrar.d.cts +2 -2
  29. package/dist/cjs/dcr/dynamic-client-registrar.d.ts +2 -2
  30. package/dist/cjs/dcr/dynamic-client-registrar.js +10 -11
  31. package/dist/cjs/dcr/dynamic-client-registrar.js.map +1 -1
  32. package/dist/cjs/lib/url-utils.js +2 -2
  33. package/dist/cjs/lib/url-utils.js.map +1 -1
  34. package/dist/cjs/response-wrappers.d.cts +1 -1
  35. package/dist/cjs/response-wrappers.d.ts +1 -1
  36. package/dist/cjs/response-wrappers.js.map +1 -1
  37. package/dist/cjs/search/search.d.cts +1 -1
  38. package/dist/cjs/search/search.d.ts +1 -1
  39. package/dist/cjs/search/search.js.map +1 -1
  40. package/dist/esm/auth/capability-discovery.js +5 -1
  41. package/dist/esm/auth/capability-discovery.js.map +1 -1
  42. package/dist/esm/auth/interactive-oauth-flow.d.ts +9 -2
  43. package/dist/esm/auth/interactive-oauth-flow.js +28 -10
  44. package/dist/esm/auth/interactive-oauth-flow.js.map +1 -1
  45. package/dist/esm/auth/oauth-callback-listener.js +4 -0
  46. package/dist/esm/auth/oauth-callback-listener.js.map +1 -1
  47. package/dist/esm/auth/types.d.ts +18 -4
  48. package/dist/esm/auth/types.js.map +1 -1
  49. package/dist/esm/client-helpers.d.ts +5 -6
  50. package/dist/esm/client-helpers.js +8 -8
  51. package/dist/esm/client-helpers.js.map +1 -1
  52. package/dist/esm/connection/connect-client.d.ts +1 -2
  53. package/dist/esm/connection/connect-client.js +2 -5
  54. package/dist/esm/connection/connect-client.js.map +1 -1
  55. package/dist/esm/connection/existing-process-transport.d.ts +1 -2
  56. package/dist/esm/connection/existing-process-transport.js +1 -1
  57. package/dist/esm/connection/existing-process-transport.js.map +1 -1
  58. package/dist/esm/dcr/dcr-authenticator.d.ts +6 -1
  59. package/dist/esm/dcr/dcr-authenticator.js +79 -35
  60. package/dist/esm/dcr/dcr-authenticator.js.map +1 -1
  61. package/dist/esm/dcr/dynamic-client-registrar.d.ts +2 -2
  62. package/dist/esm/dcr/dynamic-client-registrar.js +7 -6
  63. package/dist/esm/dcr/dynamic-client-registrar.js.map +1 -1
  64. package/dist/esm/lib/url-utils.js +2 -2
  65. package/dist/esm/lib/url-utils.js.map +1 -1
  66. package/dist/esm/response-wrappers.d.ts +1 -1
  67. package/dist/esm/response-wrappers.js.map +1 -1
  68. package/dist/esm/search/search.d.ts +1 -1
  69. package/dist/esm/search/search.js.map +1 -1
  70. package/package.json +4 -3
  71. package/dist/cjs/monkey-patches.d.cts +0 -6
  72. package/dist/cjs/monkey-patches.d.ts +0 -6
  73. package/dist/cjs/monkey-patches.js +0 -233
  74. package/dist/cjs/monkey-patches.js.map +0 -1
  75. package/dist/esm/monkey-patches.d.ts +0 -6
  76. package/dist/esm/monkey-patches.js +0 -32
  77. package/dist/esm/monkey-patches.js.map +0 -1
@@ -4,8 +4,7 @@
4
4
  * MCP transport that wraps an existing child process for stdio communication.
5
5
  * Used when connecting to servers already spawned by initServers().
6
6
  */
7
- import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
8
- import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
7
+ import type { JSONRPCMessage, Transport } from '@modelcontextprotocol/client';
9
8
  import type { ChildProcess } from 'child_process';
10
9
  /**
11
10
  * Transport that communicates with an existing child process via stdio.
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * MCP transport that wraps an existing child process for stdio communication.
5
5
  * Used when connecting to servers already spawned by initServers().
6
- */ import { ReadBuffer, serializeMessage } from '@modelcontextprotocol/sdk/shared/stdio.js';
6
+ */ import { ReadBuffer, serializeMessage } from '@modelcontextprotocol/client';
7
7
  /**
8
8
  * Transport that communicates with an existing child process via stdio.
9
9
  * Does NOT spawn a new process - uses the one provided.
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/connection/existing-process-transport.ts"],"sourcesContent":["/**\n * existing-process-transport.ts\n *\n * MCP transport that wraps an existing child process for stdio communication.\n * Used when connecting to servers already spawned by initServers().\n */\n\nimport { ReadBuffer, serializeMessage } from '@modelcontextprotocol/sdk/shared/stdio.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';\nimport type { ChildProcess } from 'child_process';\n\n/**\n * Transport that communicates with an existing child process via stdio.\n * Does NOT spawn a new process - uses the one provided.\n */\nexport class ExistingProcessTransport implements Transport {\n private _process: ChildProcess;\n private _readBuffer: ReadBuffer;\n private _dataHandler: ((chunk: Buffer) => void) | null = null;\n private _errorHandler: ((error: Error) => void) | null = null;\n\n // Transport interface callbacks\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(process: ChildProcess) {\n if (!process.stdin || !process.stdout) {\n throw new Error('Child process must have stdin and stdout pipes');\n }\n\n this._process = process;\n this._readBuffer = new ReadBuffer();\n }\n\n /**\n * Start the transport - sets up stdio listeners on existing process.\n */\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n // Listen for process events\n this._process.on('error', (error) => {\n this.onerror?.(error);\n reject(error);\n });\n\n this._process.on('close', () => {\n this.onclose?.();\n });\n\n // Create and save data handler for close\n this._dataHandler = (chunk: Buffer) => {\n this._readBuffer.append(chunk);\n this.processReadBuffer();\n };\n\n // Create and save error handler for close\n this._errorHandler = (error: Error) => {\n this.onerror?.(error);\n };\n\n // Listen for stdout data (MCP messages)\n this._process.stdout?.on('data', this._dataHandler);\n this._process.stdout?.on('error', this._errorHandler);\n this._process.stdin?.on('error', this._errorHandler);\n\n // Process is already running - resolve immediately\n resolve();\n });\n }\n\n /**\n * Process buffered messages from stdout.\n */\n private processReadBuffer(): void {\n while (true) {\n try {\n const message = this._readBuffer.readMessage();\n if (message === null) {\n break;\n }\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(error as Error);\n }\n }\n }\n\n /**\n * Close the transport - close without killing the shared process.\n * The process is managed by the cluster and may have other active connections.\n */\n async close(): Promise<void> {\n if (this._dataHandler) {\n this._process.stdout?.off('data', this._dataHandler);\n this._dataHandler = null;\n }\n\n if (this._errorHandler) {\n this._process.stdout?.off('error', this._errorHandler);\n this._process.stdin?.off('error', this._errorHandler);\n this._errorHandler = null;\n }\n\n this._readBuffer.clear();\n }\n\n /**\n * Send a message to the server via stdin.\n */\n async send(message: JSONRPCMessage): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this._process.stdin) {\n reject(new Error('stdin is not available'));\n return;\n }\n\n const json = serializeMessage(message);\n\n if (this._process.stdin.write(json)) {\n resolve();\n } else {\n this._process.stdin.once('drain', resolve);\n }\n });\n }\n}\n"],"names":["ReadBuffer","serializeMessage","ExistingProcessTransport","start","Promise","resolve","reject","_process","on","error","onerror","onclose","_dataHandler","chunk","_readBuffer","append","processReadBuffer","_errorHandler","stdout","stdin","message","readMessage","onmessage","close","off","clear","send","Error","json","write","once","process"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/connection/existing-process-transport.ts"],"sourcesContent":["/**\n * existing-process-transport.ts\n *\n * MCP transport that wraps an existing child process for stdio communication.\n * Used when connecting to servers already spawned by initServers().\n */\n\nimport type { JSONRPCMessage, Transport } from '@modelcontextprotocol/client';\nimport { ReadBuffer, serializeMessage } from '@modelcontextprotocol/client';\nimport type { ChildProcess } from 'child_process';\n\n/**\n * Transport that communicates with an existing child process via stdio.\n * Does NOT spawn a new process - uses the one provided.\n */\nexport class ExistingProcessTransport implements Transport {\n private _process: ChildProcess;\n private _readBuffer: ReadBuffer;\n private _dataHandler: ((chunk: Buffer) => void) | null = null;\n private _errorHandler: ((error: Error) => void) | null = null;\n\n // Transport interface callbacks\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(process: ChildProcess) {\n if (!process.stdin || !process.stdout) {\n throw new Error('Child process must have stdin and stdout pipes');\n }\n\n this._process = process;\n this._readBuffer = new ReadBuffer();\n }\n\n /**\n * Start the transport - sets up stdio listeners on existing process.\n */\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n // Listen for process events\n this._process.on('error', (error) => {\n this.onerror?.(error);\n reject(error);\n });\n\n this._process.on('close', () => {\n this.onclose?.();\n });\n\n // Create and save data handler for close\n this._dataHandler = (chunk: Buffer) => {\n this._readBuffer.append(chunk);\n this.processReadBuffer();\n };\n\n // Create and save error handler for close\n this._errorHandler = (error: Error) => {\n this.onerror?.(error);\n };\n\n // Listen for stdout data (MCP messages)\n this._process.stdout?.on('data', this._dataHandler);\n this._process.stdout?.on('error', this._errorHandler);\n this._process.stdin?.on('error', this._errorHandler);\n\n // Process is already running - resolve immediately\n resolve();\n });\n }\n\n /**\n * Process buffered messages from stdout.\n */\n private processReadBuffer(): void {\n while (true) {\n try {\n const message = this._readBuffer.readMessage();\n if (message === null) {\n break;\n }\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(error as Error);\n }\n }\n }\n\n /**\n * Close the transport - close without killing the shared process.\n * The process is managed by the cluster and may have other active connections.\n */\n async close(): Promise<void> {\n if (this._dataHandler) {\n this._process.stdout?.off('data', this._dataHandler);\n this._dataHandler = null;\n }\n\n if (this._errorHandler) {\n this._process.stdout?.off('error', this._errorHandler);\n this._process.stdin?.off('error', this._errorHandler);\n this._errorHandler = null;\n }\n\n this._readBuffer.clear();\n }\n\n /**\n * Send a message to the server via stdin.\n */\n async send(message: JSONRPCMessage): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this._process.stdin) {\n reject(new Error('stdin is not available'));\n return;\n }\n\n const json = serializeMessage(message);\n\n if (this._process.stdin.write(json)) {\n resolve();\n } else {\n this._process.stdin.once('drain', resolve);\n }\n });\n }\n}\n"],"names":["ReadBuffer","serializeMessage","ExistingProcessTransport","start","Promise","resolve","reject","_process","on","error","onerror","onclose","_dataHandler","chunk","_readBuffer","append","processReadBuffer","_errorHandler","stdout","stdin","message","readMessage","onmessage","close","off","clear","send","Error","json","write","once","process"],"mappings":"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"}
@@ -62,11 +62,16 @@ export declare class DcrAuthenticator {
62
62
  private ensureAuthenticatedExternal;
63
63
  /**
64
64
  * Refreshes an access token using a refresh token.
65
+ * @param resource - Canonical resource server URI, audience-binding the token (RFC 8707).
65
66
  * @param allowLoopback - Loopback trust grant computed from the server actually being talked to (SSRF mitigation, see discovery-fetch.ts). Defaults to `false`.
66
67
  */
67
68
  private refreshTokens;
68
69
  /**
69
- * Delete stored tokens for a server
70
+ * Deletes both stored token families for a server, across every issuer they were bound to.
71
+ * @throws CredentialBindingError if the configured store cannot enumerate keys.
70
72
  */
71
73
  deleteTokens(baseUrl: string): Promise<void>;
74
+ /** Discards a stored credential that is not bound to `issuer` (SEP-2352), including one stored before issuer binding existed. */
75
+ private loadTokens;
76
+ private buildFlowOptions;
72
77
  }
@@ -7,11 +7,27 @@ import Keyv from 'keyv';
7
7
  import { KeyvFile } from 'keyv-file';
8
8
  import { isLoopbackUrl } from '../auth/discovery-fetch.js';
9
9
  import { InteractiveOAuthFlow } from '../auth/interactive-oauth-flow.js';
10
+ import { normalizeUrl } from '../lib/url-utils.js';
10
11
  import { logger as defaultLogger } from '../utils/logger.js';
11
12
  import { DynamicClientRegistrar } from './dynamic-client-registrar.js';
12
13
  /**
13
14
  * Buffer time before token expiry to trigger proactive refresh (5 minutes)
14
15
  */ const REFRESH_BUFFER_MS = 5 * 60 * 1000;
16
+ /** Raised when a credential cannot be bound to, or looked up by, an issuer. */ let CredentialBindingError = class CredentialBindingError extends Error {
17
+ constructor(message){
18
+ super(message);
19
+ this.name = 'CredentialBindingError';
20
+ }
21
+ };
22
+ /**
23
+ * The issuer credentials are keyed by, so they can never be presented to a
24
+ * different authorization server even when the resource keeps its URL (SEP-2352).
25
+ */ function requireIssuer(capabilities) {
26
+ if (!capabilities.issuer) {
27
+ throw new CredentialBindingError('Authorization server metadata has no issuer - credentials cannot be bound to an authorization server (RFC 8414 requires issuer)');
28
+ }
29
+ return capabilities.issuer;
30
+ }
15
31
  /**
16
32
  * DcrAuthenticator manages authentication for MCP HTTP servers
17
33
  * Handles DCR registration, OAuth flows, and token management
@@ -61,9 +77,11 @@ import { DynamicClientRegistrar } from './dynamic-client-registrar.js';
61
77
  // Loopback trust for every discovery-derived fetch below, computed from
62
78
  // the server we're talking to, never from capabilities' endpoints (see discovery-fetch.ts).
63
79
  const allowLoopback = isLoopbackUrl(baseUrl);
64
- const dcrTokenKey = `dcr-tokens:${baseUrl}`;
80
+ const issuer = requireIssuer(capabilities);
81
+ const resource = normalizeUrl(baseUrl);
82
+ const dcrTokenKey = `dcr-tokens:${issuer}:${resource}`;
65
83
  // 1. Check for existing DCR tokens (different from external tokens)
66
- let tokens = await this.tokenStore.get(dcrTokenKey);
84
+ let tokens = await this.loadTokens(dcrTokenKey, issuer);
67
85
  if (tokens) {
68
86
  // 2. Verify token is still valid by calling /oauth/verify
69
87
  try {
@@ -102,17 +120,7 @@ import { DynamicClientRegistrar } from './dynamic-client-registrar.js';
102
120
  allowLoopback
103
121
  });
104
122
  // Perform OAuth authorization flow with PKCE (RFC 7636)
105
- const flowOptions = {
106
- port,
107
- headless: this.headless,
108
- redirectUri: this.redirectUri,
109
- pkce: true,
110
- logger: this.logger,
111
- allowLoopback
112
- };
113
- if (capabilities.scopes) {
114
- flowOptions.scopes = capabilities.scopes;
115
- }
123
+ const flowOptions = this.buildFlowOptions(port, capabilities, issuer, resource, allowLoopback);
116
124
  tokens = await this.oauthFlow.performAuthFlow(capabilities.authorizationEndpoint, capabilities.tokenEndpoint, client.clientId, client.clientSecret, flowOptions);
117
125
  // For self-hosted mode, verify the token works with /oauth/verify immediately
118
126
  try {
@@ -136,23 +144,31 @@ import { DynamicClientRegistrar } from './dynamic-client-registrar.js';
136
144
  throw new Error('Self-hosted DCR authentication completed but token verification failed');
137
145
  }
138
146
  // Save tokens for future use
139
- await this.tokenStore.set(dcrTokenKey, tokens);
147
+ await this.tokenStore.set(dcrTokenKey, {
148
+ ...tokens,
149
+ issuer
150
+ });
140
151
  this.logger.debug('✅ Self-hosted DCR authentication successful, tokens saved');
141
152
  return tokens;
142
153
  }
143
154
  /** Handles authentication for external (non-self-hosted) OAuth providers. */ async ensureAuthenticatedExternal(baseUrl, capabilities) {
144
155
  // See ensureAuthenticatedSelfHosted - same loopback trust rule.
145
156
  const allowLoopback = isLoopbackUrl(baseUrl);
146
- const tokenKey = `tokens:${baseUrl}`;
157
+ const issuer = requireIssuer(capabilities);
158
+ const resource = normalizeUrl(baseUrl);
159
+ const tokenKey = `tokens:${issuer}:${resource}`;
147
160
  // 1. Check for existing tokens
148
- let tokens = await this.tokenStore.get(tokenKey);
161
+ let tokens = await this.loadTokens(tokenKey, issuer);
149
162
  if (tokens) {
150
163
  // 2. Proactive refresh if token expires within 5 minutes
151
164
  if (tokens.expiresAt < Date.now() + REFRESH_BUFFER_MS) {
152
165
  this.logger.debug('🔄 Refreshing access token...');
153
166
  try {
154
- tokens = await this.refreshTokens(tokens, capabilities.tokenEndpoint, allowLoopback);
155
- await this.tokenStore.set(tokenKey, tokens);
167
+ tokens = await this.refreshTokens(tokens, capabilities.tokenEndpoint, resource, allowLoopback);
168
+ await this.tokenStore.set(tokenKey, {
169
+ ...tokens,
170
+ issuer
171
+ });
156
172
  this.logger.debug('✅ Token refreshed successfully');
157
173
  } catch (_error) {
158
174
  // Refresh failed - clear tokens and re-authenticate
@@ -179,27 +195,21 @@ import { DynamicClientRegistrar } from './dynamic-client-registrar.js';
179
195
  allowLoopback
180
196
  });
181
197
  // Perform OAuth authorization flow with PKCE (RFC 7636)
182
- const flowOptions = {
183
- port,
184
- headless: this.headless,
185
- redirectUri: this.redirectUri,
186
- pkce: true,
187
- logger: this.logger,
188
- allowLoopback
189
- };
190
- if (capabilities.scopes) {
191
- flowOptions.scopes = capabilities.scopes;
192
- }
198
+ const flowOptions = this.buildFlowOptions(port, capabilities, issuer, resource, allowLoopback);
193
199
  tokens = await this.oauthFlow.performAuthFlow(capabilities.authorizationEndpoint, capabilities.tokenEndpoint, client.clientId, client.clientSecret, flowOptions);
194
200
  // Save tokens for future use
195
- await this.tokenStore.set(tokenKey, tokens);
201
+ await this.tokenStore.set(tokenKey, {
202
+ ...tokens,
203
+ issuer
204
+ });
196
205
  this.logger.debug('✅ Authentication successful, tokens saved');
197
206
  return tokens;
198
207
  }
199
208
  /**
200
209
  * Refreshes an access token using a refresh token.
210
+ * @param resource - Canonical resource server URI, audience-binding the token (RFC 8707).
201
211
  * @param allowLoopback - Loopback trust grant computed from the server actually being talked to (SSRF mitigation, see discovery-fetch.ts). Defaults to `false`.
202
- */ async refreshTokens(tokens, tokenEndpoint, allowLoopback = false) {
212
+ */ async refreshTokens(tokens, tokenEndpoint, resource, allowLoopback = false) {
203
213
  if (!tokenEndpoint) {
204
214
  throw new Error('Token endpoint not available for refresh');
205
215
  }
@@ -209,15 +219,49 @@ import { DynamicClientRegistrar } from './dynamic-client-registrar.js';
209
219
  if (!tokens.clientId || !tokens.clientSecret) {
210
220
  throw new Error('Client credentials not available for refresh');
211
221
  }
212
- return await this.oauthFlow.refreshTokens(tokenEndpoint, tokens.refreshToken, tokens.clientId, tokens.clientSecret, allowLoopback);
222
+ return await this.oauthFlow.refreshTokens(tokenEndpoint, tokens.refreshToken, tokens.clientId, tokens.clientSecret, resource, allowLoopback);
213
223
  }
214
224
  /**
215
- * Delete stored tokens for a server
225
+ * Deletes both stored token families for a server, across every issuer they were bound to.
226
+ * @throws CredentialBindingError if the configured store cannot enumerate keys.
216
227
  */ async deleteTokens(baseUrl) {
217
- const tokenKey = `tokens:${baseUrl}`;
218
- await this.tokenStore.delete(tokenKey);
228
+ const suffix = `:${normalizeUrl(baseUrl)}`;
229
+ if (!this.tokenStore.iterator) {
230
+ throw new CredentialBindingError('Token store does not support key enumeration, which issuer-keyed credentials require');
231
+ }
232
+ for await (const [key] of this.tokenStore.iterator(this.tokenStore.namespace)){
233
+ if (typeof key === 'string' && (key.startsWith('tokens:') || key.startsWith('dcr-tokens:')) && key.endsWith(suffix)) {
234
+ await this.tokenStore.delete(key);
235
+ }
236
+ }
219
237
  this.logger.debug(`🗑️ Deleted tokens for ${baseUrl}`);
220
238
  }
239
+ /** Discards a stored credential that is not bound to `issuer` (SEP-2352), including one stored before issuer binding existed. */ async loadTokens(key, issuer) {
240
+ const tokens = await this.tokenStore.get(key);
241
+ if (!tokens) return undefined;
242
+ if (tokens.issuer === issuer) return tokens;
243
+ this.logger.debug('🔑 Stored credential is not bound to the discovered issuer, re-authorizing');
244
+ await this.tokenStore.delete(key);
245
+ return undefined;
246
+ }
247
+ buildFlowOptions(port, capabilities, issuer, resource, allowLoopback) {
248
+ var _capabilities_authorizationResponseIssSupported;
249
+ const flowOptions = {
250
+ port,
251
+ issuer,
252
+ resource,
253
+ headless: this.headless,
254
+ redirectUri: this.redirectUri,
255
+ pkce: true,
256
+ logger: this.logger,
257
+ allowLoopback,
258
+ authorizationResponseIssSupported: (_capabilities_authorizationResponseIssSupported = capabilities.authorizationResponseIssSupported) !== null && _capabilities_authorizationResponseIssSupported !== void 0 ? _capabilities_authorizationResponseIssSupported : false
259
+ };
260
+ if (capabilities.scopes) {
261
+ flowOptions.scopes = capabilities.scopes;
262
+ }
263
+ return flowOptions;
264
+ }
221
265
  constructor(options){
222
266
  var _options_logger;
223
267
  if (options.tokenStore) {
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/dcr/dcr-authenticator.ts"],"sourcesContent":["/**\n * DCR Authenticator\n * Consolidates DCR and OAuth flow logic for MCP HTTP servers\n */\n\nimport path from 'node:path';\nimport * as fs from 'fs';\nimport Keyv from 'keyv';\nimport { KeyvFile } from 'keyv-file';\nimport { isLoopbackUrl } from '../auth/discovery-fetch.ts';\nimport { InteractiveOAuthFlow } from '../auth/interactive-oauth-flow.ts';\nimport type { AuthCapabilities, TokenSet } from '../auth/types.ts';\nimport { logger as defaultLogger, type Logger } from '../utils/logger.ts';\nimport { DynamicClientRegistrar } from './dynamic-client-registrar.ts';\n\n/**\n * DcrAuthenticator configuration options\n */\nexport interface DcrAuthenticatorOptions {\n /** Custom Keyv store (for testing) - if not provided, uses default ~/.mcpeasy/tokens.json */\n tokenStore?: Keyv;\n /** Headless mode (don't open browser) */\n headless?: boolean;\n /** Required redirect URI for OAuth callback */\n redirectUri: string;\n /** Optional logger for debug output (defaults to singleton logger) */\n logger?: Logger;\n}\n\n/**\n * Buffer time before token expiry to trigger proactive refresh (5 minutes)\n */\nconst REFRESH_BUFFER_MS = 5 * 60 * 1000;\n\n/**\n * DcrAuthenticator manages authentication for MCP HTTP servers\n * Handles DCR registration, OAuth flows, and token management\n */\nexport class DcrAuthenticator {\n private tokenStore: Keyv;\n private dcrClient: DynamicClientRegistrar;\n private oauthFlow: InteractiveOAuthFlow;\n private headless: boolean;\n private redirectUri: string;\n private logger: Logger;\n\n constructor(options: DcrAuthenticatorOptions) {\n if (options.tokenStore) {\n this.tokenStore = options.tokenStore;\n } else {\n // Default CLI store in .mcp-z directory (per-project)\n const storePath = path.join(process.cwd(), '.mcp-z', 'tokens.json');\n\n // Ensure directory exists before creating store\n fs.mkdirSync(path.dirname(storePath), { recursive: true });\n\n this.tokenStore = new Keyv({\n store: new KeyvFile({ filename: storePath }),\n });\n }\n this.dcrClient = new DynamicClientRegistrar();\n this.oauthFlow = new InteractiveOAuthFlow();\n this.headless = options.headless || false;\n this.redirectUri = options.redirectUri;\n this.logger = options.logger ?? defaultLogger;\n }\n\n /**\n * Detect if server is self-hosted DCR (vs external OAuth provider)\n * Self-hosted servers have their own OAuth endpoints and manage token storage\n */\n private async detectSelfHostedMode(baseUrl: string): Promise<boolean> {\n try {\n // Self-hosted DCR servers typically run their own OAuth server\n // Check if this is a self-hosted instance by testing OAuth metadata\n // For now, assume self-hosted if baseUrl matches common localhost patterns\n // TODO: Implement proper self-hosted detection logic\n return baseUrl.includes('localhost') || baseUrl.includes('127.0.0.1');\n } catch (_error) {\n return false; // Assume external mode if detection fails\n }\n }\n\n /**\n * Ensure server is authenticated, performing DCR and OAuth if needed\n * Proactively refreshes tokens if they're within 5 minutes of expiry\n *\n * @param baseUrl - Base URL of the server (e.g., https://example.com)\n * @param capabilities - Auth capabilities from .well-known endpoint\n * @returns Valid token set ready to use\n *\n * @throws Error if authentication fails\n *\n * @example\n * const authenticator = new DcrAuthenticator({ redirectUri: 'http://localhost:3000/callback' });\n * const tokens = await authenticator.ensureAuthenticated(\n * 'https://example.com',\n * capabilities\n * );\n */\n async ensureAuthenticated(baseUrl: string, capabilities: AuthCapabilities): Promise<TokenSet> {\n // Auto-detect server mode\n const isSelfHosted = await this.detectSelfHostedMode(baseUrl);\n\n if (isSelfHosted) {\n return this.ensureAuthenticatedSelfHosted(baseUrl, capabilities);\n }\n return this.ensureAuthenticatedExternal(baseUrl, capabilities);\n }\n\n /**\n * Handle authentication for self-hosted DCR servers\n * Self-hosted servers manage their own token storage via /oauth/verify\n */\n private async ensureAuthenticatedSelfHosted(baseUrl: string, capabilities: AuthCapabilities): Promise<TokenSet> {\n // Loopback trust for every discovery-derived fetch below, computed from\n // the server we're talking to, never from capabilities' endpoints (see discovery-fetch.ts).\n const allowLoopback = isLoopbackUrl(baseUrl);\n const dcrTokenKey = `dcr-tokens:${baseUrl}`;\n\n // 1. Check for existing DCR tokens (different from external tokens)\n let tokens = (await this.tokenStore.get(dcrTokenKey)) as TokenSet | undefined;\n\n if (tokens) {\n // 2. Verify token is still valid by calling /oauth/verify\n try {\n const verifyUrl = `${baseUrl}/oauth/verify`;\n const verifyResponse = await fetch(verifyUrl, {\n headers: { Authorization: `Bearer ${tokens.accessToken}`, Connection: 'close' },\n });\n\n if (verifyResponse.ok) {\n const verifyData = (await verifyResponse.json()) as { token?: string };\n if (verifyData.token === tokens.accessToken) {\n // Token is still valid with the self-hosted server\n return tokens;\n }\n }\n } catch (_error) {\n // Token verification failed - need to re-authenticate\n }\n\n // Token is expired or invalid\n await this.tokenStore.delete(dcrTokenKey);\n tokens = undefined;\n }\n\n // 3. No valid tokens - perform full DCR + OAuth flow\n if (!capabilities.registrationEndpoint || !capabilities.authorizationEndpoint || !capabilities.tokenEndpoint) {\n throw new Error('Server does not provide required OAuth endpoints');\n }\n\n this.logger.debug('🔐 No valid tokens found, starting self-hosted DCR authentication...');\n\n // Extract port from pre-resolved redirectUri\n const port = parseInt(new URL(this.redirectUri).port, 10) || (this.redirectUri.startsWith('https:') ? 443 : 80);\n\n // Register OAuth client via DCR\n this.logger.debug('📝 Registering OAuth client with self-hosted server...');\n const client = await this.dcrClient.registerClient(capabilities.registrationEndpoint, {\n redirectUri: this.redirectUri,\n allowLoopback,\n });\n\n // Perform OAuth authorization flow with PKCE (RFC 7636)\n const flowOptions: { port: number; headless: boolean; scopes?: string[]; redirectUri: string; pkce: boolean; logger: Logger; allowLoopback: boolean } = {\n port,\n headless: this.headless,\n redirectUri: this.redirectUri,\n pkce: true,\n logger: this.logger,\n allowLoopback,\n };\n if (capabilities.scopes) {\n flowOptions.scopes = capabilities.scopes;\n }\n\n tokens = await this.oauthFlow.performAuthFlow(capabilities.authorizationEndpoint, capabilities.tokenEndpoint, client.clientId, client.clientSecret, flowOptions);\n\n // For self-hosted mode, verify the token works with /oauth/verify immediately\n try {\n const verifyUrl = `${baseUrl}/oauth/verify`;\n const verifyResponse = await fetch(verifyUrl, {\n headers: { Authorization: `Bearer ${tokens.accessToken}`, Connection: 'close' },\n });\n\n if (!verifyResponse.ok) {\n throw new Error(`DCR token verification failed after authentication: ${verifyResponse.status}`);\n }\n\n const verifyData = (await verifyResponse.json()) as { token?: string };\n if (verifyData.token !== tokens.accessToken) {\n throw new Error('DCR server returned different token in verification');\n }\n\n this.logger.debug('✅ DCR token verified with self-hosted server');\n } catch (error) {\n this.logger.error('❌ DCR token verification failed:', error instanceof Error ? error.message : String(error));\n throw new Error('Self-hosted DCR authentication completed but token verification failed');\n }\n\n // Save tokens for future use\n await this.tokenStore.set(dcrTokenKey, tokens);\n this.logger.debug('✅ Self-hosted DCR authentication successful, tokens saved');\n\n return tokens;\n }\n\n /** Handles authentication for external (non-self-hosted) OAuth providers. */\n private async ensureAuthenticatedExternal(baseUrl: string, capabilities: AuthCapabilities): Promise<TokenSet> {\n // See ensureAuthenticatedSelfHosted - same loopback trust rule.\n const allowLoopback = isLoopbackUrl(baseUrl);\n const tokenKey = `tokens:${baseUrl}`;\n\n // 1. Check for existing tokens\n let tokens = (await this.tokenStore.get(tokenKey)) as TokenSet | undefined;\n\n if (tokens) {\n // 2. Proactive refresh if token expires within 5 minutes\n if (tokens.expiresAt < Date.now() + REFRESH_BUFFER_MS) {\n this.logger.debug('🔄 Refreshing access token...');\n\n try {\n tokens = await this.refreshTokens(tokens, capabilities.tokenEndpoint, allowLoopback);\n await this.tokenStore.set(tokenKey, tokens);\n this.logger.debug('✅ Token refreshed successfully');\n } catch (_error) {\n // Refresh failed - clear tokens and re-authenticate\n this.logger.warn('⚠️ Token refresh failed, re-authenticating...');\n await this.tokenStore.delete(tokenKey);\n tokens = undefined;\n }\n }\n\n if (tokens) {\n return tokens;\n }\n }\n\n // 3. No valid tokens - perform DCR + OAuth flow\n if (!capabilities.registrationEndpoint || !capabilities.authorizationEndpoint || !capabilities.tokenEndpoint) {\n throw new Error('Server does not provide required OAuth endpoints');\n }\n\n this.logger.debug('🔐 No valid tokens found, starting external OAuth authentication...');\n\n // Extract port from pre-resolved redirectUri\n const port = parseInt(new URL(this.redirectUri).port, 10) || (this.redirectUri.startsWith('https:') ? 443 : 80);\n\n // Register OAuth client via DCR\n this.logger.debug('📝 Registering OAuth client...');\n const client = await this.dcrClient.registerClient(capabilities.registrationEndpoint, {\n redirectUri: this.redirectUri,\n allowLoopback,\n });\n\n // Perform OAuth authorization flow with PKCE (RFC 7636)\n const flowOptions: { port: number; headless: boolean; scopes?: string[]; redirectUri: string; pkce: boolean; logger: Logger; allowLoopback: boolean } = {\n port,\n headless: this.headless,\n redirectUri: this.redirectUri,\n pkce: true,\n logger: this.logger,\n allowLoopback,\n };\n if (capabilities.scopes) {\n flowOptions.scopes = capabilities.scopes;\n }\n\n tokens = await this.oauthFlow.performAuthFlow(capabilities.authorizationEndpoint, capabilities.tokenEndpoint, client.clientId, client.clientSecret, flowOptions);\n\n // Save tokens for future use\n await this.tokenStore.set(tokenKey, tokens);\n this.logger.debug('✅ Authentication successful, tokens saved');\n\n return tokens;\n }\n\n /**\n * Refreshes an access token using a refresh token.\n * @param allowLoopback - Loopback trust grant computed from the server actually being talked to (SSRF mitigation, see discovery-fetch.ts). Defaults to `false`.\n */\n private async refreshTokens(tokens: TokenSet, tokenEndpoint: string | undefined, allowLoopback = false): Promise<TokenSet> {\n if (!tokenEndpoint) {\n throw new Error('Token endpoint not available for refresh');\n }\n\n if (!tokens.refreshToken) {\n throw new Error('No refresh token available');\n }\n\n if (!tokens.clientId || !tokens.clientSecret) {\n throw new Error('Client credentials not available for refresh');\n }\n\n return await this.oauthFlow.refreshTokens(tokenEndpoint, tokens.refreshToken, tokens.clientId, tokens.clientSecret, allowLoopback);\n }\n\n /**\n * Delete stored tokens for a server\n */\n async deleteTokens(baseUrl: string): Promise<void> {\n const tokenKey = `tokens:${baseUrl}`;\n await this.tokenStore.delete(tokenKey);\n this.logger.debug(`🗑️ Deleted tokens for ${baseUrl}`);\n }\n}\n"],"names":["path","fs","Keyv","KeyvFile","isLoopbackUrl","InteractiveOAuthFlow","logger","defaultLogger","DynamicClientRegistrar","REFRESH_BUFFER_MS","DcrAuthenticator","detectSelfHostedMode","baseUrl","includes","_error","ensureAuthenticated","capabilities","isSelfHosted","ensureAuthenticatedSelfHosted","ensureAuthenticatedExternal","allowLoopback","dcrTokenKey","tokens","tokenStore","get","verifyUrl","verifyResponse","fetch","headers","Authorization","accessToken","Connection","ok","verifyData","json","token","delete","undefined","registrationEndpoint","authorizationEndpoint","tokenEndpoint","Error","debug","port","parseInt","URL","redirectUri","startsWith","client","dcrClient","registerClient","flowOptions","headless","pkce","scopes","oauthFlow","performAuthFlow","clientId","clientSecret","status","error","message","String","set","tokenKey","expiresAt","Date","now","refreshTokens","warn","refreshToken","deleteTokens","options","storePath","join","process","cwd","mkdirSync","dirname","recursive","store","filename"],"mappings":"AAAA;;;CAGC,GAED,OAAOA,UAAU,YAAY;AAC7B,YAAYC,QAAQ,KAAK;AACzB,OAAOC,UAAU,OAAO;AACxB,SAASC,QAAQ,QAAQ,YAAY;AACrC,SAASC,aAAa,QAAQ,6BAA6B;AAC3D,SAASC,oBAAoB,QAAQ,oCAAoC;AAEzE,SAASC,UAAUC,aAAa,QAAqB,qBAAqB;AAC1E,SAASC,sBAAsB,QAAQ,gCAAgC;AAgBvE;;CAEC,GACD,MAAMC,oBAAoB,IAAI,KAAK;AAEnC;;;CAGC,GACD,OAAO,MAAMC;IA6BX;;;GAGC,GACD,MAAcC,qBAAqBC,OAAe,EAAoB;QACpE,IAAI;YACF,+DAA+D;YAC/D,oEAAoE;YACpE,2EAA2E;YAC3E,qDAAqD;YACrD,OAAOA,QAAQC,QAAQ,CAAC,gBAAgBD,QAAQC,QAAQ,CAAC;QAC3D,EAAE,OAAOC,QAAQ;YACf,OAAO,OAAO,0CAA0C;QAC1D;IACF;IAEA;;;;;;;;;;;;;;;;GAgBC,GACD,MAAMC,oBAAoBH,OAAe,EAAEI,YAA8B,EAAqB;QAC5F,0BAA0B;QAC1B,MAAMC,eAAe,MAAM,IAAI,CAACN,oBAAoB,CAACC;QAErD,IAAIK,cAAc;YAChB,OAAO,IAAI,CAACC,6BAA6B,CAACN,SAASI;QACrD;QACA,OAAO,IAAI,CAACG,2BAA2B,CAACP,SAASI;IACnD;IAEA;;;GAGC,GACD,MAAcE,8BAA8BN,OAAe,EAAEI,YAA8B,EAAqB;QAC9G,wEAAwE;QACxE,4FAA4F;QAC5F,MAAMI,gBAAgBhB,cAAcQ;QACpC,MAAMS,cAAc,CAAC,WAAW,EAAET,SAAS;QAE3C,oEAAoE;QACpE,IAAIU,SAAU,MAAM,IAAI,CAACC,UAAU,CAACC,GAAG,CAACH;QAExC,IAAIC,QAAQ;YACV,0DAA0D;YAC1D,IAAI;gBACF,MAAMG,YAAY,GAAGb,QAAQ,aAAa,CAAC;gBAC3C,MAAMc,iBAAiB,MAAMC,MAAMF,WAAW;oBAC5CG,SAAS;wBAAEC,eAAe,CAAC,OAAO,EAAEP,OAAOQ,WAAW,EAAE;wBAAEC,YAAY;oBAAQ;gBAChF;gBAEA,IAAIL,eAAeM,EAAE,EAAE;oBACrB,MAAMC,aAAc,MAAMP,eAAeQ,IAAI;oBAC7C,IAAID,WAAWE,KAAK,KAAKb,OAAOQ,WAAW,EAAE;wBAC3C,mDAAmD;wBACnD,OAAOR;oBACT;gBACF;YACF,EAAE,OAAOR,QAAQ;YACf,sDAAsD;YACxD;YAEA,8BAA8B;YAC9B,MAAM,IAAI,CAACS,UAAU,CAACa,MAAM,CAACf;YAC7BC,SAASe;QACX;QAEA,qDAAqD;QACrD,IAAI,CAACrB,aAAasB,oBAAoB,IAAI,CAACtB,aAAauB,qBAAqB,IAAI,CAACvB,aAAawB,aAAa,EAAE;YAC5G,MAAM,IAAIC,MAAM;QAClB;QAEA,IAAI,CAACnC,MAAM,CAACoC,KAAK,CAAC;QAElB,6CAA6C;QAC7C,MAAMC,OAAOC,SAAS,IAAIC,IAAI,IAAI,CAACC,WAAW,EAAEH,IAAI,EAAE,OAAQ,CAAA,IAAI,CAACG,WAAW,CAACC,UAAU,CAAC,YAAY,MAAM,EAAC;QAE7G,gCAAgC;QAChC,IAAI,CAACzC,MAAM,CAACoC,KAAK,CAAC;QAClB,MAAMM,SAAS,MAAM,IAAI,CAACC,SAAS,CAACC,cAAc,CAAClC,aAAasB,oBAAoB,EAAE;YACpFQ,aAAa,IAAI,CAACA,WAAW;YAC7B1B;QACF;QAEA,wDAAwD;QACxD,MAAM+B,cAAkJ;YACtJR;YACAS,UAAU,IAAI,CAACA,QAAQ;YACvBN,aAAa,IAAI,CAACA,WAAW;YAC7BO,MAAM;YACN/C,QAAQ,IAAI,CAACA,MAAM;YACnBc;QACF;QACA,IAAIJ,aAAasC,MAAM,EAAE;YACvBH,YAAYG,MAAM,GAAGtC,aAAasC,MAAM;QAC1C;QAEAhC,SAAS,MAAM,IAAI,CAACiC,SAAS,CAACC,eAAe,CAACxC,aAAauB,qBAAqB,EAAEvB,aAAawB,aAAa,EAAEQ,OAAOS,QAAQ,EAAET,OAAOU,YAAY,EAAEP;QAEpJ,8EAA8E;QAC9E,IAAI;YACF,MAAM1B,YAAY,GAAGb,QAAQ,aAAa,CAAC;YAC3C,MAAMc,iBAAiB,MAAMC,MAAMF,WAAW;gBAC5CG,SAAS;oBAAEC,eAAe,CAAC,OAAO,EAAEP,OAAOQ,WAAW,EAAE;oBAAEC,YAAY;gBAAQ;YAChF;YAEA,IAAI,CAACL,eAAeM,EAAE,EAAE;gBACtB,MAAM,IAAIS,MAAM,CAAC,oDAAoD,EAAEf,eAAeiC,MAAM,EAAE;YAChG;YAEA,MAAM1B,aAAc,MAAMP,eAAeQ,IAAI;YAC7C,IAAID,WAAWE,KAAK,KAAKb,OAAOQ,WAAW,EAAE;gBAC3C,MAAM,IAAIW,MAAM;YAClB;YAEA,IAAI,CAACnC,MAAM,CAACoC,KAAK,CAAC;QACpB,EAAE,OAAOkB,OAAO;YACd,IAAI,CAACtD,MAAM,CAACsD,KAAK,CAAC,oCAAoCA,iBAAiBnB,QAAQmB,MAAMC,OAAO,GAAGC,OAAOF;YACtG,MAAM,IAAInB,MAAM;QAClB;QAEA,6BAA6B;QAC7B,MAAM,IAAI,CAAClB,UAAU,CAACwC,GAAG,CAAC1C,aAAaC;QACvC,IAAI,CAAChB,MAAM,CAACoC,KAAK,CAAC;QAElB,OAAOpB;IACT;IAEA,2EAA2E,GAC3E,MAAcH,4BAA4BP,OAAe,EAAEI,YAA8B,EAAqB;QAC5G,gEAAgE;QAChE,MAAMI,gBAAgBhB,cAAcQ;QACpC,MAAMoD,WAAW,CAAC,OAAO,EAAEpD,SAAS;QAEpC,+BAA+B;QAC/B,IAAIU,SAAU,MAAM,IAAI,CAACC,UAAU,CAACC,GAAG,CAACwC;QAExC,IAAI1C,QAAQ;YACV,yDAAyD;YACzD,IAAIA,OAAO2C,SAAS,GAAGC,KAAKC,GAAG,KAAK1D,mBAAmB;gBACrD,IAAI,CAACH,MAAM,CAACoC,KAAK,CAAC;gBAElB,IAAI;oBACFpB,SAAS,MAAM,IAAI,CAAC8C,aAAa,CAAC9C,QAAQN,aAAawB,aAAa,EAAEpB;oBACtE,MAAM,IAAI,CAACG,UAAU,CAACwC,GAAG,CAACC,UAAU1C;oBACpC,IAAI,CAAChB,MAAM,CAACoC,KAAK,CAAC;gBACpB,EAAE,OAAO5B,QAAQ;oBACf,oDAAoD;oBACpD,IAAI,CAACR,MAAM,CAAC+D,IAAI,CAAC;oBACjB,MAAM,IAAI,CAAC9C,UAAU,CAACa,MAAM,CAAC4B;oBAC7B1C,SAASe;gBACX;YACF;YAEA,IAAIf,QAAQ;gBACV,OAAOA;YACT;QACF;QAEA,gDAAgD;QAChD,IAAI,CAACN,aAAasB,oBAAoB,IAAI,CAACtB,aAAauB,qBAAqB,IAAI,CAACvB,aAAawB,aAAa,EAAE;YAC5G,MAAM,IAAIC,MAAM;QAClB;QAEA,IAAI,CAACnC,MAAM,CAACoC,KAAK,CAAC;QAElB,6CAA6C;QAC7C,MAAMC,OAAOC,SAAS,IAAIC,IAAI,IAAI,CAACC,WAAW,EAAEH,IAAI,EAAE,OAAQ,CAAA,IAAI,CAACG,WAAW,CAACC,UAAU,CAAC,YAAY,MAAM,EAAC;QAE7G,gCAAgC;QAChC,IAAI,CAACzC,MAAM,CAACoC,KAAK,CAAC;QAClB,MAAMM,SAAS,MAAM,IAAI,CAACC,SAAS,CAACC,cAAc,CAAClC,aAAasB,oBAAoB,EAAE;YACpFQ,aAAa,IAAI,CAACA,WAAW;YAC7B1B;QACF;QAEA,wDAAwD;QACxD,MAAM+B,cAAkJ;YACtJR;YACAS,UAAU,IAAI,CAACA,QAAQ;YACvBN,aAAa,IAAI,CAACA,WAAW;YAC7BO,MAAM;YACN/C,QAAQ,IAAI,CAACA,MAAM;YACnBc;QACF;QACA,IAAIJ,aAAasC,MAAM,EAAE;YACvBH,YAAYG,MAAM,GAAGtC,aAAasC,MAAM;QAC1C;QAEAhC,SAAS,MAAM,IAAI,CAACiC,SAAS,CAACC,eAAe,CAACxC,aAAauB,qBAAqB,EAAEvB,aAAawB,aAAa,EAAEQ,OAAOS,QAAQ,EAAET,OAAOU,YAAY,EAAEP;QAEpJ,6BAA6B;QAC7B,MAAM,IAAI,CAAC5B,UAAU,CAACwC,GAAG,CAACC,UAAU1C;QACpC,IAAI,CAAChB,MAAM,CAACoC,KAAK,CAAC;QAElB,OAAOpB;IACT;IAEA;;;GAGC,GACD,MAAc8C,cAAc9C,MAAgB,EAAEkB,aAAiC,EAAEpB,gBAAgB,KAAK,EAAqB;QACzH,IAAI,CAACoB,eAAe;YAClB,MAAM,IAAIC,MAAM;QAClB;QAEA,IAAI,CAACnB,OAAOgD,YAAY,EAAE;YACxB,MAAM,IAAI7B,MAAM;QAClB;QAEA,IAAI,CAACnB,OAAOmC,QAAQ,IAAI,CAACnC,OAAOoC,YAAY,EAAE;YAC5C,MAAM,IAAIjB,MAAM;QAClB;QAEA,OAAO,MAAM,IAAI,CAACc,SAAS,CAACa,aAAa,CAAC5B,eAAelB,OAAOgD,YAAY,EAAEhD,OAAOmC,QAAQ,EAAEnC,OAAOoC,YAAY,EAAEtC;IACtH;IAEA;;GAEC,GACD,MAAMmD,aAAa3D,OAAe,EAAiB;QACjD,MAAMoD,WAAW,CAAC,OAAO,EAAEpD,SAAS;QACpC,MAAM,IAAI,CAACW,UAAU,CAACa,MAAM,CAAC4B;QAC7B,IAAI,CAAC1D,MAAM,CAACoC,KAAK,CAAC,CAAC,wBAAwB,EAAE9B,SAAS;IACxD;IAnQA,YAAY4D,OAAgC,CAAE;YAkB9BA;QAjBd,IAAIA,QAAQjD,UAAU,EAAE;YACtB,IAAI,CAACA,UAAU,GAAGiD,QAAQjD,UAAU;QACtC,OAAO;YACL,sDAAsD;YACtD,MAAMkD,YAAYzE,KAAK0E,IAAI,CAACC,QAAQC,GAAG,IAAI,UAAU;YAErD,gDAAgD;YAChD3E,GAAG4E,SAAS,CAAC7E,KAAK8E,OAAO,CAACL,YAAY;gBAAEM,WAAW;YAAK;YAExD,IAAI,CAACxD,UAAU,GAAG,IAAIrB,KAAK;gBACzB8E,OAAO,IAAI7E,SAAS;oBAAE8E,UAAUR;gBAAU;YAC5C;QACF;QACA,IAAI,CAACxB,SAAS,GAAG,IAAIzC;QACrB,IAAI,CAAC+C,SAAS,GAAG,IAAIlD;QACrB,IAAI,CAAC+C,QAAQ,GAAGoB,QAAQpB,QAAQ,IAAI;QACpC,IAAI,CAACN,WAAW,GAAG0B,QAAQ1B,WAAW;QACtC,IAAI,CAACxC,MAAM,IAAGkE,kBAAAA,QAAQlE,MAAM,cAAdkE,6BAAAA,kBAAkBjE;IAClC;AAiPF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/dcr/dcr-authenticator.ts"],"sourcesContent":["/**\n * DCR Authenticator\n * Consolidates DCR and OAuth flow logic for MCP HTTP servers\n */\n\nimport path from 'node:path';\nimport * as fs from 'fs';\nimport Keyv from 'keyv';\nimport { KeyvFile } from 'keyv-file';\nimport { isLoopbackUrl } from '../auth/discovery-fetch.ts';\nimport { InteractiveOAuthFlow } from '../auth/interactive-oauth-flow.ts';\nimport type { AuthCapabilities, OAuthFlowOptions, TokenSet } from '../auth/types.ts';\nimport { normalizeUrl } from '../lib/url-utils.ts';\nimport { logger as defaultLogger, type Logger } from '../utils/logger.ts';\nimport { DynamicClientRegistrar } from './dynamic-client-registrar.ts';\n\n/**\n * DcrAuthenticator configuration options\n */\nexport interface DcrAuthenticatorOptions {\n /** Custom Keyv store (for testing) - if not provided, uses default ~/.mcpeasy/tokens.json */\n tokenStore?: Keyv;\n /** Headless mode (don't open browser) */\n headless?: boolean;\n /** Required redirect URI for OAuth callback */\n redirectUri: string;\n /** Optional logger for debug output (defaults to singleton logger) */\n logger?: Logger;\n}\n\n/**\n * Buffer time before token expiry to trigger proactive refresh (5 minutes)\n */\nconst REFRESH_BUFFER_MS = 5 * 60 * 1000;\n\n/** Raised when a credential cannot be bound to, or looked up by, an issuer. */\nclass CredentialBindingError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CredentialBindingError';\n }\n}\n\n/**\n * The issuer credentials are keyed by, so they can never be presented to a\n * different authorization server even when the resource keeps its URL (SEP-2352).\n */\nfunction requireIssuer(capabilities: AuthCapabilities): string {\n if (!capabilities.issuer) {\n throw new CredentialBindingError('Authorization server metadata has no issuer - credentials cannot be bound to an authorization server (RFC 8414 requires issuer)');\n }\n return capabilities.issuer;\n}\n\n/**\n * DcrAuthenticator manages authentication for MCP HTTP servers\n * Handles DCR registration, OAuth flows, and token management\n */\nexport class DcrAuthenticator {\n private tokenStore: Keyv;\n private dcrClient: DynamicClientRegistrar;\n private oauthFlow: InteractiveOAuthFlow;\n private headless: boolean;\n private redirectUri: string;\n private logger: Logger;\n\n constructor(options: DcrAuthenticatorOptions) {\n if (options.tokenStore) {\n this.tokenStore = options.tokenStore;\n } else {\n // Default CLI store in .mcp-z directory (per-project)\n const storePath = path.join(process.cwd(), '.mcp-z', 'tokens.json');\n\n // Ensure directory exists before creating store\n fs.mkdirSync(path.dirname(storePath), { recursive: true });\n\n this.tokenStore = new Keyv({\n store: new KeyvFile({ filename: storePath }),\n });\n }\n this.dcrClient = new DynamicClientRegistrar();\n this.oauthFlow = new InteractiveOAuthFlow();\n this.headless = options.headless || false;\n this.redirectUri = options.redirectUri;\n this.logger = options.logger ?? defaultLogger;\n }\n\n /**\n * Detect if server is self-hosted DCR (vs external OAuth provider)\n * Self-hosted servers have their own OAuth endpoints and manage token storage\n */\n private async detectSelfHostedMode(baseUrl: string): Promise<boolean> {\n try {\n // Self-hosted DCR servers typically run their own OAuth server\n // Check if this is a self-hosted instance by testing OAuth metadata\n // For now, assume self-hosted if baseUrl matches common localhost patterns\n // TODO: Implement proper self-hosted detection logic\n return baseUrl.includes('localhost') || baseUrl.includes('127.0.0.1');\n } catch (_error) {\n return false; // Assume external mode if detection fails\n }\n }\n\n /**\n * Ensure server is authenticated, performing DCR and OAuth if needed\n * Proactively refreshes tokens if they're within 5 minutes of expiry\n *\n * @param baseUrl - Base URL of the server (e.g., https://example.com)\n * @param capabilities - Auth capabilities from .well-known endpoint\n * @returns Valid token set ready to use\n *\n * @throws Error if authentication fails\n *\n * @example\n * const authenticator = new DcrAuthenticator({ redirectUri: 'http://localhost:3000/callback' });\n * const tokens = await authenticator.ensureAuthenticated(\n * 'https://example.com',\n * capabilities\n * );\n */\n async ensureAuthenticated(baseUrl: string, capabilities: AuthCapabilities): Promise<TokenSet> {\n // Auto-detect server mode\n const isSelfHosted = await this.detectSelfHostedMode(baseUrl);\n\n if (isSelfHosted) {\n return this.ensureAuthenticatedSelfHosted(baseUrl, capabilities);\n }\n return this.ensureAuthenticatedExternal(baseUrl, capabilities);\n }\n\n /**\n * Handle authentication for self-hosted DCR servers\n * Self-hosted servers manage their own token storage via /oauth/verify\n */\n private async ensureAuthenticatedSelfHosted(baseUrl: string, capabilities: AuthCapabilities): Promise<TokenSet> {\n // Loopback trust for every discovery-derived fetch below, computed from\n // the server we're talking to, never from capabilities' endpoints (see discovery-fetch.ts).\n const allowLoopback = isLoopbackUrl(baseUrl);\n const issuer = requireIssuer(capabilities);\n const resource = normalizeUrl(baseUrl);\n const dcrTokenKey = `dcr-tokens:${issuer}:${resource}`;\n\n // 1. Check for existing DCR tokens (different from external tokens)\n let tokens = await this.loadTokens(dcrTokenKey, issuer);\n\n if (tokens) {\n // 2. Verify token is still valid by calling /oauth/verify\n try {\n const verifyUrl = `${baseUrl}/oauth/verify`;\n const verifyResponse = await fetch(verifyUrl, {\n headers: { Authorization: `Bearer ${tokens.accessToken}`, Connection: 'close' },\n });\n\n if (verifyResponse.ok) {\n const verifyData = (await verifyResponse.json()) as { token?: string };\n if (verifyData.token === tokens.accessToken) {\n // Token is still valid with the self-hosted server\n return tokens;\n }\n }\n } catch (_error) {\n // Token verification failed - need to re-authenticate\n }\n\n // Token is expired or invalid\n await this.tokenStore.delete(dcrTokenKey);\n tokens = undefined;\n }\n\n // 3. No valid tokens - perform full DCR + OAuth flow\n if (!capabilities.registrationEndpoint || !capabilities.authorizationEndpoint || !capabilities.tokenEndpoint) {\n throw new Error('Server does not provide required OAuth endpoints');\n }\n\n this.logger.debug('🔐 No valid tokens found, starting self-hosted DCR authentication...');\n\n // Extract port from pre-resolved redirectUri\n const port = parseInt(new URL(this.redirectUri).port, 10) || (this.redirectUri.startsWith('https:') ? 443 : 80);\n\n // Register OAuth client via DCR\n this.logger.debug('📝 Registering OAuth client with self-hosted server...');\n const client = await this.dcrClient.registerClient(capabilities.registrationEndpoint, {\n redirectUri: this.redirectUri,\n allowLoopback,\n });\n\n // Perform OAuth authorization flow with PKCE (RFC 7636)\n const flowOptions = this.buildFlowOptions(port, capabilities, issuer, resource, allowLoopback);\n\n tokens = await this.oauthFlow.performAuthFlow(capabilities.authorizationEndpoint, capabilities.tokenEndpoint, client.clientId, client.clientSecret, flowOptions);\n\n // For self-hosted mode, verify the token works with /oauth/verify immediately\n try {\n const verifyUrl = `${baseUrl}/oauth/verify`;\n const verifyResponse = await fetch(verifyUrl, {\n headers: { Authorization: `Bearer ${tokens.accessToken}`, Connection: 'close' },\n });\n\n if (!verifyResponse.ok) {\n throw new Error(`DCR token verification failed after authentication: ${verifyResponse.status}`);\n }\n\n const verifyData = (await verifyResponse.json()) as { token?: string };\n if (verifyData.token !== tokens.accessToken) {\n throw new Error('DCR server returned different token in verification');\n }\n\n this.logger.debug('✅ DCR token verified with self-hosted server');\n } catch (error) {\n this.logger.error('❌ DCR token verification failed:', error instanceof Error ? error.message : String(error));\n throw new Error('Self-hosted DCR authentication completed but token verification failed');\n }\n\n // Save tokens for future use\n await this.tokenStore.set(dcrTokenKey, { ...tokens, issuer });\n this.logger.debug('✅ Self-hosted DCR authentication successful, tokens saved');\n\n return tokens;\n }\n\n /** Handles authentication for external (non-self-hosted) OAuth providers. */\n private async ensureAuthenticatedExternal(baseUrl: string, capabilities: AuthCapabilities): Promise<TokenSet> {\n // See ensureAuthenticatedSelfHosted - same loopback trust rule.\n const allowLoopback = isLoopbackUrl(baseUrl);\n const issuer = requireIssuer(capabilities);\n const resource = normalizeUrl(baseUrl);\n const tokenKey = `tokens:${issuer}:${resource}`;\n\n // 1. Check for existing tokens\n let tokens = await this.loadTokens(tokenKey, issuer);\n\n if (tokens) {\n // 2. Proactive refresh if token expires within 5 minutes\n if (tokens.expiresAt < Date.now() + REFRESH_BUFFER_MS) {\n this.logger.debug('🔄 Refreshing access token...');\n\n try {\n tokens = await this.refreshTokens(tokens, capabilities.tokenEndpoint, resource, allowLoopback);\n await this.tokenStore.set(tokenKey, { ...tokens, issuer });\n this.logger.debug('✅ Token refreshed successfully');\n } catch (_error) {\n // Refresh failed - clear tokens and re-authenticate\n this.logger.warn('⚠️ Token refresh failed, re-authenticating...');\n await this.tokenStore.delete(tokenKey);\n tokens = undefined;\n }\n }\n\n if (tokens) {\n return tokens;\n }\n }\n\n // 3. No valid tokens - perform DCR + OAuth flow\n if (!capabilities.registrationEndpoint || !capabilities.authorizationEndpoint || !capabilities.tokenEndpoint) {\n throw new Error('Server does not provide required OAuth endpoints');\n }\n\n this.logger.debug('🔐 No valid tokens found, starting external OAuth authentication...');\n\n // Extract port from pre-resolved redirectUri\n const port = parseInt(new URL(this.redirectUri).port, 10) || (this.redirectUri.startsWith('https:') ? 443 : 80);\n\n // Register OAuth client via DCR\n this.logger.debug('📝 Registering OAuth client...');\n const client = await this.dcrClient.registerClient(capabilities.registrationEndpoint, {\n redirectUri: this.redirectUri,\n allowLoopback,\n });\n\n // Perform OAuth authorization flow with PKCE (RFC 7636)\n const flowOptions = this.buildFlowOptions(port, capabilities, issuer, resource, allowLoopback);\n\n tokens = await this.oauthFlow.performAuthFlow(capabilities.authorizationEndpoint, capabilities.tokenEndpoint, client.clientId, client.clientSecret, flowOptions);\n\n // Save tokens for future use\n await this.tokenStore.set(tokenKey, { ...tokens, issuer });\n this.logger.debug('✅ Authentication successful, tokens saved');\n\n return tokens;\n }\n\n /**\n * Refreshes an access token using a refresh token.\n * @param resource - Canonical resource server URI, audience-binding the token (RFC 8707).\n * @param allowLoopback - Loopback trust grant computed from the server actually being talked to (SSRF mitigation, see discovery-fetch.ts). Defaults to `false`.\n */\n private async refreshTokens(tokens: TokenSet, tokenEndpoint: string | undefined, resource: string, allowLoopback = false): Promise<TokenSet> {\n if (!tokenEndpoint) {\n throw new Error('Token endpoint not available for refresh');\n }\n\n if (!tokens.refreshToken) {\n throw new Error('No refresh token available');\n }\n\n if (!tokens.clientId || !tokens.clientSecret) {\n throw new Error('Client credentials not available for refresh');\n }\n\n return await this.oauthFlow.refreshTokens(tokenEndpoint, tokens.refreshToken, tokens.clientId, tokens.clientSecret, resource, allowLoopback);\n }\n\n /**\n * Deletes both stored token families for a server, across every issuer they were bound to.\n * @throws CredentialBindingError if the configured store cannot enumerate keys.\n */\n async deleteTokens(baseUrl: string): Promise<void> {\n const suffix = `:${normalizeUrl(baseUrl)}`;\n if (!this.tokenStore.iterator) {\n throw new CredentialBindingError('Token store does not support key enumeration, which issuer-keyed credentials require');\n }\n\n for await (const [key] of this.tokenStore.iterator(this.tokenStore.namespace)) {\n if (typeof key === 'string' && (key.startsWith('tokens:') || key.startsWith('dcr-tokens:')) && key.endsWith(suffix)) {\n await this.tokenStore.delete(key);\n }\n }\n this.logger.debug(`🗑️ Deleted tokens for ${baseUrl}`);\n }\n\n /** Discards a stored credential that is not bound to `issuer` (SEP-2352), including one stored before issuer binding existed. */\n private async loadTokens(key: string, issuer: string): Promise<TokenSet | undefined> {\n const tokens = (await this.tokenStore.get(key)) as TokenSet | undefined;\n if (!tokens) return undefined;\n if (tokens.issuer === issuer) return tokens;\n\n this.logger.debug('🔑 Stored credential is not bound to the discovered issuer, re-authorizing');\n await this.tokenStore.delete(key);\n return undefined;\n }\n\n private buildFlowOptions(port: number, capabilities: AuthCapabilities, issuer: string, resource: string, allowLoopback: boolean): OAuthFlowOptions {\n const flowOptions: OAuthFlowOptions = {\n port,\n issuer,\n resource,\n headless: this.headless,\n redirectUri: this.redirectUri,\n pkce: true,\n logger: this.logger,\n allowLoopback,\n authorizationResponseIssSupported: capabilities.authorizationResponseIssSupported ?? false,\n };\n if (capabilities.scopes) {\n flowOptions.scopes = capabilities.scopes;\n }\n return flowOptions;\n }\n}\n"],"names":["path","fs","Keyv","KeyvFile","isLoopbackUrl","InteractiveOAuthFlow","normalizeUrl","logger","defaultLogger","DynamicClientRegistrar","REFRESH_BUFFER_MS","CredentialBindingError","Error","message","name","requireIssuer","capabilities","issuer","DcrAuthenticator","detectSelfHostedMode","baseUrl","includes","_error","ensureAuthenticated","isSelfHosted","ensureAuthenticatedSelfHosted","ensureAuthenticatedExternal","allowLoopback","resource","dcrTokenKey","tokens","loadTokens","verifyUrl","verifyResponse","fetch","headers","Authorization","accessToken","Connection","ok","verifyData","json","token","tokenStore","delete","undefined","registrationEndpoint","authorizationEndpoint","tokenEndpoint","debug","port","parseInt","URL","redirectUri","startsWith","client","dcrClient","registerClient","flowOptions","buildFlowOptions","oauthFlow","performAuthFlow","clientId","clientSecret","status","error","String","set","tokenKey","expiresAt","Date","now","refreshTokens","warn","refreshToken","deleteTokens","suffix","iterator","key","namespace","endsWith","get","headless","pkce","authorizationResponseIssSupported","scopes","options","storePath","join","process","cwd","mkdirSync","dirname","recursive","store","filename"],"mappings":"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"}
@@ -9,9 +9,9 @@ export declare class DynamicClientRegistrar {
9
9
  * Registers a new OAuth client with the authorization server (RFC 7591).
10
10
  *
11
11
  * @param registrationEndpoint - Often sourced from remote-controlled AS metadata; pass `options.allowLoopback` explicitly. Defaults to `false`.
12
- * @param options - Registration options (client name, redirect URI, loopback trust).
12
+ * @param options - Registration options (redirect URI, client name, loopback trust).
13
13
  * @returns Client credentials (client ID and secret).
14
14
  * @throws Error if registration fails or the server returns an error.
15
15
  */
16
- registerClient(registrationEndpoint: string, options?: DcrRegistrationOptions): Promise<ClientCredentials>;
16
+ registerClient(registrationEndpoint: string, options: DcrRegistrationOptions): Promise<ClientCredentials>;
17
17
  }
@@ -7,17 +7,15 @@
7
7
  * Registers a new OAuth client with the authorization server (RFC 7591).
8
8
  *
9
9
  * @param registrationEndpoint - Often sourced from remote-controlled AS metadata; pass `options.allowLoopback` explicitly. Defaults to `false`.
10
- * @param options - Registration options (client name, redirect URI, loopback trust).
10
+ * @param options - Registration options (redirect URI, client name, loopback trust).
11
11
  * @returns Client credentials (client ID and secret).
12
12
  * @throws Error if registration fails or the server returns an error.
13
- */ async registerClient(registrationEndpoint, options = {}) {
13
+ */ async registerClient(registrationEndpoint, options) {
14
14
  var _options_allowLoopback;
15
15
  const requestBody = {
16
16
  client_name: options.clientName || '@mcp-z/client',
17
- redirect_uris: options.redirectUri ? [
17
+ redirect_uris: [
18
18
  options.redirectUri
19
- ] : [
20
- 'http://localhost:3000/callback'
21
19
  ],
22
20
  grant_types: [
23
21
  'authorization_code',
@@ -26,7 +24,10 @@
26
24
  response_types: [
27
25
  'code'
28
26
  ],
29
- token_endpoint_auth_method: 'client_secret_basic'
27
+ token_endpoint_auth_method: 'client_secret_basic',
28
+ // The callback listener always binds an OS-assigned loopback port, so the
29
+ // redirect URI is one an authorization server rejects for a web client (SEP-837, RFC 8252).
30
+ application_type: 'native'
30
31
  };
31
32
  // registrationEndpoint is remote-controlled discovery data; allowLoopback
32
33
  // must come from the caller's own trust, never from registrationEndpoint.
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/dcr/dynamic-client-registrar.ts"],"sourcesContent":["/**\n * Dynamic Client Registration (DCR) Client\n * Implements RFC 7591 for OAuth client registration\n */\n\nimport { discoveryFetch } from '../auth/discovery-fetch.ts';\nimport type { ClientCredentials, DcrRegistrationOptions } from '../auth/types.ts';\n\n/** DCR Registration Request (RFC 7591) */\ninterface DcrRegistrationRequest {\n client_name?: string;\n redirect_uris?: string[];\n grant_types?: string[];\n response_types?: string[];\n token_endpoint_auth_method?: string;\n}\n\n/** DCR Registration Response (RFC 7591) */\ninterface DcrRegistrationResponse {\n client_id: string;\n client_secret?: string;\n client_id_issued_at?: number;\n client_secret_expires_at?: number;\n}\n\n/** Handles Dynamic Client Registration with OAuth servers. */\nexport class DynamicClientRegistrar {\n /**\n * Registers a new OAuth client with the authorization server (RFC 7591).\n *\n * @param registrationEndpoint - Often sourced from remote-controlled AS metadata; pass `options.allowLoopback` explicitly. Defaults to `false`.\n * @param options - Registration options (client name, redirect URI, loopback trust).\n * @returns Client credentials (client ID and secret).\n * @throws Error if registration fails or the server returns an error.\n */\n async registerClient(registrationEndpoint: string, options: DcrRegistrationOptions = {}): Promise<ClientCredentials> {\n const requestBody: DcrRegistrationRequest = {\n client_name: options.clientName || '@mcp-z/client',\n redirect_uris: options.redirectUri ? [options.redirectUri] : ['http://localhost:3000/callback'],\n grant_types: ['authorization_code', 'refresh_token'],\n response_types: ['code'],\n token_endpoint_auth_method: 'client_secret_basic',\n };\n\n // registrationEndpoint is remote-controlled discovery data; allowLoopback\n // must come from the caller's own trust, never from registrationEndpoint.\n const response = await discoveryFetch(\n registrationEndpoint,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: JSON.stringify(requestBody),\n },\n 'registration endpoint',\n { allowLoopback: options.allowLoopback ?? false }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`DCR registration failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as DcrRegistrationResponse;\n\n if (!data.client_id) {\n throw new Error('DCR registration response missing client_id');\n }\n\n const credentials: ClientCredentials = {\n clientId: data.client_id,\n clientSecret: data.client_secret || '',\n };\n\n if (data.client_id_issued_at) {\n credentials.issuedAt = data.client_id_issued_at;\n }\n\n return credentials;\n }\n}\n"],"names":["discoveryFetch","DynamicClientRegistrar","registerClient","registrationEndpoint","options","requestBody","client_name","clientName","redirect_uris","redirectUri","grant_types","response_types","token_endpoint_auth_method","response","method","headers","Accept","Connection","body","JSON","stringify","allowLoopback","ok","errorText","text","Error","status","data","json","client_id","credentials","clientId","clientSecret","client_secret","client_id_issued_at","issuedAt"],"mappings":"AAAA;;;CAGC,GAED,SAASA,cAAc,QAAQ,6BAA6B;AAoB5D,4DAA4D,GAC5D,OAAO,MAAMC;IACX;;;;;;;GAOC,GACD,MAAMC,eAAeC,oBAA4B,EAAEC,UAAkC,CAAC,CAAC,EAA8B;YAuBhGA;QAtBnB,MAAMC,cAAsC;YAC1CC,aAAaF,QAAQG,UAAU,IAAI;YACnCC,eAAeJ,QAAQK,WAAW,GAAG;gBAACL,QAAQK,WAAW;aAAC,GAAG;gBAAC;aAAiC;YAC/FC,aAAa;gBAAC;gBAAsB;aAAgB;YACpDC,gBAAgB;gBAAC;aAAO;YACxBC,4BAA4B;QAC9B;QAEA,0EAA0E;QAC1E,0EAA0E;QAC1E,MAAMC,WAAW,MAAMb,eACrBG,sBACA;YACEW,QAAQ;YACRC,SAAS;gBACP,gBAAgB;gBAChBC,QAAQ;gBACRC,YAAY;YACd;YACAC,MAAMC,KAAKC,SAAS,CAACf;QACvB,GACA,yBACA;YAAEgB,aAAa,GAAEjB,yBAAAA,QAAQiB,aAAa,cAArBjB,oCAAAA,yBAAyB;QAAM;QAGlD,IAAI,CAACS,SAASS,EAAE,EAAE;YAChB,MAAMC,YAAY,MAAMV,SAASW,IAAI;YACrC,MAAM,IAAIC,MAAM,CAAC,yBAAyB,EAAEZ,SAASa,MAAM,CAAC,GAAG,EAAEH,WAAW;QAC9E;QAEA,MAAMI,OAAQ,MAAMd,SAASe,IAAI;QAEjC,IAAI,CAACD,KAAKE,SAAS,EAAE;YACnB,MAAM,IAAIJ,MAAM;QAClB;QAEA,MAAMK,cAAiC;YACrCC,UAAUJ,KAAKE,SAAS;YACxBG,cAAcL,KAAKM,aAAa,IAAI;QACtC;QAEA,IAAIN,KAAKO,mBAAmB,EAAE;YAC5BJ,YAAYK,QAAQ,GAAGR,KAAKO,mBAAmB;QACjD;QAEA,OAAOJ;IACT;AACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/dcr/dynamic-client-registrar.ts"],"sourcesContent":["/**\n * Dynamic Client Registration (DCR) Client\n * Implements RFC 7591 for OAuth client registration\n */\n\nimport { discoveryFetch } from '../auth/discovery-fetch.ts';\nimport type { ClientCredentials, DcrRegistrationOptions } from '../auth/types.ts';\n\n/** DCR Registration Request (RFC 7591) */\ninterface DcrRegistrationRequest {\n client_name?: string;\n redirect_uris?: string[];\n grant_types?: string[];\n response_types?: string[];\n token_endpoint_auth_method?: string;\n application_type?: string;\n}\n\n/** DCR Registration Response (RFC 7591) */\ninterface DcrRegistrationResponse {\n client_id: string;\n client_secret?: string;\n client_id_issued_at?: number;\n client_secret_expires_at?: number;\n}\n\n/** Handles Dynamic Client Registration with OAuth servers. */\nexport class DynamicClientRegistrar {\n /**\n * Registers a new OAuth client with the authorization server (RFC 7591).\n *\n * @param registrationEndpoint - Often sourced from remote-controlled AS metadata; pass `options.allowLoopback` explicitly. Defaults to `false`.\n * @param options - Registration options (redirect URI, client name, loopback trust).\n * @returns Client credentials (client ID and secret).\n * @throws Error if registration fails or the server returns an error.\n */\n async registerClient(registrationEndpoint: string, options: DcrRegistrationOptions): Promise<ClientCredentials> {\n const requestBody: DcrRegistrationRequest = {\n client_name: options.clientName || '@mcp-z/client',\n redirect_uris: [options.redirectUri],\n grant_types: ['authorization_code', 'refresh_token'],\n response_types: ['code'],\n token_endpoint_auth_method: 'client_secret_basic',\n // The callback listener always binds an OS-assigned loopback port, so the\n // redirect URI is one an authorization server rejects for a web client (SEP-837, RFC 8252).\n application_type: 'native',\n };\n\n // registrationEndpoint is remote-controlled discovery data; allowLoopback\n // must come from the caller's own trust, never from registrationEndpoint.\n const response = await discoveryFetch(\n registrationEndpoint,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: JSON.stringify(requestBody),\n },\n 'registration endpoint',\n { allowLoopback: options.allowLoopback ?? false }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`DCR registration failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as DcrRegistrationResponse;\n\n if (!data.client_id) {\n throw new Error('DCR registration response missing client_id');\n }\n\n const credentials: ClientCredentials = {\n clientId: data.client_id,\n clientSecret: data.client_secret || '',\n };\n\n if (data.client_id_issued_at) {\n credentials.issuedAt = data.client_id_issued_at;\n }\n\n return credentials;\n }\n}\n"],"names":["discoveryFetch","DynamicClientRegistrar","registerClient","registrationEndpoint","options","requestBody","client_name","clientName","redirect_uris","redirectUri","grant_types","response_types","token_endpoint_auth_method","application_type","response","method","headers","Accept","Connection","body","JSON","stringify","allowLoopback","ok","errorText","text","Error","status","data","json","client_id","credentials","clientId","clientSecret","client_secret","client_id_issued_at","issuedAt"],"mappings":"AAAA;;;CAGC,GAED,SAASA,cAAc,QAAQ,6BAA6B;AAqB5D,4DAA4D,GAC5D,OAAO,MAAMC;IACX;;;;;;;GAOC,GACD,MAAMC,eAAeC,oBAA4B,EAAEC,OAA+B,EAA8B;YA0B3FA;QAzBnB,MAAMC,cAAsC;YAC1CC,aAAaF,QAAQG,UAAU,IAAI;YACnCC,eAAe;gBAACJ,QAAQK,WAAW;aAAC;YACpCC,aAAa;gBAAC;gBAAsB;aAAgB;YACpDC,gBAAgB;gBAAC;aAAO;YACxBC,4BAA4B;YAC5B,0EAA0E;YAC1E,4FAA4F;YAC5FC,kBAAkB;QACpB;QAEA,0EAA0E;QAC1E,0EAA0E;QAC1E,MAAMC,WAAW,MAAMd,eACrBG,sBACA;YACEY,QAAQ;YACRC,SAAS;gBACP,gBAAgB;gBAChBC,QAAQ;gBACRC,YAAY;YACd;YACAC,MAAMC,KAAKC,SAAS,CAAChB;QACvB,GACA,yBACA;YAAEiB,aAAa,GAAElB,yBAAAA,QAAQkB,aAAa,cAArBlB,oCAAAA,yBAAyB;QAAM;QAGlD,IAAI,CAACU,SAASS,EAAE,EAAE;YAChB,MAAMC,YAAY,MAAMV,SAASW,IAAI;YACrC,MAAM,IAAIC,MAAM,CAAC,yBAAyB,EAAEZ,SAASa,MAAM,CAAC,GAAG,EAAEH,WAAW;QAC9E;QAEA,MAAMI,OAAQ,MAAMd,SAASe,IAAI;QAEjC,IAAI,CAACD,KAAKE,SAAS,EAAE;YACnB,MAAM,IAAIJ,MAAM;QAClB;QAEA,MAAMK,cAAiC;YACrCC,UAAUJ,KAAKE,SAAS;YACxBG,cAAcL,KAAKM,aAAa,IAAI;QACtC;QAEA,IAAIN,KAAKO,mBAAmB,EAAE;YAC5BJ,YAAYK,QAAQ,GAAGR,KAAKO,mBAAmB;QACjD;QAEA,OAAOJ;IACT;AACF"}
@@ -3,8 +3,8 @@ export function normalizeUrl(input) {
3
3
  const url = new URL(input);
4
4
  url.search = '';
5
5
  url.hash = '';
6
- url.pathname = url.pathname.replace(/\/+$/, '');
7
- return url.origin + url.pathname;
6
+ // Strip after joining: assigning an empty pathname puts the '/' straight back.
7
+ return (url.origin + url.pathname).replace(/\/+$/, '');
8
8
  } catch {
9
9
  return input.replace(/\/+$/, '');
10
10
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/lib/url-utils.ts"],"sourcesContent":["export function normalizeUrl(input: string): string {\n try {\n const url = new URL(input);\n url.search = '';\n url.hash = '';\n url.pathname = url.pathname.replace(/\\/+$/, '');\n return url.origin + url.pathname;\n } catch {\n return input.replace(/\\/+$/, '');\n }\n}\n\nexport function joinWellKnown(baseUrl: string, suffix: string): string {\n return `${normalizeUrl(baseUrl)}${suffix}`;\n}\n"],"names":["normalizeUrl","input","url","URL","search","hash","pathname","replace","origin","joinWellKnown","baseUrl","suffix"],"mappings":"AAAA,OAAO,SAASA,aAAaC,KAAa;IACxC,IAAI;QACF,MAAMC,MAAM,IAAIC,IAAIF;QACpBC,IAAIE,MAAM,GAAG;QACbF,IAAIG,IAAI,GAAG;QACXH,IAAII,QAAQ,GAAGJ,IAAII,QAAQ,CAACC,OAAO,CAAC,QAAQ;QAC5C,OAAOL,IAAIM,MAAM,GAAGN,IAAII,QAAQ;IAClC,EAAE,OAAM;QACN,OAAOL,MAAMM,OAAO,CAAC,QAAQ;IAC/B;AACF;AAEA,OAAO,SAASE,cAAcC,OAAe,EAAEC,MAAc;IAC3D,OAAO,GAAGX,aAAaU,WAAWC,QAAQ;AAC5C"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/lib/url-utils.ts"],"sourcesContent":["export function normalizeUrl(input: string): string {\n try {\n const url = new URL(input);\n url.search = '';\n url.hash = '';\n // Strip after joining: assigning an empty pathname puts the '/' straight back.\n return (url.origin + url.pathname).replace(/\\/+$/, '');\n } catch {\n return input.replace(/\\/+$/, '');\n }\n}\n\nexport function joinWellKnown(baseUrl: string, suffix: string): string {\n return `${normalizeUrl(baseUrl)}${suffix}`;\n}\n"],"names":["normalizeUrl","input","url","URL","search","hash","origin","pathname","replace","joinWellKnown","baseUrl","suffix"],"mappings":"AAAA,OAAO,SAASA,aAAaC,KAAa;IACxC,IAAI;QACF,MAAMC,MAAM,IAAIC,IAAIF;QACpBC,IAAIE,MAAM,GAAG;QACbF,IAAIG,IAAI,GAAG;QACX,+EAA+E;QAC/E,OAAO,AAACH,CAAAA,IAAII,MAAM,GAAGJ,IAAIK,QAAQ,AAAD,EAAGC,OAAO,CAAC,QAAQ;IACrD,EAAE,OAAM;QACN,OAAOP,MAAMO,OAAO,CAAC,QAAQ;IAC/B;AACF;AAEA,OAAO,SAASC,cAAcC,OAAe,EAAEC,MAAc;IAC3D,OAAO,GAAGX,aAAaU,WAAWC,QAAQ;AAC5C"}
@@ -1,4 +1,4 @@
1
- import type { Client } from '@modelcontextprotocol/sdk/client/index.js';
1
+ import type { Client } from '@modelcontextprotocol/client';
2
2
  export type NativeCallToolResponse = Awaited<ReturnType<Client['callTool']>>;
3
3
  export type NativeGetPromptResponse = Awaited<ReturnType<Client['getPrompt']>>;
4
4
  export type NativeReadResourceResponse = Awaited<ReturnType<Client['readResource']>>;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/response-wrappers.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\nimport type { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { ContentBlock, PromptMessage, TextContent } from '@modelcontextprotocol/sdk/types.js';\n\nexport type NativeCallToolResponse = Awaited<ReturnType<Client['callTool']>>;\nexport type NativeGetPromptResponse = Awaited<ReturnType<Client['getPrompt']>>;\nexport type NativeReadResourceResponse = Awaited<ReturnType<Client['readResource']>>;\n\nexport type JsonValidator<T> = (value: unknown) => asserts value is T;\n\nexport class ToolResponseError extends Error {\n readonly response: NativeCallToolResponse;\n\n constructor(message: string, response: NativeCallToolResponse) {\n super(message);\n this.name = 'ToolResponseError';\n this.response = response;\n }\n}\n\nexport class ToolResponseWrapper {\n private readonly payload: NativeCallToolResponse;\n\n constructor(payload: NativeCallToolResponse) {\n this.payload = payload;\n }\n\n raw(): NativeCallToolResponse {\n return this.payload;\n }\n\n json<T = unknown>(validator?: JsonValidator<T>): T {\n const value = this.resolveJsonPayload();\n if (validator) {\n const assertValid: JsonValidator<T> = validator;\n assertValid(value);\n }\n return value as T;\n }\n\n text(): string {\n if (isCompatibilityResult(this.payload)) {\n if (typeof this.payload.toolResult === 'string') {\n return this.payload.toolResult;\n }\n throw new ToolResponseError('Compatibility tool result is not text', this.payload);\n }\n\n this.throwIfError();\n\n const textBlock = findFirstTextBlock(this.payload.content ?? []);\n if (!textBlock) {\n throw new ToolResponseError('Tool response did not include text content', this.payload);\n }\n\n return textBlock.text;\n }\n\n private resolveJsonPayload(): unknown {\n if (isCompatibilityResult(this.payload)) {\n return this.payload.toolResult;\n }\n\n this.throwIfError();\n\n if (hasStructuredContent(this.payload)) {\n return this.payload.structuredContent;\n }\n\n const textBlock = findFirstTextBlock(this.payload.content ?? []);\n if (!textBlock) {\n throw new ToolResponseError('Tool response did not include structuredContent or text content', this.payload);\n }\n\n try {\n return JSON.parse(textBlock.text) as unknown;\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new ToolResponseError(`Failed to parse tool text content as JSON: ${reason}`, this.payload);\n }\n }\n\n private throwIfError(): void {\n if ('isError' in this.payload && this.payload.isError) {\n let detail = typeof this.payload.error === 'object' && this.payload.error && 'message' in this.payload.error ? String((this.payload.error as { message?: unknown }).message ?? '') : '';\n if (!detail) {\n const textBlock = findFirstTextBlock((this.payload.content ?? []) as ContentBlock[]);\n if (textBlock?.text) {\n detail = textBlock.text;\n }\n }\n const message = detail ? `Tool invocation returned an error result: ${detail}` : 'Tool invocation returned an error result';\n throw new ToolResponseError(message, this.payload);\n }\n }\n}\n\nexport class PromptResponseError extends Error {\n readonly response: NativeGetPromptResponse;\n\n constructor(message: string, response: NativeGetPromptResponse) {\n super(message);\n this.name = 'PromptResponseError';\n this.response = response;\n }\n}\n\nexport class PromptResponseWrapper {\n private readonly payload: NativeGetPromptResponse;\n\n constructor(payload: NativeGetPromptResponse) {\n this.payload = payload;\n }\n\n raw(): NativeGetPromptResponse {\n return this.payload;\n }\n\n text(): string {\n const segments = collectPromptText(this.payload.messages);\n if (!segments.length) {\n throw new PromptResponseError('Prompt response did not include text content', this.payload);\n }\n return segments.join('\\n\\n');\n }\n\n json<T = unknown>(validator?: JsonValidator<T>): T {\n const textValue = this.text();\n try {\n const parsed = JSON.parse(textValue) as unknown;\n if (validator) {\n const assertValid: JsonValidator<T> = validator;\n assertValid(parsed);\n }\n return parsed as T;\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new PromptResponseError(`Failed to parse prompt text as JSON: ${reason}`, this.payload);\n }\n }\n}\n\nexport class ResourceResponseError extends Error {\n readonly response: NativeReadResourceResponse;\n\n constructor(message: string, response: NativeReadResourceResponse) {\n super(message);\n this.name = 'ResourceResponseError';\n this.response = response;\n }\n}\n\nexport class ResourceResponseWrapper {\n private readonly payload: NativeReadResourceResponse;\n\n constructor(payload: NativeReadResourceResponse) {\n this.payload = payload;\n }\n\n raw(): NativeReadResourceResponse {\n return this.payload;\n }\n\n text(): string {\n const entry = this.firstEntry();\n if ('text' in entry && typeof entry.text === 'string') {\n return entry.text;\n }\n if ('blob' in entry && typeof entry.blob === 'string') {\n try {\n return Buffer.from(entry.blob, 'base64').toString('utf8');\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new ResourceResponseError(`Failed to decode resource blob as UTF-8 text: ${reason}`, this.payload);\n }\n }\n throw new ResourceResponseError('Resource content does not include text or blob data', this.payload);\n }\n\n json<T = unknown>(validator?: JsonValidator<T>): T {\n const textValue = this.text();\n try {\n const parsed = JSON.parse(textValue) as unknown;\n if (validator) {\n const assertValid: JsonValidator<T> = validator;\n assertValid(parsed);\n }\n return parsed as T;\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new ResourceResponseError(`Failed to parse resource text as JSON: ${reason}`, this.payload);\n }\n }\n\n private firstEntry(): NativeReadResourceResponse['contents'][number] {\n const [entry] = this.payload.contents ?? [];\n if (!entry) {\n throw new ResourceResponseError('Resource response did not include any contents', this.payload);\n }\n return entry;\n }\n}\n\nfunction hasStructuredContent(response: NativeCallToolResponse): response is NativeCallToolResponse & { structuredContent: Record<string, unknown> } {\n return Boolean((response as { structuredContent?: unknown }).structuredContent);\n}\n\nfunction isCompatibilityResult(response: NativeCallToolResponse): response is NativeCallToolResponse & { toolResult: unknown } {\n return hasOwn(response, 'toolResult');\n}\n\nfunction findFirstTextBlock(blocks: ContentBlock[] | undefined): TextContent | undefined {\n if (!Array.isArray(blocks)) {\n return undefined;\n }\n return blocks.find(isTextContent);\n}\n\nfunction collectPromptText(messages: PromptMessage[]): string[] {\n const segments: string[] = [];\n for (const message of messages) {\n const textBlock = isTextContent(message.content) ? message.content : undefined;\n if (textBlock) {\n segments.push(textBlock.text);\n }\n }\n return segments;\n}\n\nfunction isTextContent(block: unknown): block is TextContent {\n return Boolean(block) && typeof block === 'object' && (block as { type?: string }).type === 'text';\n}\n\nconst protoHasOwn = Object.prototype.hasOwnProperty;\n\nfunction hasOwn(target: object, key: PropertyKey): boolean {\n return protoHasOwn.call(target, key);\n}\n\nfunction formatErrorReason(error: unknown): string {\n if (error instanceof Error && typeof error.message === 'string') {\n return error.message;\n }\n if (typeof error === 'string') {\n return error;\n }\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n}\n"],"names":["Buffer","ToolResponseError","Error","message","response","name","ToolResponseWrapper","raw","payload","json","validator","value","resolveJsonPayload","assertValid","text","isCompatibilityResult","toolResult","throwIfError","textBlock","findFirstTextBlock","content","hasStructuredContent","structuredContent","JSON","parse","error","reason","formatErrorReason","isError","detail","String","PromptResponseError","PromptResponseWrapper","segments","collectPromptText","messages","length","join","textValue","parsed","ResourceResponseError","ResourceResponseWrapper","entry","firstEntry","blob","from","toString","contents","Boolean","hasOwn","blocks","Array","isArray","undefined","find","isTextContent","push","block","type","protoHasOwn","Object","prototype","hasOwnProperty","target","key","call","stringify"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/response-wrappers.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\nimport type { Client, ContentBlock, PromptMessage, TextContent } from '@modelcontextprotocol/client';\n\nexport type NativeCallToolResponse = Awaited<ReturnType<Client['callTool']>>;\nexport type NativeGetPromptResponse = Awaited<ReturnType<Client['getPrompt']>>;\nexport type NativeReadResourceResponse = Awaited<ReturnType<Client['readResource']>>;\n\nexport type JsonValidator<T> = (value: unknown) => asserts value is T;\n\nexport class ToolResponseError extends Error {\n readonly response: NativeCallToolResponse;\n\n constructor(message: string, response: NativeCallToolResponse) {\n super(message);\n this.name = 'ToolResponseError';\n this.response = response;\n }\n}\n\nexport class ToolResponseWrapper {\n private readonly payload: NativeCallToolResponse;\n\n constructor(payload: NativeCallToolResponse) {\n this.payload = payload;\n }\n\n raw(): NativeCallToolResponse {\n return this.payload;\n }\n\n json<T = unknown>(validator?: JsonValidator<T>): T {\n const value = this.resolveJsonPayload();\n if (validator) {\n const assertValid: JsonValidator<T> = validator;\n assertValid(value);\n }\n return value as T;\n }\n\n text(): string {\n if (isCompatibilityResult(this.payload)) {\n if (typeof this.payload.toolResult === 'string') {\n return this.payload.toolResult;\n }\n throw new ToolResponseError('Compatibility tool result is not text', this.payload);\n }\n\n this.throwIfError();\n\n const textBlock = findFirstTextBlock(this.payload.content ?? []);\n if (!textBlock) {\n throw new ToolResponseError('Tool response did not include text content', this.payload);\n }\n\n return textBlock.text;\n }\n\n private resolveJsonPayload(): unknown {\n if (isCompatibilityResult(this.payload)) {\n return this.payload.toolResult;\n }\n\n this.throwIfError();\n\n if (hasStructuredContent(this.payload)) {\n return this.payload.structuredContent;\n }\n\n const textBlock = findFirstTextBlock(this.payload.content ?? []);\n if (!textBlock) {\n throw new ToolResponseError('Tool response did not include structuredContent or text content', this.payload);\n }\n\n try {\n return JSON.parse(textBlock.text) as unknown;\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new ToolResponseError(`Failed to parse tool text content as JSON: ${reason}`, this.payload);\n }\n }\n\n private throwIfError(): void {\n if ('isError' in this.payload && this.payload.isError) {\n let detail = typeof this.payload.error === 'object' && this.payload.error && 'message' in this.payload.error ? String((this.payload.error as { message?: unknown }).message ?? '') : '';\n if (!detail) {\n const textBlock = findFirstTextBlock((this.payload.content ?? []) as ContentBlock[]);\n if (textBlock?.text) {\n detail = textBlock.text;\n }\n }\n const message = detail ? `Tool invocation returned an error result: ${detail}` : 'Tool invocation returned an error result';\n throw new ToolResponseError(message, this.payload);\n }\n }\n}\n\nexport class PromptResponseError extends Error {\n readonly response: NativeGetPromptResponse;\n\n constructor(message: string, response: NativeGetPromptResponse) {\n super(message);\n this.name = 'PromptResponseError';\n this.response = response;\n }\n}\n\nexport class PromptResponseWrapper {\n private readonly payload: NativeGetPromptResponse;\n\n constructor(payload: NativeGetPromptResponse) {\n this.payload = payload;\n }\n\n raw(): NativeGetPromptResponse {\n return this.payload;\n }\n\n text(): string {\n const segments = collectPromptText(this.payload.messages);\n if (!segments.length) {\n throw new PromptResponseError('Prompt response did not include text content', this.payload);\n }\n return segments.join('\\n\\n');\n }\n\n json<T = unknown>(validator?: JsonValidator<T>): T {\n const textValue = this.text();\n try {\n const parsed = JSON.parse(textValue) as unknown;\n if (validator) {\n const assertValid: JsonValidator<T> = validator;\n assertValid(parsed);\n }\n return parsed as T;\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new PromptResponseError(`Failed to parse prompt text as JSON: ${reason}`, this.payload);\n }\n }\n}\n\nexport class ResourceResponseError extends Error {\n readonly response: NativeReadResourceResponse;\n\n constructor(message: string, response: NativeReadResourceResponse) {\n super(message);\n this.name = 'ResourceResponseError';\n this.response = response;\n }\n}\n\nexport class ResourceResponseWrapper {\n private readonly payload: NativeReadResourceResponse;\n\n constructor(payload: NativeReadResourceResponse) {\n this.payload = payload;\n }\n\n raw(): NativeReadResourceResponse {\n return this.payload;\n }\n\n text(): string {\n const entry = this.firstEntry();\n if ('text' in entry && typeof entry.text === 'string') {\n return entry.text;\n }\n if ('blob' in entry && typeof entry.blob === 'string') {\n try {\n return Buffer.from(entry.blob, 'base64').toString('utf8');\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new ResourceResponseError(`Failed to decode resource blob as UTF-8 text: ${reason}`, this.payload);\n }\n }\n throw new ResourceResponseError('Resource content does not include text or blob data', this.payload);\n }\n\n json<T = unknown>(validator?: JsonValidator<T>): T {\n const textValue = this.text();\n try {\n const parsed = JSON.parse(textValue) as unknown;\n if (validator) {\n const assertValid: JsonValidator<T> = validator;\n assertValid(parsed);\n }\n return parsed as T;\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new ResourceResponseError(`Failed to parse resource text as JSON: ${reason}`, this.payload);\n }\n }\n\n private firstEntry(): NativeReadResourceResponse['contents'][number] {\n const [entry] = this.payload.contents ?? [];\n if (!entry) {\n throw new ResourceResponseError('Resource response did not include any contents', this.payload);\n }\n return entry;\n }\n}\n\nfunction hasStructuredContent(response: NativeCallToolResponse): response is NativeCallToolResponse & { structuredContent: Record<string, unknown> } {\n return Boolean((response as { structuredContent?: unknown }).structuredContent);\n}\n\nfunction isCompatibilityResult(response: NativeCallToolResponse): response is NativeCallToolResponse & { toolResult: unknown } {\n return hasOwn(response, 'toolResult');\n}\n\nfunction findFirstTextBlock(blocks: ContentBlock[] | undefined): TextContent | undefined {\n if (!Array.isArray(blocks)) {\n return undefined;\n }\n return blocks.find(isTextContent);\n}\n\nfunction collectPromptText(messages: PromptMessage[]): string[] {\n const segments: string[] = [];\n for (const message of messages) {\n const textBlock = isTextContent(message.content) ? message.content : undefined;\n if (textBlock) {\n segments.push(textBlock.text);\n }\n }\n return segments;\n}\n\nfunction isTextContent(block: unknown): block is TextContent {\n return Boolean(block) && typeof block === 'object' && (block as { type?: string }).type === 'text';\n}\n\nconst protoHasOwn = Object.prototype.hasOwnProperty;\n\nfunction hasOwn(target: object, key: PropertyKey): boolean {\n return protoHasOwn.call(target, key);\n}\n\nfunction formatErrorReason(error: unknown): string {\n if (error instanceof Error && typeof error.message === 'string') {\n return error.message;\n }\n if (typeof error === 'string') {\n return error;\n }\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n}\n"],"names":["Buffer","ToolResponseError","Error","message","response","name","ToolResponseWrapper","raw","payload","json","validator","value","resolveJsonPayload","assertValid","text","isCompatibilityResult","toolResult","throwIfError","textBlock","findFirstTextBlock","content","hasStructuredContent","structuredContent","JSON","parse","error","reason","formatErrorReason","isError","detail","String","PromptResponseError","PromptResponseWrapper","segments","collectPromptText","messages","length","join","textValue","parsed","ResourceResponseError","ResourceResponseWrapper","entry","firstEntry","blob","from","toString","contents","Boolean","hasOwn","blocks","Array","isArray","undefined","find","isTextContent","push","block","type","protoHasOwn","Object","prototype","hasOwnProperty","target","key","call","stringify"],"mappings":"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"}
@@ -4,7 +4,7 @@
4
4
  * Provides text-based search across tools, prompts, and resources
5
5
  * from connected MCP servers.
6
6
  */
7
- import type { Client } from '@modelcontextprotocol/sdk/client/index.js';
7
+ import type { Client } from '@modelcontextprotocol/client';
8
8
  import type { CapabilityIndex, SearchOptions, SearchResponse } from './types.js';
9
9
  export type CapabilityClient = Pick<Client, 'listTools' | 'listPrompts' | 'listResources'>;
10
10
  /**