@mcp-z/client 1.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.
- package/AGENTS.md +159 -0
- package/LICENSE +21 -0
- package/README.md +90 -0
- package/dist/cjs/auth/capability-discovery.d.cts +25 -0
- package/dist/cjs/auth/capability-discovery.d.ts +25 -0
- package/dist/cjs/auth/capability-discovery.js +280 -0
- package/dist/cjs/auth/capability-discovery.js.map +1 -0
- package/dist/cjs/auth/index.d.cts +9 -0
- package/dist/cjs/auth/index.d.ts +9 -0
- package/dist/cjs/auth/index.js +28 -0
- package/dist/cjs/auth/index.js.map +1 -0
- package/dist/cjs/auth/interactive-oauth-flow.d.cts +58 -0
- package/dist/cjs/auth/interactive-oauth-flow.d.ts +58 -0
- package/dist/cjs/auth/interactive-oauth-flow.js +537 -0
- package/dist/cjs/auth/interactive-oauth-flow.js.map +1 -0
- package/dist/cjs/auth/oauth-callback-listener.d.cts +56 -0
- package/dist/cjs/auth/oauth-callback-listener.d.ts +56 -0
- package/dist/cjs/auth/oauth-callback-listener.js +333 -0
- package/dist/cjs/auth/oauth-callback-listener.js.map +1 -0
- package/dist/cjs/auth/pkce.d.cts +17 -0
- package/dist/cjs/auth/pkce.d.ts +17 -0
- package/dist/cjs/auth/pkce.js +192 -0
- package/dist/cjs/auth/pkce.js.map +1 -0
- package/dist/cjs/auth/rfc9728-discovery.d.cts +34 -0
- package/dist/cjs/auth/rfc9728-discovery.d.ts +34 -0
- package/dist/cjs/auth/rfc9728-discovery.js +436 -0
- package/dist/cjs/auth/rfc9728-discovery.js.map +1 -0
- package/dist/cjs/auth/types.d.cts +137 -0
- package/dist/cjs/auth/types.d.ts +137 -0
- package/dist/cjs/auth/types.js +9 -0
- package/dist/cjs/auth/types.js.map +1 -0
- package/dist/cjs/client-helpers.d.cts +55 -0
- package/dist/cjs/client-helpers.d.ts +55 -0
- package/dist/cjs/client-helpers.js +128 -0
- package/dist/cjs/client-helpers.js.map +1 -0
- package/dist/cjs/config/server-loader.d.cts +27 -0
- package/dist/cjs/config/server-loader.d.ts +27 -0
- package/dist/cjs/config/server-loader.js +111 -0
- package/dist/cjs/config/server-loader.js.map +1 -0
- package/dist/cjs/config/validate-config.d.cts +15 -0
- package/dist/cjs/config/validate-config.d.ts +15 -0
- package/dist/cjs/config/validate-config.js +128 -0
- package/dist/cjs/config/validate-config.js.map +1 -0
- package/dist/cjs/connection/connect-client.d.cts +59 -0
- package/dist/cjs/connection/connect-client.d.ts +59 -0
- package/dist/cjs/connection/connect-client.js +536 -0
- package/dist/cjs/connection/connect-client.js.map +1 -0
- package/dist/cjs/connection/existing-process-transport.d.cts +40 -0
- package/dist/cjs/connection/existing-process-transport.d.ts +40 -0
- package/dist/cjs/connection/existing-process-transport.js +274 -0
- package/dist/cjs/connection/existing-process-transport.js.map +1 -0
- package/dist/cjs/connection/types.d.cts +61 -0
- package/dist/cjs/connection/types.d.ts +61 -0
- package/dist/cjs/connection/types.js +53 -0
- package/dist/cjs/connection/types.js.map +1 -0
- package/dist/cjs/connection/wait-for-http-ready.d.cts +15 -0
- package/dist/cjs/connection/wait-for-http-ready.d.ts +15 -0
- package/dist/cjs/connection/wait-for-http-ready.js +232 -0
- package/dist/cjs/connection/wait-for-http-ready.js.map +1 -0
- package/dist/cjs/dcr/dcr-authenticator.d.cts +73 -0
- package/dist/cjs/dcr/dcr-authenticator.d.ts +73 -0
- package/dist/cjs/dcr/dcr-authenticator.js +655 -0
- package/dist/cjs/dcr/dcr-authenticator.js.map +1 -0
- package/dist/cjs/dcr/dynamic-client-registrar.d.cts +28 -0
- package/dist/cjs/dcr/dynamic-client-registrar.d.ts +28 -0
- package/dist/cjs/dcr/dynamic-client-registrar.js +245 -0
- package/dist/cjs/dcr/dynamic-client-registrar.js.map +1 -0
- package/dist/cjs/dcr/index.d.cts +8 -0
- package/dist/cjs/dcr/index.d.ts +8 -0
- package/dist/cjs/dcr/index.js +24 -0
- package/dist/cjs/dcr/index.js.map +1 -0
- package/dist/cjs/index.d.cts +21 -0
- package/dist/cjs/index.d.ts +21 -0
- package/dist/cjs/index.js +94 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/monkey-patches.d.cts +6 -0
- package/dist/cjs/monkey-patches.d.ts +6 -0
- package/dist/cjs/monkey-patches.js +236 -0
- package/dist/cjs/monkey-patches.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/response-wrappers.d.cts +41 -0
- package/dist/cjs/response-wrappers.d.ts +41 -0
- package/dist/cjs/response-wrappers.js +443 -0
- package/dist/cjs/response-wrappers.js.map +1 -0
- package/dist/cjs/search/index.d.cts +6 -0
- package/dist/cjs/search/index.d.ts +6 -0
- package/dist/cjs/search/index.js +25 -0
- package/dist/cjs/search/index.js.map +1 -0
- package/dist/cjs/search/search.d.cts +22 -0
- package/dist/cjs/search/search.d.ts +22 -0
- package/dist/cjs/search/search.js +630 -0
- package/dist/cjs/search/search.js.map +1 -0
- package/dist/cjs/search/types.d.cts +122 -0
- package/dist/cjs/search/types.d.ts +122 -0
- package/dist/cjs/search/types.js +10 -0
- package/dist/cjs/search/types.js.map +1 -0
- package/dist/cjs/spawn/spawn-server.d.cts +83 -0
- package/dist/cjs/spawn/spawn-server.d.ts +83 -0
- package/dist/cjs/spawn/spawn-server.js +410 -0
- package/dist/cjs/spawn/spawn-server.js.map +1 -0
- package/dist/cjs/spawn/spawn-servers.d.cts +151 -0
- package/dist/cjs/spawn/spawn-servers.d.ts +151 -0
- package/dist/cjs/spawn/spawn-servers.js +911 -0
- package/dist/cjs/spawn/spawn-servers.js.map +1 -0
- package/dist/cjs/types.d.cts +11 -0
- package/dist/cjs/types.d.ts +11 -0
- package/dist/cjs/types.js +10 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/utils/logger.d.cts +24 -0
- package/dist/cjs/utils/logger.d.ts +24 -0
- package/dist/cjs/utils/logger.js +80 -0
- package/dist/cjs/utils/logger.js.map +1 -0
- package/dist/cjs/utils/path-utils.d.cts +45 -0
- package/dist/cjs/utils/path-utils.d.ts +45 -0
- package/dist/cjs/utils/path-utils.js +158 -0
- package/dist/cjs/utils/path-utils.js.map +1 -0
- package/dist/cjs/utils/sanitizer.d.cts +30 -0
- package/dist/cjs/utils/sanitizer.d.ts +30 -0
- package/dist/cjs/utils/sanitizer.js +124 -0
- package/dist/cjs/utils/sanitizer.js.map +1 -0
- package/dist/esm/auth/capability-discovery.d.ts +25 -0
- package/dist/esm/auth/capability-discovery.js +110 -0
- package/dist/esm/auth/capability-discovery.js.map +1 -0
- package/dist/esm/auth/index.d.ts +9 -0
- package/dist/esm/auth/index.js +6 -0
- package/dist/esm/auth/index.js.map +1 -0
- package/dist/esm/auth/interactive-oauth-flow.d.ts +58 -0
- package/dist/esm/auth/interactive-oauth-flow.js +217 -0
- package/dist/esm/auth/interactive-oauth-flow.js.map +1 -0
- package/dist/esm/auth/oauth-callback-listener.d.ts +56 -0
- package/dist/esm/auth/oauth-callback-listener.js +166 -0
- package/dist/esm/auth/oauth-callback-listener.js.map +1 -0
- package/dist/esm/auth/pkce.d.ts +17 -0
- package/dist/esm/auth/pkce.js +41 -0
- package/dist/esm/auth/pkce.js.map +1 -0
- package/dist/esm/auth/rfc9728-discovery.d.ts +34 -0
- package/dist/esm/auth/rfc9728-discovery.js +157 -0
- package/dist/esm/auth/rfc9728-discovery.js.map +1 -0
- package/dist/esm/auth/types.d.ts +137 -0
- package/dist/esm/auth/types.js +7 -0
- package/dist/esm/auth/types.js.map +1 -0
- package/dist/esm/client-helpers.d.ts +55 -0
- package/dist/esm/client-helpers.js +81 -0
- package/dist/esm/client-helpers.js.map +1 -0
- package/dist/esm/config/server-loader.d.ts +27 -0
- package/dist/esm/config/server-loader.js +49 -0
- package/dist/esm/config/server-loader.js.map +1 -0
- package/dist/esm/config/validate-config.d.ts +15 -0
- package/dist/esm/config/validate-config.js +76 -0
- package/dist/esm/config/validate-config.js.map +1 -0
- package/dist/esm/connection/connect-client.d.ts +59 -0
- package/dist/esm/connection/connect-client.js +272 -0
- package/dist/esm/connection/connect-client.js.map +1 -0
- package/dist/esm/connection/existing-process-transport.d.ts +40 -0
- package/dist/esm/connection/existing-process-transport.js +103 -0
- package/dist/esm/connection/existing-process-transport.js.map +1 -0
- package/dist/esm/connection/types.d.ts +61 -0
- package/dist/esm/connection/types.js +34 -0
- package/dist/esm/connection/types.js.map +1 -0
- package/dist/esm/connection/wait-for-http-ready.d.ts +15 -0
- package/dist/esm/connection/wait-for-http-ready.js +43 -0
- package/dist/esm/connection/wait-for-http-ready.js.map +1 -0
- package/dist/esm/dcr/dcr-authenticator.d.ts +73 -0
- package/dist/esm/dcr/dcr-authenticator.js +235 -0
- package/dist/esm/dcr/dcr-authenticator.js.map +1 -0
- package/dist/esm/dcr/dynamic-client-registrar.d.ts +28 -0
- package/dist/esm/dcr/dynamic-client-registrar.js +66 -0
- package/dist/esm/dcr/dynamic-client-registrar.js.map +1 -0
- package/dist/esm/dcr/index.d.ts +8 -0
- package/dist/esm/dcr/index.js +5 -0
- package/dist/esm/dcr/index.js.map +1 -0
- package/dist/esm/index.d.ts +21 -0
- package/dist/esm/index.js +22 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/monkey-patches.d.ts +6 -0
- package/dist/esm/monkey-patches.js +32 -0
- package/dist/esm/monkey-patches.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/response-wrappers.d.ts +41 -0
- package/dist/esm/response-wrappers.js +201 -0
- package/dist/esm/response-wrappers.js.map +1 -0
- package/dist/esm/search/index.d.ts +6 -0
- package/dist/esm/search/index.js +3 -0
- package/dist/esm/search/index.js.map +1 -0
- package/dist/esm/search/search.d.ts +22 -0
- package/dist/esm/search/search.js +236 -0
- package/dist/esm/search/search.js.map +1 -0
- package/dist/esm/search/types.d.ts +122 -0
- package/dist/esm/search/types.js +8 -0
- package/dist/esm/search/types.js.map +1 -0
- package/dist/esm/spawn/spawn-server.d.ts +83 -0
- package/dist/esm/spawn/spawn-server.js +145 -0
- package/dist/esm/spawn/spawn-server.js.map +1 -0
- package/dist/esm/spawn/spawn-servers.d.ts +151 -0
- package/dist/esm/spawn/spawn-servers.js +406 -0
- package/dist/esm/spawn/spawn-servers.js.map +1 -0
- package/dist/esm/types.d.ts +11 -0
- package/dist/esm/types.js +9 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/utils/logger.d.ts +24 -0
- package/dist/esm/utils/logger.js +59 -0
- package/dist/esm/utils/logger.js.map +1 -0
- package/dist/esm/utils/path-utils.d.ts +45 -0
- package/dist/esm/utils/path-utils.js +89 -0
- package/dist/esm/utils/path-utils.js.map +1 -0
- package/dist/esm/utils/sanitizer.d.ts +30 -0
- package/dist/esm/utils/sanitizer.js +43 -0
- package/dist/esm/utils/sanitizer.js.map +1 -0
- package/package.json +92 -0
- package/schemas/servers.d.ts +90 -0
- package/schemas/servers.schema.json +104 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/connection/wait-for-http-ready.ts"],"sourcesContent":["/**\n * wait-for-http-ready.ts\n *\n * Utility for waiting for HTTP servers to become ready.\n */\n\n/**\n * Wait for HTTP server to be ready by checking endpoint accessibility.\n * Used to handle HTTP servers that need time to start listening.\n *\n * @param url - URL to check for server readiness\n * @param timeoutMs - Maximum time to wait in milliseconds (default: 30000)\n * @returns Promise that resolves when server is ready\n * @throws Error if server doesn't become ready within timeout\n */\nexport async function waitForHttpReady(url: string, timeoutMs = 30000): Promise<void> {\n const start = Date.now();\n const maxRetries = Math.ceil(timeoutMs / 100); // Check every 100ms\n\n for (let i = 0; i < maxRetries; i++) {\n try {\n // Use HEAD request to check server is responding\n const response = await fetch(url, {\n method: 'HEAD',\n headers: { Connection: 'close' },\n signal: AbortSignal.timeout(500), // 500ms per attempt\n });\n\n // Server is responding if we get any HTTP status\n if (response.status >= 200 && response.status < 500) {\n return;\n }\n\n // Server error (5xx) - keep trying as it might still be starting\n } catch (_error) {\n // Connection refused, timeout, or network error\n // Server not ready yet, continue polling\n if (i === maxRetries - 1) {\n const elapsed = Date.now() - start;\n throw new Error(`HTTP server ${url} not ready after ${elapsed}ms`);\n }\n }\n\n // Wait before retry\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n\n throw new Error(`HTTP server ${url} not ready after ${timeoutMs}ms`);\n}\n"],"names":["waitForHttpReady","url","timeoutMs","start","Date","now","maxRetries","Math","ceil","i","response","fetch","method","headers","Connection","signal","AbortSignal","timeout","status","_error","elapsed","Error","Promise","resolve","setTimeout"],"mappings":"AAAA;;;;CAIC,GAED;;;;;;;;CAQC,GACD,OAAO,eAAeA,iBAAiBC,GAAW,EAAEC,YAAY,KAAK;IACnE,MAAMC,QAAQC,KAAKC,GAAG;IACtB,MAAMC,aAAaC,KAAKC,IAAI,CAACN,YAAY,MAAM,oBAAoB;IAEnE,IAAK,IAAIO,IAAI,GAAGA,IAAIH,YAAYG,IAAK;QACnC,IAAI;YACF,iDAAiD;YACjD,MAAMC,WAAW,MAAMC,MAAMV,KAAK;gBAChCW,QAAQ;gBACRC,SAAS;oBAAEC,YAAY;gBAAQ;gBAC/BC,QAAQC,YAAYC,OAAO,CAAC;YAC9B;YAEA,iDAAiD;YACjD,IAAIP,SAASQ,MAAM,IAAI,OAAOR,SAASQ,MAAM,GAAG,KAAK;gBACnD;YACF;QAEA,iEAAiE;QACnE,EAAE,OAAOC,QAAQ;YACf,gDAAgD;YAChD,yCAAyC;YACzC,IAAIV,MAAMH,aAAa,GAAG;gBACxB,MAAMc,UAAUhB,KAAKC,GAAG,KAAKF;gBAC7B,MAAM,IAAIkB,MAAM,CAAC,YAAY,EAAEpB,IAAI,iBAAiB,EAAEmB,QAAQ,EAAE,CAAC;YACnE;QACF;QAEA,oBAAoB;QACpB,MAAM,IAAIE,QAAQ,CAACC,UAAYC,WAAWD,SAAS;IACrD;IAEA,MAAM,IAAIF,MAAM,CAAC,YAAY,EAAEpB,IAAI,iBAAiB,EAAEC,UAAU,EAAE,CAAC;AACrE"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCR Authenticator
|
|
3
|
+
* Consolidates DCR and OAuth flow logic for MCP HTTP servers
|
|
4
|
+
*/
|
|
5
|
+
import Keyv from 'keyv';
|
|
6
|
+
import type { AuthCapabilities, TokenSet } from '../auth/types.js';
|
|
7
|
+
import { type Logger } from '../utils/logger.js';
|
|
8
|
+
/**
|
|
9
|
+
* DcrAuthenticator configuration options
|
|
10
|
+
*/
|
|
11
|
+
export interface DcrAuthenticatorOptions {
|
|
12
|
+
/** Custom Keyv store (for testing) - if not provided, uses default ~/.mcpeasy/tokens.json */
|
|
13
|
+
tokenStore?: Keyv;
|
|
14
|
+
/** Headless mode (don't open browser) */
|
|
15
|
+
headless?: boolean;
|
|
16
|
+
/** Required redirect URI for OAuth callback */
|
|
17
|
+
redirectUri: string;
|
|
18
|
+
/** Optional logger for debug output (defaults to singleton logger) */
|
|
19
|
+
logger?: Logger;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* DcrAuthenticator manages authentication for MCP HTTP servers
|
|
23
|
+
* Handles DCR registration, OAuth flows, and token management
|
|
24
|
+
*/
|
|
25
|
+
export declare class DcrAuthenticator {
|
|
26
|
+
private tokenStore;
|
|
27
|
+
private dcrClient;
|
|
28
|
+
private oauthFlow;
|
|
29
|
+
private headless;
|
|
30
|
+
private redirectUri;
|
|
31
|
+
private logger;
|
|
32
|
+
constructor(options: DcrAuthenticatorOptions);
|
|
33
|
+
/**
|
|
34
|
+
* Detect if server is self-hosted DCR (vs external OAuth provider)
|
|
35
|
+
* Self-hosted servers have their own OAuth endpoints and manage token storage
|
|
36
|
+
*/
|
|
37
|
+
private detectSelfHostedMode;
|
|
38
|
+
/**
|
|
39
|
+
* Ensure server is authenticated, performing DCR and OAuth if needed
|
|
40
|
+
* Proactively refreshes tokens if they're within 5 minutes of expiry
|
|
41
|
+
*
|
|
42
|
+
* @param baseUrl - Base URL of the server (e.g., https://example.com)
|
|
43
|
+
* @param capabilities - Auth capabilities from .well-known endpoint
|
|
44
|
+
* @returns Valid token set ready to use
|
|
45
|
+
*
|
|
46
|
+
* @throws Error if authentication fails
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* const authenticator = new DcrAuthenticator({ redirectUri: 'http://localhost:3000/callback' });
|
|
50
|
+
* const tokens = await authenticator.ensureAuthenticated(
|
|
51
|
+
* 'https://example.com',
|
|
52
|
+
* capabilities
|
|
53
|
+
* );
|
|
54
|
+
*/
|
|
55
|
+
ensureAuthenticated(baseUrl: string, capabilities: AuthCapabilities): Promise<TokenSet>;
|
|
56
|
+
/**
|
|
57
|
+
* Handle authentication for self-hosted DCR servers
|
|
58
|
+
* Self-hosted servers manage their own token storage via /oauth/verify
|
|
59
|
+
*/
|
|
60
|
+
private ensureAuthenticatedSelfHosted;
|
|
61
|
+
/**
|
|
62
|
+
* Handle authentication for external OAuth providers (original implementation)
|
|
63
|
+
*/
|
|
64
|
+
private ensureAuthenticatedExternal;
|
|
65
|
+
/**
|
|
66
|
+
* Refresh access token using refresh token
|
|
67
|
+
*/
|
|
68
|
+
private refreshTokens;
|
|
69
|
+
/**
|
|
70
|
+
* Delete stored tokens for a server
|
|
71
|
+
*/
|
|
72
|
+
deleteTokens(baseUrl: string): Promise<void>;
|
|
73
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCR Authenticator
|
|
3
|
+
* Consolidates DCR and OAuth flow logic for MCP HTTP servers
|
|
4
|
+
*/ import path from 'node:path';
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import Keyv from 'keyv';
|
|
7
|
+
import { KeyvFile } from 'keyv-file';
|
|
8
|
+
import { InteractiveOAuthFlow } from '../auth/interactive-oauth-flow.js';
|
|
9
|
+
import { logger as defaultLogger } from '../utils/logger.js';
|
|
10
|
+
import { DynamicClientRegistrar } from './dynamic-client-registrar.js';
|
|
11
|
+
/**
|
|
12
|
+
* Buffer time before token expiry to trigger proactive refresh (5 minutes)
|
|
13
|
+
*/ const REFRESH_BUFFER_MS = 5 * 60 * 1000;
|
|
14
|
+
/**
|
|
15
|
+
* DcrAuthenticator manages authentication for MCP HTTP servers
|
|
16
|
+
* Handles DCR registration, OAuth flows, and token management
|
|
17
|
+
*/ export class DcrAuthenticator {
|
|
18
|
+
/**
|
|
19
|
+
* Detect if server is self-hosted DCR (vs external OAuth provider)
|
|
20
|
+
* Self-hosted servers have their own OAuth endpoints and manage token storage
|
|
21
|
+
*/ async detectSelfHostedMode(baseUrl) {
|
|
22
|
+
try {
|
|
23
|
+
// Self-hosted DCR servers typically run their own OAuth server
|
|
24
|
+
// Check if this is a self-hosted instance by testing OAuth metadata
|
|
25
|
+
// For now, assume self-hosted if baseUrl matches common localhost patterns
|
|
26
|
+
// TODO: Implement proper self-hosted detection logic
|
|
27
|
+
return baseUrl.includes('localhost') || baseUrl.includes('127.0.0.1');
|
|
28
|
+
} catch (_error) {
|
|
29
|
+
return false; // Assume external mode if detection fails
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Ensure server is authenticated, performing DCR and OAuth if needed
|
|
34
|
+
* Proactively refreshes tokens if they're within 5 minutes of expiry
|
|
35
|
+
*
|
|
36
|
+
* @param baseUrl - Base URL of the server (e.g., https://example.com)
|
|
37
|
+
* @param capabilities - Auth capabilities from .well-known endpoint
|
|
38
|
+
* @returns Valid token set ready to use
|
|
39
|
+
*
|
|
40
|
+
* @throws Error if authentication fails
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* const authenticator = new DcrAuthenticator({ redirectUri: 'http://localhost:3000/callback' });
|
|
44
|
+
* const tokens = await authenticator.ensureAuthenticated(
|
|
45
|
+
* 'https://example.com',
|
|
46
|
+
* capabilities
|
|
47
|
+
* );
|
|
48
|
+
*/ async ensureAuthenticated(baseUrl, capabilities) {
|
|
49
|
+
// Auto-detect server mode
|
|
50
|
+
const isSelfHosted = await this.detectSelfHostedMode(baseUrl);
|
|
51
|
+
if (isSelfHosted) {
|
|
52
|
+
return this.ensureAuthenticatedSelfHosted(baseUrl, capabilities);
|
|
53
|
+
}
|
|
54
|
+
return this.ensureAuthenticatedExternal(baseUrl, capabilities);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Handle authentication for self-hosted DCR servers
|
|
58
|
+
* Self-hosted servers manage their own token storage via /oauth/verify
|
|
59
|
+
*/ async ensureAuthenticatedSelfHosted(baseUrl, capabilities) {
|
|
60
|
+
const dcrTokenKey = `dcr-tokens:${baseUrl}`;
|
|
61
|
+
// 1. Check for existing DCR tokens (different from external tokens)
|
|
62
|
+
let tokens = await this.tokenStore.get(dcrTokenKey);
|
|
63
|
+
if (tokens) {
|
|
64
|
+
// 2. Verify token is still valid by calling /oauth/verify
|
|
65
|
+
try {
|
|
66
|
+
const verifyUrl = `${baseUrl}/oauth/verify`;
|
|
67
|
+
const verifyResponse = await fetch(verifyUrl, {
|
|
68
|
+
headers: {
|
|
69
|
+
Authorization: `Bearer ${tokens.accessToken}`,
|
|
70
|
+
Connection: 'close'
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
if (verifyResponse.ok) {
|
|
74
|
+
const verifyData = await verifyResponse.json();
|
|
75
|
+
if (verifyData.token === tokens.accessToken) {
|
|
76
|
+
// Token is still valid with the self-hosted server
|
|
77
|
+
return tokens;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch (_error) {
|
|
81
|
+
// Token verification failed - need to re-authenticate
|
|
82
|
+
}
|
|
83
|
+
// Token is expired or invalid
|
|
84
|
+
await this.tokenStore.delete(dcrTokenKey);
|
|
85
|
+
tokens = undefined;
|
|
86
|
+
}
|
|
87
|
+
// 3. No valid tokens - perform full DCR + OAuth flow
|
|
88
|
+
if (!capabilities.registrationEndpoint || !capabilities.authorizationEndpoint || !capabilities.tokenEndpoint) {
|
|
89
|
+
throw new Error('Server does not provide required OAuth endpoints');
|
|
90
|
+
}
|
|
91
|
+
this.logger.debug('🔐 No valid tokens found, starting self-hosted DCR authentication...');
|
|
92
|
+
// Extract port from pre-resolved redirectUri
|
|
93
|
+
const port = parseInt(new URL(this.redirectUri).port, 10) || (this.redirectUri.startsWith('https:') ? 443 : 80);
|
|
94
|
+
// Register OAuth client via DCR
|
|
95
|
+
this.logger.debug('📝 Registering OAuth client with self-hosted server...');
|
|
96
|
+
const client = await this.dcrClient.registerClient(capabilities.registrationEndpoint, {
|
|
97
|
+
redirectUri: this.redirectUri
|
|
98
|
+
});
|
|
99
|
+
// Perform OAuth authorization flow with PKCE (RFC 7636)
|
|
100
|
+
const flowOptions = {
|
|
101
|
+
port,
|
|
102
|
+
headless: this.headless,
|
|
103
|
+
redirectUri: this.redirectUri,
|
|
104
|
+
pkce: true,
|
|
105
|
+
logger: this.logger
|
|
106
|
+
};
|
|
107
|
+
if (capabilities.scopes) {
|
|
108
|
+
flowOptions.scopes = capabilities.scopes;
|
|
109
|
+
}
|
|
110
|
+
tokens = await this.oauthFlow.performAuthFlow(capabilities.authorizationEndpoint, capabilities.tokenEndpoint, client.clientId, client.clientSecret, flowOptions);
|
|
111
|
+
// For self-hosted mode, verify the token works with /oauth/verify immediately
|
|
112
|
+
try {
|
|
113
|
+
const verifyUrl = `${baseUrl}/oauth/verify`;
|
|
114
|
+
const verifyResponse = await fetch(verifyUrl, {
|
|
115
|
+
headers: {
|
|
116
|
+
Authorization: `Bearer ${tokens.accessToken}`,
|
|
117
|
+
Connection: 'close'
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
if (!verifyResponse.ok) {
|
|
121
|
+
throw new Error(`DCR token verification failed after authentication: ${verifyResponse.status}`);
|
|
122
|
+
}
|
|
123
|
+
const verifyData = await verifyResponse.json();
|
|
124
|
+
if (verifyData.token !== tokens.accessToken) {
|
|
125
|
+
throw new Error('DCR server returned different token in verification');
|
|
126
|
+
}
|
|
127
|
+
this.logger.debug('✅ DCR token verified with self-hosted server');
|
|
128
|
+
} catch (error) {
|
|
129
|
+
this.logger.error('❌ DCR token verification failed:', error instanceof Error ? error.message : String(error));
|
|
130
|
+
throw new Error('Self-hosted DCR authentication completed but token verification failed');
|
|
131
|
+
}
|
|
132
|
+
// Save tokens for future use
|
|
133
|
+
await this.tokenStore.set(dcrTokenKey, tokens);
|
|
134
|
+
this.logger.debug('✅ Self-hosted DCR authentication successful, tokens saved');
|
|
135
|
+
return tokens;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Handle authentication for external OAuth providers (original implementation)
|
|
139
|
+
*/ async ensureAuthenticatedExternal(baseUrl, capabilities) {
|
|
140
|
+
const tokenKey = `tokens:${baseUrl}`;
|
|
141
|
+
// 1. Check for existing tokens
|
|
142
|
+
let tokens = await this.tokenStore.get(tokenKey);
|
|
143
|
+
if (tokens) {
|
|
144
|
+
// 2. Proactive refresh if token expires within 5 minutes
|
|
145
|
+
if (tokens.expiresAt < Date.now() + REFRESH_BUFFER_MS) {
|
|
146
|
+
this.logger.debug('🔄 Refreshing access token...');
|
|
147
|
+
try {
|
|
148
|
+
tokens = await this.refreshTokens(tokens, capabilities.tokenEndpoint);
|
|
149
|
+
await this.tokenStore.set(tokenKey, tokens);
|
|
150
|
+
this.logger.debug('✅ Token refreshed successfully');
|
|
151
|
+
} catch (_error) {
|
|
152
|
+
// Refresh failed - clear tokens and re-authenticate
|
|
153
|
+
this.logger.warn('⚠️ Token refresh failed, re-authenticating...');
|
|
154
|
+
await this.tokenStore.delete(tokenKey);
|
|
155
|
+
tokens = undefined;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (tokens) {
|
|
159
|
+
return tokens;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// 3. No valid tokens - perform DCR + OAuth flow
|
|
163
|
+
if (!capabilities.registrationEndpoint || !capabilities.authorizationEndpoint || !capabilities.tokenEndpoint) {
|
|
164
|
+
throw new Error('Server does not provide required OAuth endpoints');
|
|
165
|
+
}
|
|
166
|
+
this.logger.debug('🔐 No valid tokens found, starting external OAuth authentication...');
|
|
167
|
+
// Extract port from pre-resolved redirectUri
|
|
168
|
+
const port = parseInt(new URL(this.redirectUri).port, 10) || (this.redirectUri.startsWith('https:') ? 443 : 80);
|
|
169
|
+
// Register OAuth client via DCR
|
|
170
|
+
this.logger.debug('📝 Registering OAuth client...');
|
|
171
|
+
const client = await this.dcrClient.registerClient(capabilities.registrationEndpoint, {
|
|
172
|
+
redirectUri: this.redirectUri
|
|
173
|
+
});
|
|
174
|
+
// Perform OAuth authorization flow with PKCE (RFC 7636)
|
|
175
|
+
const flowOptions = {
|
|
176
|
+
port,
|
|
177
|
+
headless: this.headless,
|
|
178
|
+
redirectUri: this.redirectUri,
|
|
179
|
+
pkce: true,
|
|
180
|
+
logger: this.logger
|
|
181
|
+
};
|
|
182
|
+
if (capabilities.scopes) {
|
|
183
|
+
flowOptions.scopes = capabilities.scopes;
|
|
184
|
+
}
|
|
185
|
+
tokens = await this.oauthFlow.performAuthFlow(capabilities.authorizationEndpoint, capabilities.tokenEndpoint, client.clientId, client.clientSecret, flowOptions);
|
|
186
|
+
// Save tokens for future use
|
|
187
|
+
await this.tokenStore.set(tokenKey, tokens);
|
|
188
|
+
this.logger.debug('✅ Authentication successful, tokens saved');
|
|
189
|
+
return tokens;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Refresh access token using refresh token
|
|
193
|
+
*/ async refreshTokens(tokens, tokenEndpoint) {
|
|
194
|
+
if (!tokenEndpoint) {
|
|
195
|
+
throw new Error('Token endpoint not available for refresh');
|
|
196
|
+
}
|
|
197
|
+
if (!tokens.refreshToken) {
|
|
198
|
+
throw new Error('No refresh token available');
|
|
199
|
+
}
|
|
200
|
+
if (!tokens.clientId || !tokens.clientSecret) {
|
|
201
|
+
throw new Error('Client credentials not available for refresh');
|
|
202
|
+
}
|
|
203
|
+
return await this.oauthFlow.refreshTokens(tokenEndpoint, tokens.refreshToken, tokens.clientId, tokens.clientSecret);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Delete stored tokens for a server
|
|
207
|
+
*/ async deleteTokens(baseUrl) {
|
|
208
|
+
const tokenKey = `tokens:${baseUrl}`;
|
|
209
|
+
await this.tokenStore.delete(tokenKey);
|
|
210
|
+
this.logger.debug(`🗑️ Deleted tokens for ${baseUrl}`);
|
|
211
|
+
}
|
|
212
|
+
constructor(options){
|
|
213
|
+
var _options_logger;
|
|
214
|
+
if (options.tokenStore) {
|
|
215
|
+
this.tokenStore = options.tokenStore;
|
|
216
|
+
} else {
|
|
217
|
+
// Default CLI store in .mcp-z directory (per-project)
|
|
218
|
+
const storePath = path.join(process.cwd(), '.mcp-z', 'tokens.json');
|
|
219
|
+
// Ensure directory exists before creating store
|
|
220
|
+
fs.mkdirSync(path.dirname(storePath), {
|
|
221
|
+
recursive: true
|
|
222
|
+
});
|
|
223
|
+
this.tokenStore = new Keyv({
|
|
224
|
+
store: new KeyvFile({
|
|
225
|
+
filename: storePath
|
|
226
|
+
})
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
this.dcrClient = new DynamicClientRegistrar();
|
|
230
|
+
this.oauthFlow = new InteractiveOAuthFlow();
|
|
231
|
+
this.headless = options.headless || false;
|
|
232
|
+
this.redirectUri = options.redirectUri;
|
|
233
|
+
this.logger = (_options_logger = options.logger) !== null && _options_logger !== void 0 ? _options_logger : defaultLogger;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/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 { 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 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 });\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 } = {\n port,\n headless: this.headless,\n redirectUri: this.redirectUri,\n pkce: true,\n logger: this.logger,\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 /**\n * Handle authentication for external OAuth providers (original implementation)\n */\n private async ensureAuthenticatedExternal(baseUrl: string, capabilities: AuthCapabilities): Promise<TokenSet> {\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);\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 });\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 } = {\n port,\n headless: this.headless,\n redirectUri: this.redirectUri,\n pkce: true,\n logger: this.logger,\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 * Refresh access token using refresh token\n */\n private async refreshTokens(tokens: TokenSet, tokenEndpoint?: string): 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);\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","InteractiveOAuthFlow","logger","defaultLogger","DynamicClientRegistrar","REFRESH_BUFFER_MS","DcrAuthenticator","detectSelfHostedMode","baseUrl","includes","_error","ensureAuthenticated","capabilities","isSelfHosted","ensureAuthenticatedSelfHosted","ensureAuthenticatedExternal","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":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Client Registration (DCR) Client
|
|
3
|
+
* Implements RFC 7591 for OAuth client registration
|
|
4
|
+
*/
|
|
5
|
+
import type { ClientCredentials, DcrRegistrationOptions } from '../auth/types.js';
|
|
6
|
+
/**
|
|
7
|
+
* DynamicClientRegistrar handles Dynamic Client Registration with OAuth servers
|
|
8
|
+
*/
|
|
9
|
+
export declare class DynamicClientRegistrar {
|
|
10
|
+
/**
|
|
11
|
+
* Register a new OAuth client with the authorization server
|
|
12
|
+
*
|
|
13
|
+
* @param registrationEndpoint - DCR registration endpoint URL
|
|
14
|
+
* @param options - Registration options (client name, redirect URI)
|
|
15
|
+
* @returns Client credentials (client ID and secret)
|
|
16
|
+
*
|
|
17
|
+
* @throws Error if registration fails or server returns error
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* const registrar = new DynamicClientRegistrar();
|
|
21
|
+
* const creds = await registrar.registerClient(
|
|
22
|
+
* 'https://example.com/oauth/register',
|
|
23
|
+
* { clientName: '@mcp-z/client', redirectUri: 'http://localhost:3000/callback' }
|
|
24
|
+
* );
|
|
25
|
+
* console.log('Client ID:', creds.clientId);
|
|
26
|
+
*/
|
|
27
|
+
registerClient(registrationEndpoint: string, options?: DcrRegistrationOptions): Promise<ClientCredentials>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Client Registration (DCR) Client
|
|
3
|
+
* Implements RFC 7591 for OAuth client registration
|
|
4
|
+
*/ /**
|
|
5
|
+
* DynamicClientRegistrar handles Dynamic Client Registration with OAuth servers
|
|
6
|
+
*/ export class DynamicClientRegistrar {
|
|
7
|
+
/**
|
|
8
|
+
* Register a new OAuth client with the authorization server
|
|
9
|
+
*
|
|
10
|
+
* @param registrationEndpoint - DCR registration endpoint URL
|
|
11
|
+
* @param options - Registration options (client name, redirect URI)
|
|
12
|
+
* @returns Client credentials (client ID and secret)
|
|
13
|
+
*
|
|
14
|
+
* @throws Error if registration fails or server returns error
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* const registrar = new DynamicClientRegistrar();
|
|
18
|
+
* const creds = await registrar.registerClient(
|
|
19
|
+
* 'https://example.com/oauth/register',
|
|
20
|
+
* { clientName: '@mcp-z/client', redirectUri: 'http://localhost:3000/callback' }
|
|
21
|
+
* );
|
|
22
|
+
* console.log('Client ID:', creds.clientId);
|
|
23
|
+
*/ async registerClient(registrationEndpoint, options = {}) {
|
|
24
|
+
const requestBody = {
|
|
25
|
+
client_name: options.clientName || '@mcp-z/client',
|
|
26
|
+
redirect_uris: options.redirectUri ? [
|
|
27
|
+
options.redirectUri
|
|
28
|
+
] : [
|
|
29
|
+
'http://localhost:3000/callback'
|
|
30
|
+
],
|
|
31
|
+
grant_types: [
|
|
32
|
+
'authorization_code',
|
|
33
|
+
'refresh_token'
|
|
34
|
+
],
|
|
35
|
+
response_types: [
|
|
36
|
+
'code'
|
|
37
|
+
],
|
|
38
|
+
token_endpoint_auth_method: 'client_secret_basic'
|
|
39
|
+
};
|
|
40
|
+
const response = await fetch(registrationEndpoint, {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: {
|
|
43
|
+
'Content-Type': 'application/json',
|
|
44
|
+
Accept: 'application/json',
|
|
45
|
+
Connection: 'close'
|
|
46
|
+
},
|
|
47
|
+
body: JSON.stringify(requestBody)
|
|
48
|
+
});
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
const errorText = await response.text();
|
|
51
|
+
throw new Error(`DCR registration failed (${response.status}): ${errorText}`);
|
|
52
|
+
}
|
|
53
|
+
const data = await response.json();
|
|
54
|
+
if (!data.client_id) {
|
|
55
|
+
throw new Error('DCR registration response missing client_id');
|
|
56
|
+
}
|
|
57
|
+
const credentials = {
|
|
58
|
+
clientId: data.client_id,
|
|
59
|
+
clientSecret: data.client_secret || ''
|
|
60
|
+
};
|
|
61
|
+
if (data.client_id_issued_at) {
|
|
62
|
+
credentials.issuedAt = data.client_id_issued_at;
|
|
63
|
+
}
|
|
64
|
+
return credentials;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/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 type { ClientCredentials, DcrRegistrationOptions } from '../auth/types.ts';\n\n/**\n * DCR Registration Request (RFC 7591)\n */\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/**\n * DCR Registration Response (RFC 7591)\n */\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/**\n * DynamicClientRegistrar handles Dynamic Client Registration with OAuth servers\n */\nexport class DynamicClientRegistrar {\n /**\n * Register a new OAuth client with the authorization server\n *\n * @param registrationEndpoint - DCR registration endpoint URL\n * @param options - Registration options (client name, redirect URI)\n * @returns Client credentials (client ID and secret)\n *\n * @throws Error if registration fails or server returns error\n *\n * @example\n * const registrar = new DynamicClientRegistrar();\n * const creds = await registrar.registerClient(\n * 'https://example.com/oauth/register',\n * { clientName: '@mcp-z/client', redirectUri: 'http://localhost:3000/callback' }\n * );\n * console.log('Client ID:', creds.clientId);\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 const response = await fetch(registrationEndpoint, {\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\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":["DynamicClientRegistrar","registerClient","registrationEndpoint","options","requestBody","client_name","clientName","redirect_uris","redirectUri","grant_types","response_types","token_endpoint_auth_method","response","fetch","method","headers","Accept","Connection","body","JSON","stringify","ok","errorText","text","Error","status","data","json","client_id","credentials","clientId","clientSecret","client_secret","client_id_issued_at","issuedAt"],"mappings":"AAAA;;;CAGC,GAyBD;;CAEC,GACD,OAAO,MAAMA;IACX;;;;;;;;;;;;;;;;GAgBC,GACD,MAAMC,eAAeC,oBAA4B,EAAEC,UAAkC,CAAC,CAAC,EAA8B;QACnH,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,MAAMC,WAAW,MAAMC,MAAMX,sBAAsB;YACjDY,QAAQ;YACRC,SAAS;gBACP,gBAAgB;gBAChBC,QAAQ;gBACRC,YAAY;YACd;YACAC,MAAMC,KAAKC,SAAS,CAAChB;QACvB;QAEA,IAAI,CAACQ,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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCR (Dynamic Client Registration) Module
|
|
3
|
+
* Exports public API for DCR authentication
|
|
4
|
+
*/
|
|
5
|
+
export type { ClientCredentials, DcrRegistrationOptions } from '../auth/types.js';
|
|
6
|
+
export type { DcrAuthenticatorOptions } from './dcr-authenticator.js';
|
|
7
|
+
export { DcrAuthenticator } from './dcr-authenticator.js';
|
|
8
|
+
export { DynamicClientRegistrar } from './dynamic-client-registrar.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/dcr/index.ts"],"sourcesContent":["/**\n * DCR (Dynamic Client Registration) Module\n * Exports public API for DCR authentication\n */\n\nexport type { ClientCredentials, DcrRegistrationOptions } from '../auth/types.ts';\nexport type { DcrAuthenticatorOptions } from './dcr-authenticator.ts';\nexport { DcrAuthenticator } from './dcr-authenticator.ts';\nexport { DynamicClientRegistrar } from './dynamic-client-registrar.ts';\n"],"names":["DcrAuthenticator","DynamicClientRegistrar"],"mappings":"AAAA;;;CAGC,GAID,SAASA,gBAAgB,QAAQ,yBAAyB;AAC1D,SAASC,sBAAsB,QAAQ,gCAAgC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mcp-z/client - MCP Client Library
|
|
3
|
+
*/
|
|
4
|
+
export type { McpServerEntry, StartConfig } from '../schemas/servers.d.js';
|
|
5
|
+
export { probeAuthCapabilities } from './auth/capability-discovery.js';
|
|
6
|
+
export type { AuthCapabilities, CallbackResult, OAuthCallbackListenerOptions, OAuthFlowOptions, TokenSet } from './auth/index.js';
|
|
7
|
+
export { InteractiveOAuthFlow } from './auth/interactive-oauth-flow.js';
|
|
8
|
+
export { OAuthCallbackListener } from './auth/oauth-callback-listener.js';
|
|
9
|
+
export { decorateClient, type ManagedClient, type PromptArguments, type WrappedCallToolReturn, type WrappedGetPromptReturn, type WrappedReadResourceReturn } from './client-helpers.js';
|
|
10
|
+
export { type ValidationResult, validateServers } from './config/validate-config.js';
|
|
11
|
+
export type { JsonValue, PromptArgument, ToolArguments } from './connection/types.js';
|
|
12
|
+
export { DcrAuthenticator } from './dcr/dcr-authenticator.js';
|
|
13
|
+
export { DynamicClientRegistrar } from './dcr/dynamic-client-registrar.js';
|
|
14
|
+
export type { ClientCredentials, DcrAuthenticatorOptions, DcrRegistrationOptions } from './dcr/index.js';
|
|
15
|
+
export { type JsonValidator, type NativeCallToolResponse, type NativeGetPromptResponse, type NativeReadResourceResponse, PromptResponseError, PromptResponseWrapper, ResourceResponseError, ResourceResponseWrapper, ToolResponseError, ToolResponseWrapper, } from './response-wrappers.js';
|
|
16
|
+
export type { CapabilityClient, CapabilityIndex, CapabilityType, IndexedCapability, IndexedPrompt, IndexedResource, IndexedTool, SearchField, SearchOptions, SearchResponse, SearchResult } from './search/index.js';
|
|
17
|
+
export { buildCapabilityIndex, search, searchCapabilities } from './search/index.js';
|
|
18
|
+
export { type CloseResult, type CreateServerRegistryOptions, createServerRegistry, type Dialect, type ServerRegistry, type ServersConfig } from './spawn/spawn-servers.js';
|
|
19
|
+
export type { TransportType } from './types.js';
|
|
20
|
+
export { getLogLevel, type Logger, type LogLevel, logger, setLogLevel } from './utils/logger.js';
|
|
21
|
+
export { resolveArgsPaths, resolvePath } from './utils/path-utils.js';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mcp-z/client - MCP Client Library
|
|
3
|
+
*/ // Config types (from schema)
|
|
4
|
+
// Auth - OAuth utilities
|
|
5
|
+
export { probeAuthCapabilities } from './auth/capability-discovery.js';
|
|
6
|
+
export { InteractiveOAuthFlow } from './auth/interactive-oauth-flow.js';
|
|
7
|
+
export { OAuthCallbackListener } from './auth/oauth-callback-listener.js';
|
|
8
|
+
// Client helpers and lightweight overloads
|
|
9
|
+
export { decorateClient } from './client-helpers.js';
|
|
10
|
+
// Config - Configuration validation
|
|
11
|
+
export { validateServers } from './config/validate-config.js';
|
|
12
|
+
// DCR - Dynamic Client Registration utilities
|
|
13
|
+
export { DcrAuthenticator } from './dcr/dcr-authenticator.js';
|
|
14
|
+
export { DynamicClientRegistrar } from './dcr/dynamic-client-registrar.js';
|
|
15
|
+
export { PromptResponseError, PromptResponseWrapper, ResourceResponseError, ResourceResponseWrapper, ToolResponseError, ToolResponseWrapper } from './response-wrappers.js';
|
|
16
|
+
// Search - Capability discovery
|
|
17
|
+
export { buildCapabilityIndex, search, searchCapabilities } from './search/index.js';
|
|
18
|
+
// Spawn - Server registry (v3 API)
|
|
19
|
+
export { createServerRegistry } from './spawn/spawn-servers.js';
|
|
20
|
+
// Utils - Shared utilities
|
|
21
|
+
export { getLogLevel, logger, setLogLevel } from './utils/logger.js';
|
|
22
|
+
export { resolveArgsPaths, resolvePath } from './utils/path-utils.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/index.ts"],"sourcesContent":["/**\n * @mcp-z/client - MCP Client Library\n */\n\n// Config types (from schema)\nexport type { McpServerEntry, StartConfig } from '../schemas/servers.d.ts';\n// Auth - OAuth utilities\nexport { probeAuthCapabilities } from './auth/capability-discovery.ts';\nexport type { AuthCapabilities, CallbackResult, OAuthCallbackListenerOptions, OAuthFlowOptions, TokenSet } from './auth/index.ts';\nexport { InteractiveOAuthFlow } from './auth/interactive-oauth-flow.ts';\nexport { OAuthCallbackListener } from './auth/oauth-callback-listener.ts';\n// Client helpers and lightweight overloads\nexport { decorateClient, type ManagedClient, type PromptArguments, type WrappedCallToolReturn, type WrappedGetPromptReturn, type WrappedReadResourceReturn } from './client-helpers.ts';\n// Config - Configuration validation\nexport { type ValidationResult, validateServers } from './config/validate-config.ts';\n// Connection - MCP client connection utilities (internal helpers exposed for advanced use)\nexport type { JsonValue, PromptArgument, ToolArguments } from './connection/types.ts';\n// DCR - Dynamic Client Registration utilities\nexport { DcrAuthenticator } from './dcr/dcr-authenticator.ts';\nexport { DynamicClientRegistrar } from './dcr/dynamic-client-registrar.ts';\nexport type { ClientCredentials, DcrAuthenticatorOptions, DcrRegistrationOptions } from './dcr/index.ts';\nexport {\n type JsonValidator,\n type NativeCallToolResponse,\n type NativeGetPromptResponse,\n type NativeReadResourceResponse,\n PromptResponseError,\n PromptResponseWrapper,\n ResourceResponseError,\n ResourceResponseWrapper,\n ToolResponseError,\n ToolResponseWrapper,\n} from './response-wrappers.ts';\nexport type { CapabilityClient, CapabilityIndex, CapabilityType, IndexedCapability, IndexedPrompt, IndexedResource, IndexedTool, SearchField, SearchOptions, SearchResponse, SearchResult } from './search/index.ts';\n// Search - Capability discovery\nexport { buildCapabilityIndex, search, searchCapabilities } from './search/index.ts';\n// Spawn - Server registry (v3 API)\nexport { type CloseResult, type CreateServerRegistryOptions, createServerRegistry, type Dialect, type ServerRegistry, type ServersConfig } from './spawn/spawn-servers.ts';\nexport type { TransportType } from './types.ts';\n// Utils - Shared utilities\nexport { getLogLevel, type Logger, type LogLevel, logger, setLogLevel } from './utils/logger.ts';\nexport { resolveArgsPaths, resolvePath } from './utils/path-utils.ts';\n"],"names":["probeAuthCapabilities","InteractiveOAuthFlow","OAuthCallbackListener","decorateClient","validateServers","DcrAuthenticator","DynamicClientRegistrar","PromptResponseError","PromptResponseWrapper","ResourceResponseError","ResourceResponseWrapper","ToolResponseError","ToolResponseWrapper","buildCapabilityIndex","search","searchCapabilities","createServerRegistry","getLogLevel","logger","setLogLevel","resolveArgsPaths","resolvePath"],"mappings":"AAAA;;CAEC,GAED,6BAA6B;AAE7B,yBAAyB;AACzB,SAASA,qBAAqB,QAAQ,iCAAiC;AAEvE,SAASC,oBAAoB,QAAQ,mCAAmC;AACxE,SAASC,qBAAqB,QAAQ,oCAAoC;AAC1E,2CAA2C;AAC3C,SAASC,cAAc,QAA2I,sBAAsB;AACxL,oCAAoC;AACpC,SAAgCC,eAAe,QAAQ,8BAA8B;AAGrF,8CAA8C;AAC9C,SAASC,gBAAgB,QAAQ,6BAA6B;AAC9D,SAASC,sBAAsB,QAAQ,oCAAoC;AAE3E,SAKEC,mBAAmB,EACnBC,qBAAqB,EACrBC,qBAAqB,EACrBC,uBAAuB,EACvBC,iBAAiB,EACjBC,mBAAmB,QACd,yBAAyB;AAEhC,gCAAgC;AAChC,SAASC,oBAAoB,EAAEC,MAAM,EAAEC,kBAAkB,QAAQ,oBAAoB;AACrF,mCAAmC;AACnC,SAA6DC,oBAAoB,QAA+D,2BAA2B;AAE3K,2BAA2B;AAC3B,SAASC,WAAW,EAA8BC,MAAM,EAAEC,WAAW,QAAQ,oBAAoB;AACjG,SAASC,gBAAgB,EAAEC,WAAW,QAAQ,wBAAwB"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Monkey patches for MCP SDK bugs
|
|
3
|
+
*
|
|
4
|
+
* These patches fix bugs in dependencies that haven't been fixed upstream yet.
|
|
5
|
+
*/ import { Protocol } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
6
|
+
/**
|
|
7
|
+
* FIX: Protocol.close() doesn't clear pending timeouts
|
|
8
|
+
*
|
|
9
|
+
* BUG: The MCP SDK's Protocol.close() only closes the transport but does NOT
|
|
10
|
+
* clear pending timeouts from the internal _timeoutInfo Map. This causes Node.js
|
|
11
|
+
* to hang until timeouts fire (default 60 seconds).
|
|
12
|
+
*
|
|
13
|
+
* PATCH: Wrap Protocol.close() to clear all pending timeouts before closing.
|
|
14
|
+
*
|
|
15
|
+
* TO TEST IF STILL NEEDED:
|
|
16
|
+
* 1. Comment out this patch
|
|
17
|
+
* 2. Run: npm run test:unit
|
|
18
|
+
* 3. If tests hang ~60 seconds after completing, bug still exists
|
|
19
|
+
* 4. If tests exit promptly, SDK is fixed and this can be removed
|
|
20
|
+
*
|
|
21
|
+
* UPSTREAM: https://github.com/modelcontextprotocol/typescript-sdk/issues/XXX
|
|
22
|
+
*/ const originalClose = Protocol.prototype.close;
|
|
23
|
+
Protocol.prototype.close = async function() {
|
|
24
|
+
const self = this;
|
|
25
|
+
if (self._timeoutInfo) {
|
|
26
|
+
for (const [, info] of self._timeoutInfo){
|
|
27
|
+
clearTimeout(info.timeoutId);
|
|
28
|
+
}
|
|
29
|
+
self._timeoutInfo.clear();
|
|
30
|
+
}
|
|
31
|
+
return originalClose.call(this);
|
|
32
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/monkey-patches.ts"],"sourcesContent":["/**\n * Monkey patches for MCP SDK bugs\n *\n * These patches fix bugs in dependencies that haven't been fixed upstream yet.\n */\n\nimport { Protocol } from '@modelcontextprotocol/sdk/shared/protocol.js';\n\n/**\n * FIX: Protocol.close() doesn't clear pending timeouts\n *\n * BUG: The MCP SDK's Protocol.close() only closes the transport but does NOT\n * clear pending timeouts from the internal _timeoutInfo Map. This causes Node.js\n * to hang until timeouts fire (default 60 seconds).\n *\n * PATCH: Wrap Protocol.close() to clear all pending timeouts before closing.\n *\n * TO TEST IF STILL NEEDED:\n * 1. Comment out this patch\n * 2. Run: npm run test:unit\n * 3. If tests hang ~60 seconds after completing, bug still exists\n * 4. If tests exit promptly, SDK is fixed and this can be removed\n *\n * UPSTREAM: https://github.com/modelcontextprotocol/typescript-sdk/issues/XXX\n */\nconst originalClose = Protocol.prototype.close;\nProtocol.prototype.close = async function () {\n const self = this as unknown as { _timeoutInfo?: Map<unknown, { timeoutId: ReturnType<typeof setTimeout> }> };\n if (self._timeoutInfo) {\n for (const [, info] of self._timeoutInfo) {\n clearTimeout(info.timeoutId);\n }\n self._timeoutInfo.clear();\n }\n return originalClose.call(this);\n};\n"],"names":["Protocol","originalClose","prototype","close","self","_timeoutInfo","info","clearTimeout","timeoutId","clear","call"],"mappings":"AAAA;;;;CAIC,GAED,SAASA,QAAQ,QAAQ,+CAA+C;AAExE;;;;;;;;;;;;;;;;CAgBC,GACD,MAAMC,gBAAgBD,SAASE,SAAS,CAACC,KAAK;AAC9CH,SAASE,SAAS,CAACC,KAAK,GAAG;IACzB,MAAMC,OAAO,IAAI;IACjB,IAAIA,KAAKC,YAAY,EAAE;QACrB,KAAK,MAAM,GAAGC,KAAK,IAAIF,KAAKC,YAAY,CAAE;YACxCE,aAAaD,KAAKE,SAAS;QAC7B;QACAJ,KAAKC,YAAY,CAACI,KAAK;IACzB;IACA,OAAOR,cAAcS,IAAI,CAAC,IAAI;AAChC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "type": "module" }
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
export type NativeCallToolResponse = Awaited<ReturnType<Client['callTool']>>;
|
|
3
|
+
export type NativeGetPromptResponse = Awaited<ReturnType<Client['getPrompt']>>;
|
|
4
|
+
export type NativeReadResourceResponse = Awaited<ReturnType<Client['readResource']>>;
|
|
5
|
+
export type JsonValidator<T> = (value: unknown) => asserts value is T;
|
|
6
|
+
export declare class ToolResponseError extends Error {
|
|
7
|
+
readonly response: NativeCallToolResponse;
|
|
8
|
+
constructor(message: string, response: NativeCallToolResponse);
|
|
9
|
+
}
|
|
10
|
+
export declare class ToolResponseWrapper {
|
|
11
|
+
private readonly payload;
|
|
12
|
+
constructor(payload: NativeCallToolResponse);
|
|
13
|
+
raw(): NativeCallToolResponse;
|
|
14
|
+
json<T = unknown>(validator?: JsonValidator<T>): T;
|
|
15
|
+
text(): string;
|
|
16
|
+
private resolveJsonPayload;
|
|
17
|
+
private throwIfError;
|
|
18
|
+
}
|
|
19
|
+
export declare class PromptResponseError extends Error {
|
|
20
|
+
readonly response: NativeGetPromptResponse;
|
|
21
|
+
constructor(message: string, response: NativeGetPromptResponse);
|
|
22
|
+
}
|
|
23
|
+
export declare class PromptResponseWrapper {
|
|
24
|
+
private readonly payload;
|
|
25
|
+
constructor(payload: NativeGetPromptResponse);
|
|
26
|
+
raw(): NativeGetPromptResponse;
|
|
27
|
+
text(): string;
|
|
28
|
+
json<T = unknown>(validator?: JsonValidator<T>): T;
|
|
29
|
+
}
|
|
30
|
+
export declare class ResourceResponseError extends Error {
|
|
31
|
+
readonly response: NativeReadResourceResponse;
|
|
32
|
+
constructor(message: string, response: NativeReadResourceResponse);
|
|
33
|
+
}
|
|
34
|
+
export declare class ResourceResponseWrapper {
|
|
35
|
+
private readonly payload;
|
|
36
|
+
constructor(payload: NativeReadResourceResponse);
|
|
37
|
+
raw(): NativeReadResourceResponse;
|
|
38
|
+
text(): string;
|
|
39
|
+
json<T = unknown>(validator?: JsonValidator<T>): T;
|
|
40
|
+
private firstEntry;
|
|
41
|
+
}
|