@mcp-z/client 1.1.1 → 2.0.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 (68) hide show
  1. package/README.md +5 -1
  2. package/dist/cjs/auth/capability-discovery.js +18 -8
  3. package/dist/cjs/auth/capability-discovery.js.map +1 -1
  4. package/dist/cjs/auth/discovery-fetch.d.cts +35 -0
  5. package/dist/cjs/auth/discovery-fetch.d.ts +35 -0
  6. package/dist/cjs/auth/discovery-fetch.js +658 -0
  7. package/dist/cjs/auth/discovery-fetch.js.map +1 -0
  8. package/dist/cjs/auth/index.d.cts +1 -0
  9. package/dist/cjs/auth/index.d.ts +1 -0
  10. package/dist/cjs/auth/index.js +7 -0
  11. package/dist/cjs/auth/index.js.map +1 -1
  12. package/dist/cjs/auth/interactive-oauth-flow.d.cts +20 -13
  13. package/dist/cjs/auth/interactive-oauth-flow.d.ts +20 -13
  14. package/dist/cjs/auth/interactive-oauth-flow.js +48 -24
  15. package/dist/cjs/auth/interactive-oauth-flow.js.map +1 -1
  16. package/dist/cjs/auth/oauth-callback-listener.js +4 -0
  17. package/dist/cjs/auth/oauth-callback-listener.js.map +1 -1
  18. package/dist/cjs/auth/rfc9728-discovery.d.cts +12 -25
  19. package/dist/cjs/auth/rfc9728-discovery.d.ts +12 -25
  20. package/dist/cjs/auth/rfc9728-discovery.js +59 -48
  21. package/dist/cjs/auth/rfc9728-discovery.js.map +1 -1
  22. package/dist/cjs/auth/types.d.cts +34 -4
  23. package/dist/cjs/auth/types.d.ts +34 -4
  24. package/dist/cjs/auth/types.js.map +1 -1
  25. package/dist/cjs/dcr/dcr-authenticator.d.cts +9 -5
  26. package/dist/cjs/dcr/dcr-authenticator.d.ts +9 -5
  27. package/dist/cjs/dcr/dcr-authenticator.js +436 -43
  28. package/dist/cjs/dcr/dcr-authenticator.js.map +1 -1
  29. package/dist/cjs/dcr/dynamic-client-registrar.d.cts +7 -18
  30. package/dist/cjs/dcr/dynamic-client-registrar.d.ts +7 -18
  31. package/dist/cjs/dcr/dynamic-client-registrar.js +18 -25
  32. package/dist/cjs/dcr/dynamic-client-registrar.js.map +1 -1
  33. package/dist/cjs/index.d.cts +1 -0
  34. package/dist/cjs/index.d.ts +1 -0
  35. package/dist/cjs/index.js +7 -0
  36. package/dist/cjs/index.js.map +1 -1
  37. package/dist/cjs/lib/url-utils.js +2 -2
  38. package/dist/cjs/lib/url-utils.js.map +1 -1
  39. package/dist/esm/auth/capability-discovery.js +17 -7
  40. package/dist/esm/auth/capability-discovery.js.map +1 -1
  41. package/dist/esm/auth/discovery-fetch.d.ts +35 -0
  42. package/dist/esm/auth/discovery-fetch.js +184 -0
  43. package/dist/esm/auth/discovery-fetch.js.map +1 -0
  44. package/dist/esm/auth/index.d.ts +1 -0
  45. package/dist/esm/auth/index.js +1 -0
  46. package/dist/esm/auth/index.js.map +1 -1
  47. package/dist/esm/auth/interactive-oauth-flow.d.ts +20 -13
  48. package/dist/esm/auth/interactive-oauth-flow.js +50 -23
  49. package/dist/esm/auth/interactive-oauth-flow.js.map +1 -1
  50. package/dist/esm/auth/oauth-callback-listener.js +4 -0
  51. package/dist/esm/auth/oauth-callback-listener.js.map +1 -1
  52. package/dist/esm/auth/rfc9728-discovery.d.ts +12 -25
  53. package/dist/esm/auth/rfc9728-discovery.js +49 -56
  54. package/dist/esm/auth/rfc9728-discovery.js.map +1 -1
  55. package/dist/esm/auth/types.d.ts +34 -4
  56. package/dist/esm/auth/types.js.map +1 -1
  57. package/dist/esm/dcr/dcr-authenticator.d.ts +9 -5
  58. package/dist/esm/dcr/dcr-authenticator.js +92 -39
  59. package/dist/esm/dcr/dcr-authenticator.js.map +1 -1
  60. package/dist/esm/dcr/dynamic-client-registrar.d.ts +7 -18
  61. package/dist/esm/dcr/dynamic-client-registrar.js +19 -23
  62. package/dist/esm/dcr/dynamic-client-registrar.js.map +1 -1
  63. package/dist/esm/index.d.ts +1 -0
  64. package/dist/esm/index.js +1 -0
  65. package/dist/esm/index.js.map +1 -1
  66. package/dist/esm/lib/url-utils.js +2 -2
  67. package/dist/esm/lib/url-utils.js.map +1 -1
  68. package/package.json +3 -8
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @mcp-z/client
2
2
 
3
- Docs: https://mcp-z.github.io/client Programmatic MCP client for spawning, connecting to, and calling MCP servers.
3
+ Programmatic MCP client library for Node.js - connect, discover, and call tools on Model Context Protocol servers.
4
4
 
5
5
  ## Common uses
6
6
 
@@ -159,3 +159,7 @@ const client = await registry.connect('todoist', {
159
159
  ## Requirements
160
160
 
161
161
  - Node.js >= 22
162
+
163
+ ### Documentation
164
+
165
+ [API Docs](https://mcp-z.github.io/client)
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "probeAuthCapabilities", {
12
12
  }
13
13
  });
14
14
  var _urlutilsts = require("../lib/url-utils.js");
15
+ var _discoveryfetchts = require("./discovery-fetch.js");
15
16
  var _rfc9728discoveryts = require("./rfc9728-discovery.js");
16
17
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
17
18
  try {
@@ -175,8 +176,12 @@ function _ts_generator(thisArg, body) {
175
176
  */ function buildCapabilities(metadata, scopes) {
176
177
  var supportsDcr = !!metadata.registration_endpoint;
177
178
  var capabilities = {
178
- supportsDcr: supportsDcr
179
+ supportsDcr: supportsDcr,
180
+ authorizationResponseIssSupported: metadata.authorization_response_iss_parameter_supported === true
179
181
  };
182
+ if (metadata.issuer) {
183
+ capabilities.issuer = metadata.issuer;
184
+ }
180
185
  if (metadata.registration_endpoint) {
181
186
  capabilities.registrationEndpoint = metadata.registration_endpoint;
182
187
  }
@@ -194,7 +199,7 @@ function _ts_generator(thisArg, body) {
194
199
  }
195
200
  return capabilities;
196
201
  }
197
- function resolveCapabilitiesFromAuthorizationServer(authServerUrl, scopes) {
202
+ function resolveCapabilitiesFromAuthorizationServer(authServerUrl, scopes, allowLoopback) {
198
203
  return _async_to_generator(function() {
199
204
  var metadata;
200
205
  return _ts_generator(this, function(_state) {
@@ -202,7 +207,9 @@ function resolveCapabilitiesFromAuthorizationServer(authServerUrl, scopes) {
202
207
  case 0:
203
208
  return [
204
209
  4,
205
- (0, _rfc9728discoveryts.discoverAuthorizationServerMetadata)(authServerUrl)
210
+ (0, _rfc9728discoveryts.discoverAuthorizationServerMetadata)(authServerUrl, {
211
+ allowLoopback: allowLoopback
212
+ })
206
213
  ];
207
214
  case 1:
208
215
  metadata = _state.sent();
@@ -220,7 +227,7 @@ function resolveCapabilitiesFromAuthorizationServer(authServerUrl, scopes) {
220
227
  }
221
228
  function probeAuthCapabilities(baseUrl) {
222
229
  return _async_to_generator(function() {
223
- var normalizedBaseUrl, resourceMetadata, authServerUrl, capabilities, issuer, issuerCapabilities, issuer1, issuerCapabilities1, origin, originCapabilities, _error;
230
+ var normalizedBaseUrl, allowLoopback, resourceMetadata, authServerUrl, capabilities, issuer, issuerCapabilities, issuer1, issuerCapabilities1, origin, originCapabilities, _error;
224
231
  return _ts_generator(this, function(_state) {
225
232
  switch(_state.label){
226
233
  case 0:
@@ -231,6 +238,9 @@ function probeAuthCapabilities(baseUrl) {
231
238
  11
232
239
  ]);
233
240
  normalizedBaseUrl = (0, _urlutilsts.normalizeUrl)(baseUrl);
241
+ // Trust signal for every authorization-server fetch below: whether the
242
+ // configured MCP server is itself loopback, never a remote-supplied URL.
243
+ allowLoopback = (0, _discoveryfetchts.isLoopbackUrl)(normalizedBaseUrl);
234
244
  return [
235
245
  4,
236
246
  (0, _rfc9728discoveryts.discoverProtectedResourceMetadata)(normalizedBaseUrl)
@@ -255,7 +265,7 @@ function probeAuthCapabilities(baseUrl) {
255
265
  }
256
266
  return [
257
267
  4,
258
- resolveCapabilitiesFromAuthorizationServer(authServerUrl, resourceMetadata.scopes_supported)
268
+ resolveCapabilitiesFromAuthorizationServer(authServerUrl, resourceMetadata.scopes_supported, allowLoopback)
259
269
  ];
260
270
  case 2:
261
271
  capabilities = _state.sent();
@@ -277,7 +287,7 @@ function probeAuthCapabilities(baseUrl) {
277
287
  ];
278
288
  return [
279
289
  4,
280
- resolveCapabilitiesFromAuthorizationServer(issuer, resourceMetadata.scopes_supported)
290
+ resolveCapabilitiesFromAuthorizationServer(issuer, resourceMetadata.scopes_supported, allowLoopback)
281
291
  ];
282
292
  case 4:
283
293
  issuerCapabilities = _state.sent();
@@ -299,7 +309,7 @@ function probeAuthCapabilities(baseUrl) {
299
309
  ];
300
310
  return [
301
311
  4,
302
- resolveCapabilitiesFromAuthorizationServer(issuer1)
312
+ resolveCapabilitiesFromAuthorizationServer(issuer1, undefined, allowLoopback)
303
313
  ];
304
314
  case 7:
305
315
  issuerCapabilities1 = _state.sent();
@@ -314,7 +324,7 @@ function probeAuthCapabilities(baseUrl) {
314
324
  origin = getOrigin(normalizedBaseUrl);
315
325
  return [
316
326
  4,
317
- resolveCapabilitiesFromAuthorizationServer(origin)
327
+ resolveCapabilitiesFromAuthorizationServer(origin, undefined, allowLoopback)
318
328
  ];
319
329
  case 9:
320
330
  originCapabilities = _state.sent();
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/auth/capability-discovery.ts"],"sourcesContent":["/**\n * OAuth Server Capability Discovery\n * Probes RFC 9728 (Protected Resource) and RFC 8414 (Authorization Server) metadata\n */\n\nimport { normalizeUrl } from '../lib/url-utils.ts';\nimport { discoverAuthorizationServerIssuer, discoverAuthorizationServerMetadata, discoverProtectedResourceMetadata } from './rfc9728-discovery.ts';\nimport type { AuthCapabilities, AuthorizationServerMetadata } from './types.ts';\n\n/**\n * Extract origin (protocol + host) from a URL\n * @param url - Full URL that may include a path\n * @returns Origin (e.g., \"https://example.com\") or original string if invalid URL\n *\n * @example\n * getOrigin('https://example.com/mcp') // → 'https://example.com'\n * getOrigin('http://localhost:9999/api/v1/mcp') // → 'http://localhost:9999'\n */\nfunction getOrigin(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n // Invalid URL - return as-is for graceful degradation\n return url;\n }\n}\n\n/**\n * Probe OAuth server capabilities using RFC 9728 → RFC 8414 discovery chain\n * Returns capabilities including DCR support detection\n *\n * Discovery Strategy:\n * 1. Try RFC 9728 Protected Resource Metadata (supports cross-domain OAuth)\n * 2. If found, use first authorization_server to discover RFC 8414 Authorization Server Metadata\n * 3. Fall back to direct RFC 8414 discovery at resource origin\n *\n * @param baseUrl - Base URL of the protected resource (e.g., https://ai.todoist.net/mcp)\n * @returns AuthCapabilities object with discovered endpoints and features\n *\n * @example\n * // Todoist case: MCP at ai.todoist.net/mcp, OAuth at todoist.com\n * const caps = await probeAuthCapabilities('https://ai.todoist.net/mcp');\n * if (caps.supportsDcr) {\n * console.log('Registration endpoint:', caps.registrationEndpoint);\n * }\n */\nfunction buildCapabilities(metadata: AuthorizationServerMetadata, scopes?: string[]): AuthCapabilities {\n const supportsDcr = !!metadata.registration_endpoint;\n const capabilities: AuthCapabilities = { supportsDcr };\n\n if (metadata.registration_endpoint) {\n capabilities.registrationEndpoint = metadata.registration_endpoint;\n }\n if (metadata.authorization_endpoint) {\n capabilities.authorizationEndpoint = metadata.authorization_endpoint;\n }\n if (metadata.token_endpoint) capabilities.tokenEndpoint = metadata.token_endpoint;\n if (metadata.introspection_endpoint) {\n capabilities.introspectionEndpoint = metadata.introspection_endpoint;\n }\n\n if (scopes && scopes.length > 0) {\n capabilities.scopes = scopes;\n } else if (metadata.scopes_supported) {\n capabilities.scopes = metadata.scopes_supported;\n }\n\n return capabilities;\n}\n\nasync function resolveCapabilitiesFromAuthorizationServer(authServerUrl: string, scopes?: string[]): Promise<AuthCapabilities | null> {\n const metadata = await discoverAuthorizationServerMetadata(authServerUrl);\n if (!metadata) return null;\n return buildCapabilities(metadata, scopes);\n}\n\nexport async function probeAuthCapabilities(baseUrl: string): Promise<AuthCapabilities> {\n try {\n const normalizedBaseUrl = normalizeUrl(baseUrl);\n // Strategy 1: Try RFC 9728 Protected Resource Metadata discovery\n // This handles cross-domain OAuth (e.g., Todoist: ai.todoist.net/mcp → todoist.com)\n const resourceMetadata = await discoverProtectedResourceMetadata(normalizedBaseUrl);\n\n if (resourceMetadata && resourceMetadata.authorization_servers.length > 0) {\n // Found protected resource metadata with authorization servers\n // Discover the authorization server's metadata (RFC 8414)\n const authServerUrl = resourceMetadata.authorization_servers[0];\n if (!authServerUrl) {\n // Array has length > 0 but first element is undefined/null - skip this path\n return { supportsDcr: false };\n }\n const capabilities = await resolveCapabilitiesFromAuthorizationServer(authServerUrl, resourceMetadata.scopes_supported);\n if (capabilities) {\n return capabilities;\n }\n\n const issuer = await discoverAuthorizationServerIssuer(baseUrl);\n if (issuer) {\n const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer, resourceMetadata.scopes_supported);\n if (issuerCapabilities) return issuerCapabilities;\n }\n }\n\n const issuer = await discoverAuthorizationServerIssuer(normalizedBaseUrl);\n if (issuer) {\n const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer);\n if (issuerCapabilities) return issuerCapabilities;\n }\n\n // Strategy 2: Fall back to direct RFC 8414 discovery at resource origin\n // This handles same-domain OAuth (traditional setup)\n const origin = getOrigin(normalizedBaseUrl);\n const originCapabilities = await resolveCapabilitiesFromAuthorizationServer(origin);\n if (originCapabilities) return originCapabilities;\n\n // No OAuth metadata found\n return { supportsDcr: false };\n } catch (_error) {\n // Network error, invalid JSON, or other fetch failure\n // Gracefully degrade - assume no DCR support\n return { supportsDcr: false };\n }\n}\n"],"names":["probeAuthCapabilities","getOrigin","url","URL","origin","buildCapabilities","metadata","scopes","supportsDcr","registration_endpoint","capabilities","registrationEndpoint","authorization_endpoint","authorizationEndpoint","token_endpoint","tokenEndpoint","introspection_endpoint","introspectionEndpoint","length","scopes_supported","resolveCapabilitiesFromAuthorizationServer","authServerUrl","discoverAuthorizationServerMetadata","baseUrl","normalizedBaseUrl","resourceMetadata","issuer","issuerCapabilities","originCapabilities","_error","normalizeUrl","discoverProtectedResourceMetadata","authorization_servers","discoverAuthorizationServerIssuer"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/auth/capability-discovery.ts"],"sourcesContent":["/**\n * OAuth Server Capability Discovery\n * Probes RFC 9728 (Protected Resource) and RFC 8414 (Authorization Server) metadata\n */\n\nimport { normalizeUrl } from '../lib/url-utils.ts';\nimport { isLoopbackUrl } from './discovery-fetch.ts';\nimport { discoverAuthorizationServerIssuer, discoverAuthorizationServerMetadata, discoverProtectedResourceMetadata } from './rfc9728-discovery.ts';\nimport type { AuthCapabilities, AuthorizationServerMetadata } from './types.ts';\n\n/**\n * Extract origin (protocol + host) from a URL\n * @param url - Full URL that may include a path\n * @returns Origin (e.g., \"https://example.com\") or original string if invalid URL\n *\n * @example\n * getOrigin('https://example.com/mcp') // → 'https://example.com'\n * getOrigin('http://localhost:9999/api/v1/mcp') // → 'http://localhost:9999'\n */\nfunction getOrigin(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n // Invalid URL - return as-is for graceful degradation\n return url;\n }\n}\n\n/**\n * Probe OAuth server capabilities using RFC 9728 → RFC 8414 discovery chain\n * Returns capabilities including DCR support detection\n *\n * Discovery Strategy:\n * 1. Try RFC 9728 Protected Resource Metadata (supports cross-domain OAuth)\n * 2. If found, use first authorization_server to discover RFC 8414 Authorization Server Metadata\n * 3. Fall back to direct RFC 8414 discovery at resource origin\n *\n * @param baseUrl - Base URL of the protected resource (e.g., https://ai.todoist.net/mcp)\n * @returns AuthCapabilities object with discovered endpoints and features\n *\n * @example\n * // Todoist case: MCP at ai.todoist.net/mcp, OAuth at todoist.com\n * const caps = await probeAuthCapabilities('https://ai.todoist.net/mcp');\n * if (caps.supportsDcr) {\n * console.log('Registration endpoint:', caps.registrationEndpoint);\n * }\n */\nfunction buildCapabilities(metadata: AuthorizationServerMetadata, scopes?: string[]): AuthCapabilities {\n const supportsDcr = !!metadata.registration_endpoint;\n const capabilities: AuthCapabilities = { supportsDcr, authorizationResponseIssSupported: metadata.authorization_response_iss_parameter_supported === true };\n\n if (metadata.issuer) {\n capabilities.issuer = metadata.issuer;\n }\n\n if (metadata.registration_endpoint) {\n capabilities.registrationEndpoint = metadata.registration_endpoint;\n }\n if (metadata.authorization_endpoint) {\n capabilities.authorizationEndpoint = metadata.authorization_endpoint;\n }\n if (metadata.token_endpoint) capabilities.tokenEndpoint = metadata.token_endpoint;\n if (metadata.introspection_endpoint) {\n capabilities.introspectionEndpoint = metadata.introspection_endpoint;\n }\n\n if (scopes && scopes.length > 0) {\n capabilities.scopes = scopes;\n } else if (metadata.scopes_supported) {\n capabilities.scopes = metadata.scopes_supported;\n }\n\n return capabilities;\n}\n\nasync function resolveCapabilitiesFromAuthorizationServer(authServerUrl: string, scopes: string[] | undefined, allowLoopback: boolean): Promise<AuthCapabilities | null> {\n const metadata = await discoverAuthorizationServerMetadata(authServerUrl, { allowLoopback });\n if (!metadata) return null;\n return buildCapabilities(metadata, scopes);\n}\n\nexport async function probeAuthCapabilities(baseUrl: string): Promise<AuthCapabilities> {\n try {\n const normalizedBaseUrl = normalizeUrl(baseUrl);\n // Trust signal for every authorization-server fetch below: whether the\n // configured MCP server is itself loopback, never a remote-supplied URL.\n const allowLoopback = isLoopbackUrl(normalizedBaseUrl);\n // Strategy 1: Try RFC 9728 Protected Resource Metadata discovery\n // This handles cross-domain OAuth (e.g., Todoist: ai.todoist.net/mcp → todoist.com)\n const resourceMetadata = await discoverProtectedResourceMetadata(normalizedBaseUrl);\n\n if (resourceMetadata && resourceMetadata.authorization_servers.length > 0) {\n // Found protected resource metadata with authorization servers\n // Discover the authorization server's metadata (RFC 8414)\n const authServerUrl = resourceMetadata.authorization_servers[0];\n if (!authServerUrl) {\n // Array has length > 0 but first element is undefined/null - skip this path\n return { supportsDcr: false };\n }\n const capabilities = await resolveCapabilitiesFromAuthorizationServer(authServerUrl, resourceMetadata.scopes_supported, allowLoopback);\n if (capabilities) {\n return capabilities;\n }\n\n const issuer = await discoverAuthorizationServerIssuer(baseUrl);\n if (issuer) {\n const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer, resourceMetadata.scopes_supported, allowLoopback);\n if (issuerCapabilities) return issuerCapabilities;\n }\n }\n\n const issuer = await discoverAuthorizationServerIssuer(normalizedBaseUrl);\n if (issuer) {\n const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer, undefined, allowLoopback);\n if (issuerCapabilities) return issuerCapabilities;\n }\n\n // Strategy 2: Fall back to direct RFC 8414 discovery at resource origin\n // This handles same-domain OAuth (traditional setup)\n const origin = getOrigin(normalizedBaseUrl);\n const originCapabilities = await resolveCapabilitiesFromAuthorizationServer(origin, undefined, allowLoopback);\n if (originCapabilities) return originCapabilities;\n\n // No OAuth metadata found\n return { supportsDcr: false };\n } catch (_error) {\n // Network error, invalid JSON, or other fetch failure\n // Gracefully degrade - assume no DCR support\n return { supportsDcr: false };\n }\n}\n"],"names":["probeAuthCapabilities","getOrigin","url","URL","origin","buildCapabilities","metadata","scopes","supportsDcr","registration_endpoint","capabilities","authorizationResponseIssSupported","authorization_response_iss_parameter_supported","issuer","registrationEndpoint","authorization_endpoint","authorizationEndpoint","token_endpoint","tokenEndpoint","introspection_endpoint","introspectionEndpoint","length","scopes_supported","resolveCapabilitiesFromAuthorizationServer","authServerUrl","allowLoopback","discoverAuthorizationServerMetadata","baseUrl","normalizedBaseUrl","resourceMetadata","issuerCapabilities","originCapabilities","_error","normalizeUrl","isLoopbackUrl","discoverProtectedResourceMetadata","authorization_servers","discoverAuthorizationServerIssuer","undefined"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ export declare class DiscoveryFetchError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ /**
5
+ * True if `rawUrl`'s host is loopback, used to decide `allowLoopback` grants
6
+ * from the URL the caller configured, not remote-supplied data. Fails closed on an unparseable URL.
7
+ */
8
+ export declare function isLoopbackUrl(rawUrl: string): boolean;
9
+ export interface DiscoveryFetchOptions {
10
+ /**
11
+ * Loopback trust grant for this fetch, computed from the server the caller
12
+ * is actually talking to - never from the URL being fetched. Defaults to `false`.
13
+ */
14
+ allowLoopback?: boolean;
15
+ /**
16
+ * Per-request connect/read timeout in ms. Defaults to `DEFAULT_TIMEOUT_MS`;
17
+ * overridable so tests can bound slow-failure cases.
18
+ */
19
+ timeoutMs?: number;
20
+ }
21
+ /**
22
+ * Fetches an OAuth-discovery URL with SSRF mitigations applied to the
23
+ * initial URL and every redirect hop; redirects are validated, not auto-followed.
24
+ *
25
+ * @param rawUrl - URL to fetch; may be remote-server-supplied (the threat this guards against).
26
+ * @param init - Standard fetch options; `redirect` is always forced to `'manual'`.
27
+ * @param context - Short label used only in error messages, never echoing the URL.
28
+ * @param options - See `DiscoveryFetchOptions`.
29
+ */
30
+ export declare function discoveryFetch(rawUrl: string, init?: RequestInit, context?: string, options?: DiscoveryFetchOptions): Promise<Response>;
31
+ /**
32
+ * Parse a `discoveryFetch` response body as JSON, enforcing `MAX_BODY_BYTES`
33
+ * while reading (not after buffering the whole thing).
34
+ */
35
+ export declare function readDiscoveryJson<T>(response: Response, context: string): Promise<T>;
@@ -0,0 +1,35 @@
1
+ export declare class DiscoveryFetchError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ /**
5
+ * True if `rawUrl`'s host is loopback, used to decide `allowLoopback` grants
6
+ * from the URL the caller configured, not remote-supplied data. Fails closed on an unparseable URL.
7
+ */
8
+ export declare function isLoopbackUrl(rawUrl: string): boolean;
9
+ export interface DiscoveryFetchOptions {
10
+ /**
11
+ * Loopback trust grant for this fetch, computed from the server the caller
12
+ * is actually talking to - never from the URL being fetched. Defaults to `false`.
13
+ */
14
+ allowLoopback?: boolean;
15
+ /**
16
+ * Per-request connect/read timeout in ms. Defaults to `DEFAULT_TIMEOUT_MS`;
17
+ * overridable so tests can bound slow-failure cases.
18
+ */
19
+ timeoutMs?: number;
20
+ }
21
+ /**
22
+ * Fetches an OAuth-discovery URL with SSRF mitigations applied to the
23
+ * initial URL and every redirect hop; redirects are validated, not auto-followed.
24
+ *
25
+ * @param rawUrl - URL to fetch; may be remote-server-supplied (the threat this guards against).
26
+ * @param init - Standard fetch options; `redirect` is always forced to `'manual'`.
27
+ * @param context - Short label used only in error messages, never echoing the URL.
28
+ * @param options - See `DiscoveryFetchOptions`.
29
+ */
30
+ export declare function discoveryFetch(rawUrl: string, init?: RequestInit, context?: string, options?: DiscoveryFetchOptions): Promise<Response>;
31
+ /**
32
+ * Parse a `discoveryFetch` response body as JSON, enforcing `MAX_BODY_BYTES`
33
+ * while reading (not after buffering the whole thing).
34
+ */
35
+ export declare function readDiscoveryJson<T>(response: Response, context: string): Promise<T>;