@mcp-use/client 2.1.0 → 2.1.1-canary.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/auth/browser.d.ts +8 -0
- package/dist/auth/browser.d.ts.map +1 -1
- package/dist/auth/flow.d.ts +9 -0
- package/dist/auth/flow.d.ts.map +1 -1
- package/dist/core/browser.d.ts.map +1 -1
- package/dist/core/config.d.ts +6 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/session.d.ts +19 -0
- package/dist/core/session.d.ts.map +1 -1
- package/dist/index-browser.d.ts +1 -1
- package/dist/index-browser.d.ts.map +1 -1
- package/dist/index-browser.js +286 -41
- package/dist/index-browser.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +651 -407
- package/dist/index.js.map +1 -1
- package/dist/react/McpClientProvider.d.ts.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +488 -179
- package/dist/react/index.js.map +1 -1
- package/dist/react/types.d.ts +10 -1
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/useMcp-operations.d.ts +1 -0
- package/dist/react/useMcp-operations.d.ts.map +1 -1
- package/dist/react/useMcp.d.ts.map +1 -1
- package/dist/transport/base.d.ts +16 -1
- package/dist/transport/base.d.ts.map +1 -1
- package/dist/transport/http.d.ts +14 -0
- package/dist/transport/http.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index-browser.js
CHANGED
|
@@ -128,7 +128,7 @@ var Logger = class {
|
|
|
128
128
|
var logger = Logger.get();
|
|
129
129
|
|
|
130
130
|
// src/utils/version.ts
|
|
131
|
-
var VERSION = "2.1.
|
|
131
|
+
var VERSION = "2.1.1-canary.1";
|
|
132
132
|
function getPackageVersion() {
|
|
133
133
|
return VERSION;
|
|
134
134
|
}
|
|
@@ -1530,29 +1530,43 @@ var BrowserOAuthClientProvider = class {
|
|
|
1530
1530
|
* @param authorizationUrl - The fully constructed authorization URL from the SDK.
|
|
1531
1531
|
*/
|
|
1532
1532
|
async redirectToAuthorization(authorizationUrl) {
|
|
1533
|
-
|
|
1533
|
+
await this.prepareAuthorizationUrl(authorizationUrl);
|
|
1534
1534
|
if (this.preventAutoAuth) {
|
|
1535
1535
|
console.info(
|
|
1536
1536
|
`[${this.storageKeyPrefix}] Auto-auth prevented. Authorization URL stored for manual trigger.`
|
|
1537
1537
|
);
|
|
1538
1538
|
return;
|
|
1539
1539
|
}
|
|
1540
|
+
this.startAuthorization();
|
|
1541
|
+
}
|
|
1542
|
+
/**
|
|
1543
|
+
* Open the authorization URL prepared by the official SDK.
|
|
1544
|
+
*
|
|
1545
|
+
* This is the explicit-user-action counterpart to `preventAutoAuth`: the
|
|
1546
|
+
* provider still lets the SDK own discovery and PKCE state, while a host can
|
|
1547
|
+
* launch the stored authorization request later from an Authenticate button.
|
|
1548
|
+
*/
|
|
1549
|
+
startAuthorization() {
|
|
1550
|
+
const authorizationUrl = this.lastAttemptedAuthUrl;
|
|
1551
|
+
if (!authorizationUrl) {
|
|
1552
|
+
throw new Error("No prepared OAuth authorization is available");
|
|
1553
|
+
}
|
|
1540
1554
|
if (this.useRedirectFlow) {
|
|
1541
1555
|
console.info(
|
|
1542
1556
|
`[${this.storageKeyPrefix}] Redirecting to authorization URL (full-page redirect).`
|
|
1543
1557
|
);
|
|
1544
|
-
window.location.href =
|
|
1558
|
+
window.location.href = authorizationUrl;
|
|
1545
1559
|
return;
|
|
1546
1560
|
}
|
|
1547
1561
|
const popupFeatures = "width=600,height=700,resizable=yes,scrollbars=yes,status=yes";
|
|
1548
1562
|
try {
|
|
1549
1563
|
const popup = window.open(
|
|
1550
|
-
|
|
1564
|
+
authorizationUrl,
|
|
1551
1565
|
`mcp_auth_${this.serverUrlHash}`,
|
|
1552
1566
|
popupFeatures
|
|
1553
1567
|
);
|
|
1554
1568
|
if (this.onPopupWindow) {
|
|
1555
|
-
this.onPopupWindow(
|
|
1569
|
+
this.onPopupWindow(authorizationUrl, popupFeatures, popup);
|
|
1556
1570
|
}
|
|
1557
1571
|
if (!popup || popup.closed || typeof popup.closed === "undefined") {
|
|
1558
1572
|
console.warn(
|
|
@@ -1931,6 +1945,7 @@ async function completeAuthorization() {
|
|
|
1931
1945
|
// src/auth/flow.ts
|
|
1932
1946
|
import {
|
|
1933
1947
|
auth,
|
|
1948
|
+
InsufficientScopeError,
|
|
1934
1949
|
UnauthorizedError
|
|
1935
1950
|
} from "@modelcontextprotocol/client";
|
|
1936
1951
|
var DEFAULT_AUTH_TIMEOUT_MS = 5 * 6e4;
|
|
@@ -1951,6 +1966,25 @@ function isUnauthorized(err, depth = 0) {
|
|
|
1951
1966
|
}
|
|
1952
1967
|
return false;
|
|
1953
1968
|
}
|
|
1969
|
+
function isOAuthInteractionRequired(err, depth = 0) {
|
|
1970
|
+
if (!err || depth > 5) return false;
|
|
1971
|
+
if (err instanceof InsufficientScopeError || err instanceof UnauthorizedError) {
|
|
1972
|
+
return true;
|
|
1973
|
+
}
|
|
1974
|
+
if (err instanceof Error) {
|
|
1975
|
+
if (err.name === "InsufficientScopeError" || err.name === "UnauthorizedError") {
|
|
1976
|
+
return true;
|
|
1977
|
+
}
|
|
1978
|
+
if (err.cause && isOAuthInteractionRequired(err.cause, depth + 1)) {
|
|
1979
|
+
return true;
|
|
1980
|
+
}
|
|
1981
|
+
const data = err.data;
|
|
1982
|
+
if (data?.cause && isOAuthInteractionRequired(data.cause, depth + 1)) {
|
|
1983
|
+
return true;
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
return false;
|
|
1987
|
+
}
|
|
1954
1988
|
async function completeOAuthFlow(provider, serverUrl, options = {}) {
|
|
1955
1989
|
const flowProvider = provider;
|
|
1956
1990
|
const timeoutMs = options.timeoutMs ?? DEFAULT_AUTH_TIMEOUT_MS;
|
|
@@ -1962,14 +1996,21 @@ async function completeOAuthFlow(provider, serverUrl, options = {}) {
|
|
|
1962
1996
|
throw new Error(`Unexpected OAuth auth() result: ${result}`);
|
|
1963
1997
|
}
|
|
1964
1998
|
}
|
|
1999
|
+
if (flowProvider.preventAutoAuth === true && typeof flowProvider.startAuthorization === "function") {
|
|
2000
|
+
flowProvider.startAuthorization();
|
|
2001
|
+
}
|
|
1965
2002
|
if (typeof flowProvider.getAuthorizationResponse === "function" || typeof flowProvider.getAuthorizationCode === "function") {
|
|
1966
2003
|
const response = typeof flowProvider.getAuthorizationResponse === "function" ? await flowProvider.getAuthorizationResponse() : { code: await flowProvider.getAuthorizationCode() };
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
2004
|
+
if (options.finishAuthorization) {
|
|
2005
|
+
await options.finishAuthorization(response.code, response.iss);
|
|
2006
|
+
} else {
|
|
2007
|
+
await auth(provider, {
|
|
2008
|
+
serverUrl,
|
|
2009
|
+
authorizationCode: response.code,
|
|
2010
|
+
...response.iss !== void 0 ? { iss: response.iss } : {},
|
|
2011
|
+
fetchFn
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
1973
2014
|
return;
|
|
1974
2015
|
}
|
|
1975
2016
|
await waitForBrowserAuthComplete(flowProvider, timeoutMs);
|
|
@@ -2031,6 +2072,7 @@ import { auth as auth2, UnauthorizedError as UnauthorizedError3 } from "@modelco
|
|
|
2031
2072
|
// src/transport/http.ts
|
|
2032
2073
|
import {
|
|
2033
2074
|
Client,
|
|
2075
|
+
discoverOAuthProtectedResourceMetadata,
|
|
2034
2076
|
SdkError,
|
|
2035
2077
|
SdkHttpError,
|
|
2036
2078
|
StreamableHTTPClientTransport as StreamableHTTPClientTransport2,
|
|
@@ -2087,6 +2129,7 @@ var BaseConnector = class {
|
|
|
2087
2129
|
toolsCache = null;
|
|
2088
2130
|
capabilitiesCache = null;
|
|
2089
2131
|
serverInfoCache = null;
|
|
2132
|
+
authorizationCache;
|
|
2090
2133
|
connected = false;
|
|
2091
2134
|
opts;
|
|
2092
2135
|
notificationHandlers = [];
|
|
@@ -2350,6 +2393,28 @@ var BaseConnector = class {
|
|
|
2350
2393
|
"setupElicitationHandler: Elicitation handler registered successfully"
|
|
2351
2394
|
);
|
|
2352
2395
|
}
|
|
2396
|
+
/**
|
|
2397
|
+
* Run one logical MCP operation. HTTP connectors override this host seam to
|
|
2398
|
+
* finish an SDK-started interactive OAuth flow and retry exactly once.
|
|
2399
|
+
*/
|
|
2400
|
+
async executeRequest(operation) {
|
|
2401
|
+
return operation();
|
|
2402
|
+
}
|
|
2403
|
+
/** OAuth state discovered for the active connection, when available. */
|
|
2404
|
+
get authorization() {
|
|
2405
|
+
return this.authorizationCache;
|
|
2406
|
+
}
|
|
2407
|
+
/**
|
|
2408
|
+
* Discover optional authorization metadata without delaying connection
|
|
2409
|
+
* readiness. HTTP connectors override this with RFC 9728 discovery.
|
|
2410
|
+
*/
|
|
2411
|
+
async discoverAuthorization() {
|
|
2412
|
+
return this.authorization;
|
|
2413
|
+
}
|
|
2414
|
+
/** Start optional OAuth for a connected mixed-auth server. */
|
|
2415
|
+
async authenticate() {
|
|
2416
|
+
throw new Error("This connector does not support interactive OAuth");
|
|
2417
|
+
}
|
|
2353
2418
|
/**
|
|
2354
2419
|
* Disconnects the SDK client and releases transport resources.
|
|
2355
2420
|
*
|
|
@@ -2397,13 +2462,13 @@ var BaseConnector = class {
|
|
|
2397
2462
|
icons: serverInfo.icons
|
|
2398
2463
|
} : null;
|
|
2399
2464
|
try {
|
|
2400
|
-
const listToolsRes = await this.
|
|
2401
|
-
void 0,
|
|
2402
|
-
defaultRequestOptions
|
|
2465
|
+
const listToolsRes = await this.executeRequest(
|
|
2466
|
+
() => this.client.listTools(void 0, defaultRequestOptions)
|
|
2403
2467
|
);
|
|
2404
2468
|
this.toolsCache = listToolsRes.tools ?? [];
|
|
2405
2469
|
logger.debug(`Fetched ${this.toolsCache.length} tools from server`);
|
|
2406
2470
|
} catch (err) {
|
|
2471
|
+
if (isOAuthInteractionRequired(err)) throw err;
|
|
2407
2472
|
const error = err;
|
|
2408
2473
|
if (error.code === -32601) {
|
|
2409
2474
|
logger.debug("Server does not implement tools/list, assuming no tools");
|
|
@@ -2477,9 +2542,8 @@ var BaseConnector = class {
|
|
|
2477
2542
|
const progressHandler = enhancedOptions?.onprogress;
|
|
2478
2543
|
if (progressHandler) this.activeProgressHandlers.add(progressHandler);
|
|
2479
2544
|
try {
|
|
2480
|
-
const res = await this.
|
|
2481
|
-
{ name, arguments: args },
|
|
2482
|
-
enhancedOptions
|
|
2545
|
+
const res = await this.executeRequest(
|
|
2546
|
+
() => this.client.callTool({ name, arguments: args }, enhancedOptions)
|
|
2483
2547
|
);
|
|
2484
2548
|
logger.debug(`Tool '${name}' returned`, res);
|
|
2485
2549
|
return res;
|
|
@@ -2499,7 +2563,9 @@ var BaseConnector = class {
|
|
|
2499
2563
|
throw new Error("MCP client is not connected");
|
|
2500
2564
|
}
|
|
2501
2565
|
logger.debug("[listTools] Fetching fresh tools from server...");
|
|
2502
|
-
const result = await this.
|
|
2566
|
+
const result = await this.executeRequest(
|
|
2567
|
+
() => this.client.listTools(void 0, options)
|
|
2568
|
+
);
|
|
2503
2569
|
const tools = result.tools ? [...result.tools] : [];
|
|
2504
2570
|
logger.debug(
|
|
2505
2571
|
`[listTools] Returned ${tools.length} tools:`,
|
|
@@ -2519,7 +2585,9 @@ var BaseConnector = class {
|
|
|
2519
2585
|
throw new Error("MCP client is not connected");
|
|
2520
2586
|
}
|
|
2521
2587
|
logger.debug("Listing resources", cursor ? `with cursor: ${cursor}` : "");
|
|
2522
|
-
return await this.
|
|
2588
|
+
return await this.executeRequest(
|
|
2589
|
+
() => this.client.listResources({ cursor }, options)
|
|
2590
|
+
);
|
|
2523
2591
|
}
|
|
2524
2592
|
/**
|
|
2525
2593
|
* List all resources from the server, automatically handling pagination
|
|
@@ -2537,14 +2605,16 @@ var BaseConnector = class {
|
|
|
2537
2605
|
}
|
|
2538
2606
|
try {
|
|
2539
2607
|
logger.debug("Listing all resources (with auto-pagination)");
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2608
|
+
return await this.executeRequest(async () => {
|
|
2609
|
+
const allResources = [];
|
|
2610
|
+
let cursor = void 0;
|
|
2611
|
+
do {
|
|
2612
|
+
const result = await this.client.listResources({ cursor }, options);
|
|
2613
|
+
allResources.push(...result.resources || []);
|
|
2614
|
+
cursor = result.nextCursor;
|
|
2615
|
+
} while (cursor);
|
|
2616
|
+
return { resources: allResources };
|
|
2617
|
+
});
|
|
2548
2618
|
} catch (err) {
|
|
2549
2619
|
const error = err;
|
|
2550
2620
|
if (error.code === -32601) {
|
|
@@ -2565,7 +2635,9 @@ var BaseConnector = class {
|
|
|
2565
2635
|
throw new Error("MCP client is not connected");
|
|
2566
2636
|
}
|
|
2567
2637
|
logger.debug("Listing resource templates");
|
|
2568
|
-
return await this.
|
|
2638
|
+
return await this.executeRequest(
|
|
2639
|
+
() => this.client.listResourceTemplates(void 0, options)
|
|
2640
|
+
);
|
|
2569
2641
|
}
|
|
2570
2642
|
/**
|
|
2571
2643
|
* Request completion suggestions for a prompt or resource template argument
|
|
@@ -2579,7 +2651,9 @@ var BaseConnector = class {
|
|
|
2579
2651
|
throw new Error("MCP client is not connected");
|
|
2580
2652
|
}
|
|
2581
2653
|
logger.debug("[complete] Requesting completions for:", params.ref);
|
|
2582
|
-
const result = await this.
|
|
2654
|
+
const result = await this.executeRequest(
|
|
2655
|
+
() => this.client.complete(params, options)
|
|
2656
|
+
);
|
|
2583
2657
|
logger.debug(
|
|
2584
2658
|
`[complete] Received ${result.completion.values.length} suggestions`
|
|
2585
2659
|
);
|
|
@@ -2597,7 +2671,9 @@ var BaseConnector = class {
|
|
|
2597
2671
|
throw new Error("MCP client is not connected");
|
|
2598
2672
|
}
|
|
2599
2673
|
logger.debug(`Reading resource ${uri}`);
|
|
2600
|
-
const res = await this.
|
|
2674
|
+
const res = await this.executeRequest(
|
|
2675
|
+
() => this.client.readResource({ uri }, options)
|
|
2676
|
+
);
|
|
2601
2677
|
return res;
|
|
2602
2678
|
}
|
|
2603
2679
|
/**
|
|
@@ -2611,7 +2687,9 @@ var BaseConnector = class {
|
|
|
2611
2687
|
throw new Error("MCP client is not connected");
|
|
2612
2688
|
}
|
|
2613
2689
|
logger.debug(`Subscribing to resource: ${uri}`);
|
|
2614
|
-
return await this.
|
|
2690
|
+
return await this.executeRequest(
|
|
2691
|
+
() => this.client.subscribeResource({ uri }, options)
|
|
2692
|
+
);
|
|
2615
2693
|
}
|
|
2616
2694
|
/**
|
|
2617
2695
|
* Unsubscribe from resource updates
|
|
@@ -2624,7 +2702,9 @@ var BaseConnector = class {
|
|
|
2624
2702
|
throw new Error("MCP client is not connected");
|
|
2625
2703
|
}
|
|
2626
2704
|
logger.debug(`Unsubscribing from resource: ${uri}`);
|
|
2627
|
-
return await this.
|
|
2705
|
+
return await this.executeRequest(
|
|
2706
|
+
() => this.client.unsubscribeResource({ uri }, options)
|
|
2707
|
+
);
|
|
2628
2708
|
}
|
|
2629
2709
|
/**
|
|
2630
2710
|
* Lists prompts exposed by the server.
|
|
@@ -2641,7 +2721,7 @@ var BaseConnector = class {
|
|
|
2641
2721
|
}
|
|
2642
2722
|
try {
|
|
2643
2723
|
logger.debug("Listing prompts");
|
|
2644
|
-
return await this.client.listPrompts();
|
|
2724
|
+
return await this.executeRequest(() => this.client.listPrompts());
|
|
2645
2725
|
} catch (err) {
|
|
2646
2726
|
const error = err;
|
|
2647
2727
|
if (error.code === -32601) {
|
|
@@ -2663,7 +2743,9 @@ var BaseConnector = class {
|
|
|
2663
2743
|
throw new Error("MCP client is not connected");
|
|
2664
2744
|
}
|
|
2665
2745
|
logger.debug(`Getting prompt ${name}`);
|
|
2666
|
-
return await this.
|
|
2746
|
+
return await this.executeRequest(
|
|
2747
|
+
() => this.client.getPrompt({ name, arguments: args })
|
|
2748
|
+
);
|
|
2667
2749
|
}
|
|
2668
2750
|
/**
|
|
2669
2751
|
* Sends a raw, potentially non-standard request through the SDK client.
|
|
@@ -2678,10 +2760,12 @@ var BaseConnector = class {
|
|
|
2678
2760
|
throw new Error("MCP client is not connected");
|
|
2679
2761
|
}
|
|
2680
2762
|
logger.debug(`Sending raw request '${method}' with params`, params);
|
|
2681
|
-
return await this.
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2763
|
+
return await this.executeRequest(
|
|
2764
|
+
() => this.client.request(
|
|
2765
|
+
{ method, params: params ?? {} },
|
|
2766
|
+
passthroughResultSchema,
|
|
2767
|
+
options
|
|
2768
|
+
)
|
|
2685
2769
|
);
|
|
2686
2770
|
}
|
|
2687
2771
|
/**
|
|
@@ -2714,6 +2798,7 @@ var BaseConnector = class {
|
|
|
2714
2798
|
}
|
|
2715
2799
|
}
|
|
2716
2800
|
this.toolsCache = null;
|
|
2801
|
+
this.authorizationCache = void 0;
|
|
2717
2802
|
if (issues.length) {
|
|
2718
2803
|
logger.warn(`Resource cleanup finished with ${issues.length} issue(s)`);
|
|
2719
2804
|
}
|
|
@@ -2721,6 +2806,7 @@ var BaseConnector = class {
|
|
|
2721
2806
|
};
|
|
2722
2807
|
|
|
2723
2808
|
// src/transport/http.ts
|
|
2809
|
+
var MIXED_AUTH_DISCOVERY_TIMEOUT_MS = 2e3;
|
|
2724
2810
|
function detectUnauthorized(err, depth = 0) {
|
|
2725
2811
|
if (!err || depth > 5) return false;
|
|
2726
2812
|
if (err instanceof UnauthorizedError2) return true;
|
|
@@ -2734,6 +2820,11 @@ function detectUnauthorized(err, depth = 0) {
|
|
|
2734
2820
|
}
|
|
2735
2821
|
return false;
|
|
2736
2822
|
}
|
|
2823
|
+
function isOAuthClientProvider(provider) {
|
|
2824
|
+
return Boolean(
|
|
2825
|
+
provider && "redirectToAuthorization" in provider && typeof provider.redirectToAuthorization === "function" && "tokens" in provider && typeof provider.tokens === "function"
|
|
2826
|
+
);
|
|
2827
|
+
}
|
|
2737
2828
|
function createMcpProxyFetch(logicalServerUrl, proxyUrl, baseFetch, serverId) {
|
|
2738
2829
|
const logical = new URL(logicalServerUrl);
|
|
2739
2830
|
const proxy = proxyUrl.replace(/\/$/, "");
|
|
@@ -2759,6 +2850,31 @@ function createMcpProxyFetch(logicalServerUrl, proxyUrl, baseFetch, serverId) {
|
|
|
2759
2850
|
);
|
|
2760
2851
|
};
|
|
2761
2852
|
}
|
|
2853
|
+
function createDeadlineFetch(baseFetch, deadlineSignal) {
|
|
2854
|
+
return async (input, init) => {
|
|
2855
|
+
const requestSignal = init?.signal;
|
|
2856
|
+
if (!requestSignal) {
|
|
2857
|
+
return baseFetch(input, { ...init, signal: deadlineSignal });
|
|
2858
|
+
}
|
|
2859
|
+
const controller = new AbortController();
|
|
2860
|
+
const abortFromRequest = () => controller.abort(requestSignal.reason);
|
|
2861
|
+
const abortFromDeadline = () => controller.abort(deadlineSignal.reason);
|
|
2862
|
+
if (requestSignal.aborted) abortFromRequest();
|
|
2863
|
+
else
|
|
2864
|
+
requestSignal.addEventListener("abort", abortFromRequest, { once: true });
|
|
2865
|
+
if (deadlineSignal.aborted) abortFromDeadline();
|
|
2866
|
+
else
|
|
2867
|
+
deadlineSignal.addEventListener("abort", abortFromDeadline, {
|
|
2868
|
+
once: true
|
|
2869
|
+
});
|
|
2870
|
+
try {
|
|
2871
|
+
return await baseFetch(input, { ...init, signal: controller.signal });
|
|
2872
|
+
} finally {
|
|
2873
|
+
requestSignal.removeEventListener("abort", abortFromRequest);
|
|
2874
|
+
deadlineSignal.removeEventListener("abort", abortFromDeadline);
|
|
2875
|
+
}
|
|
2876
|
+
};
|
|
2877
|
+
}
|
|
2762
2878
|
var HttpConnector = class extends BaseConnector {
|
|
2763
2879
|
baseUrl;
|
|
2764
2880
|
headers;
|
|
@@ -2769,8 +2885,12 @@ var HttpConnector = class extends BaseConnector {
|
|
|
2769
2885
|
gatewayUrl;
|
|
2770
2886
|
serverId;
|
|
2771
2887
|
reconnectionOptions;
|
|
2888
|
+
detectMixedAuth;
|
|
2772
2889
|
transportType = null;
|
|
2773
2890
|
streamableTransport = null;
|
|
2891
|
+
hadAccessTokenAtConnect = false;
|
|
2892
|
+
pendingOAuthCompletion = null;
|
|
2893
|
+
authorizationDiscovery = null;
|
|
2774
2894
|
/**
|
|
2775
2895
|
* Creates an HTTP connector.
|
|
2776
2896
|
*
|
|
@@ -2801,6 +2921,103 @@ var HttpConnector = class extends BaseConnector {
|
|
|
2801
2921
|
};
|
|
2802
2922
|
this.protocolNegotiation = opts.protocolNegotiation ?? "auto";
|
|
2803
2923
|
this.reconnectionOptions = opts.reconnectionOptions;
|
|
2924
|
+
this.detectMixedAuth = opts.detectMixedAuth ?? true;
|
|
2925
|
+
}
|
|
2926
|
+
get oauthProvider() {
|
|
2927
|
+
return isOAuthClientProvider(this.opts.authProvider) ? this.opts.authProvider : void 0;
|
|
2928
|
+
}
|
|
2929
|
+
async completeInteractiveAuthorization() {
|
|
2930
|
+
const provider = this.oauthProvider;
|
|
2931
|
+
if (!provider) {
|
|
2932
|
+
throw new Error("No OAuth client provider is configured");
|
|
2933
|
+
}
|
|
2934
|
+
if (!this.pendingOAuthCompletion) {
|
|
2935
|
+
this.pendingOAuthCompletion = completeOAuthFlow(provider, this.baseUrl, {
|
|
2936
|
+
fetchFn: this.customFetch,
|
|
2937
|
+
finishAuthorization: async (code, iss) => {
|
|
2938
|
+
const transport = this.streamableTransport;
|
|
2939
|
+
if (!transport) {
|
|
2940
|
+
throw new Error("OAuth transport is no longer connected");
|
|
2941
|
+
}
|
|
2942
|
+
await transport.finishAuth(code, iss);
|
|
2943
|
+
}
|
|
2944
|
+
}).then(() => {
|
|
2945
|
+
if (this.authorizationCache) {
|
|
2946
|
+
this.authorizationCache = {
|
|
2947
|
+
...this.authorizationCache,
|
|
2948
|
+
authenticated: true
|
|
2949
|
+
};
|
|
2950
|
+
}
|
|
2951
|
+
}).finally(() => {
|
|
2952
|
+
this.pendingOAuthCompletion = null;
|
|
2953
|
+
});
|
|
2954
|
+
}
|
|
2955
|
+
await this.pendingOAuthCompletion;
|
|
2956
|
+
}
|
|
2957
|
+
async executeRequest(operation) {
|
|
2958
|
+
try {
|
|
2959
|
+
return await operation();
|
|
2960
|
+
} catch (error) {
|
|
2961
|
+
const provider = this.oauthProvider;
|
|
2962
|
+
if (!provider || provider.preventAutoAuth === true || !isOAuthInteractionRequired(error)) {
|
|
2963
|
+
throw error;
|
|
2964
|
+
}
|
|
2965
|
+
await this.completeInteractiveAuthorization();
|
|
2966
|
+
return operation();
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
/** Authenticate an already-connected server without requiring a 401 first. */
|
|
2970
|
+
async authenticate() {
|
|
2971
|
+
if (!this.connected || !this.streamableTransport) {
|
|
2972
|
+
throw new Error("MCP client is not connected");
|
|
2973
|
+
}
|
|
2974
|
+
await this.completeInteractiveAuthorization();
|
|
2975
|
+
}
|
|
2976
|
+
async discoverAuthorization() {
|
|
2977
|
+
if (!this.detectMixedAuth || !this.oauthProvider || this.hadAccessTokenAtConnect) {
|
|
2978
|
+
return this.authorizationCache;
|
|
2979
|
+
}
|
|
2980
|
+
if (this.authorizationDiscovery) return this.authorizationDiscovery;
|
|
2981
|
+
this.authorizationDiscovery = this.discoverMixedAuthorization();
|
|
2982
|
+
return this.authorizationDiscovery;
|
|
2983
|
+
}
|
|
2984
|
+
async discoverMixedAuthorization() {
|
|
2985
|
+
const controller = new AbortController();
|
|
2986
|
+
let timeout;
|
|
2987
|
+
const discoveryTimeout = new Promise((_, reject) => {
|
|
2988
|
+
timeout = setTimeout(() => {
|
|
2989
|
+
const error = new Error(
|
|
2990
|
+
`Mixed-auth metadata discovery timed out after ${MIXED_AUTH_DISCOVERY_TIMEOUT_MS}ms`
|
|
2991
|
+
);
|
|
2992
|
+
controller.abort(error);
|
|
2993
|
+
reject(error);
|
|
2994
|
+
}, MIXED_AUTH_DISCOVERY_TIMEOUT_MS);
|
|
2995
|
+
});
|
|
2996
|
+
const baseFetch = this.customFetch ?? globalThis.fetch.bind(globalThis);
|
|
2997
|
+
try {
|
|
2998
|
+
const metadata = await Promise.race([
|
|
2999
|
+
discoverOAuthProtectedResourceMetadata(
|
|
3000
|
+
this.baseUrl,
|
|
3001
|
+
{ protocolVersion: this.negotiatedProtocolVersion },
|
|
3002
|
+
createDeadlineFetch(baseFetch, controller.signal)
|
|
3003
|
+
),
|
|
3004
|
+
discoveryTimeout
|
|
3005
|
+
]);
|
|
3006
|
+
this.authorizationCache = {
|
|
3007
|
+
mode: "mixed",
|
|
3008
|
+
authenticated: false,
|
|
3009
|
+
...metadata.resource ? { resource: metadata.resource } : {},
|
|
3010
|
+
...metadata.scopes_supported ? { scopesSupported: [...metadata.scopes_supported] } : {}
|
|
3011
|
+
};
|
|
3012
|
+
logger.info(
|
|
3013
|
+
"OAuth protected-resource metadata found after anonymous connection; server uses mixed auth"
|
|
3014
|
+
);
|
|
3015
|
+
} catch (error) {
|
|
3016
|
+
logger.debug("Mixed-auth metadata was not discovered:", error);
|
|
3017
|
+
} finally {
|
|
3018
|
+
if (timeout) clearTimeout(timeout);
|
|
3019
|
+
}
|
|
3020
|
+
return this.authorizationCache;
|
|
2804
3021
|
}
|
|
2805
3022
|
buildClientOptions() {
|
|
2806
3023
|
return {
|
|
@@ -2907,6 +3124,16 @@ var HttpConnector = class extends BaseConnector {
|
|
|
2907
3124
|
}
|
|
2908
3125
|
const baseUrl = this.baseUrl;
|
|
2909
3126
|
logger.debug(`Connecting to MCP implementation via HTTP: ${baseUrl}`);
|
|
3127
|
+
const oauthProvider = this.oauthProvider;
|
|
3128
|
+
if (oauthProvider) {
|
|
3129
|
+
try {
|
|
3130
|
+
this.hadAccessTokenAtConnect = Boolean(
|
|
3131
|
+
(await oauthProvider.tokens())?.access_token
|
|
3132
|
+
);
|
|
3133
|
+
} catch {
|
|
3134
|
+
this.hadAccessTokenAtConnect = false;
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
2910
3137
|
try {
|
|
2911
3138
|
await this.connectWithStreamableHttp(baseUrl);
|
|
2912
3139
|
logger.debug("\u2705 Successfully connected via streamable HTTP");
|
|
@@ -3177,6 +3404,7 @@ var HttpConnector = class extends BaseConnector {
|
|
|
3177
3404
|
}
|
|
3178
3405
|
}
|
|
3179
3406
|
await super.cleanupResources();
|
|
3407
|
+
this.authorizationDiscovery = null;
|
|
3180
3408
|
}
|
|
3181
3409
|
};
|
|
3182
3410
|
|
|
@@ -3258,6 +3486,7 @@ function createConnectorFromConfig(serverConfig, connectorOptions) {
|
|
|
3258
3486
|
fetch: serverConfig.fetch,
|
|
3259
3487
|
authToken: serverConfig.authToken,
|
|
3260
3488
|
authProvider: serverConfig.authProvider,
|
|
3489
|
+
detectMixedAuth: serverConfig.detectMixedAuth,
|
|
3261
3490
|
protocolNegotiation: serverConfig.protocolNegotiation,
|
|
3262
3491
|
timeout: serverConfig.timeout,
|
|
3263
3492
|
roots: serverConfig.roots,
|
|
@@ -3500,6 +3729,18 @@ var MCPConnection = class {
|
|
|
3500
3729
|
get serverInfo() {
|
|
3501
3730
|
return this.connector.serverInfo;
|
|
3502
3731
|
}
|
|
3732
|
+
/** OAuth state discovered for this connection, when available. */
|
|
3733
|
+
get authorization() {
|
|
3734
|
+
return this.connector.authorization;
|
|
3735
|
+
}
|
|
3736
|
+
/** Discover optional OAuth metadata without delaying MCP readiness. */
|
|
3737
|
+
async discoverAuthorization() {
|
|
3738
|
+
return this.connector.discoverAuthorization();
|
|
3739
|
+
}
|
|
3740
|
+
/** Authenticate an already-connected mixed-auth server. */
|
|
3741
|
+
async authenticate() {
|
|
3742
|
+
await this.connector.authenticate();
|
|
3743
|
+
}
|
|
3503
3744
|
/**
|
|
3504
3745
|
* The negotiated protocol era for this session's connection:
|
|
3505
3746
|
* `"legacy"` (2025-era) or `"modern"` (2026-07-28-era).
|
|
@@ -3532,7 +3773,8 @@ var MCPConnection = class {
|
|
|
3532
3773
|
...server ? { server } : {},
|
|
3533
3774
|
capabilities,
|
|
3534
3775
|
instructions: this.connector.instructions,
|
|
3535
|
-
extensions
|
|
3776
|
+
extensions,
|
|
3777
|
+
...this.authorization ? { authorization: this.authorization } : {}
|
|
3536
3778
|
};
|
|
3537
3779
|
}
|
|
3538
3780
|
/**
|
|
@@ -3763,7 +4005,7 @@ var MCPConnection = class {
|
|
|
3763
4005
|
};
|
|
3764
4006
|
|
|
3765
4007
|
// src/core/base.ts
|
|
3766
|
-
function
|
|
4008
|
+
function isOAuthClientProvider2(provider) {
|
|
3767
4009
|
return !!provider && typeof provider === "object" && "redirectUrl" in provider && "clientMetadata" in provider;
|
|
3768
4010
|
}
|
|
3769
4011
|
var BaseMCPClient = class {
|
|
@@ -3996,7 +4238,7 @@ var BaseMCPClient = class {
|
|
|
3996
4238
|
...serverConfig,
|
|
3997
4239
|
authProvider: oauthProvider
|
|
3998
4240
|
};
|
|
3999
|
-
} else if ("authProvider" in serverConfig && serverConfig.authProvider &&
|
|
4241
|
+
} else if ("authProvider" in serverConfig && serverConfig.authProvider && isOAuthClientProvider2(serverConfig.authProvider)) {
|
|
4000
4242
|
oauthProvider = serverConfig.authProvider;
|
|
4001
4243
|
}
|
|
4002
4244
|
const openSession = async () => {
|
|
@@ -4336,6 +4578,7 @@ var BrowserMCPClient = class _BrowserMCPClient extends BaseMCPClient {
|
|
|
4336
4578
|
fetch: configuredFetch,
|
|
4337
4579
|
authToken,
|
|
4338
4580
|
authProvider,
|
|
4581
|
+
detectMixedAuth,
|
|
4339
4582
|
wrapTransport,
|
|
4340
4583
|
clientOptions,
|
|
4341
4584
|
protocolNegotiation,
|
|
@@ -4360,6 +4603,7 @@ var BrowserMCPClient = class _BrowserMCPClient extends BaseMCPClient {
|
|
|
4360
4603
|
fetch: configuredFetch ?? globalThis.fetch.bind(globalThis),
|
|
4361
4604
|
authToken,
|
|
4362
4605
|
authProvider,
|
|
4606
|
+
detectMixedAuth,
|
|
4363
4607
|
wrapTransport,
|
|
4364
4608
|
clientOptions,
|
|
4365
4609
|
onSampling: resolved.onSampling,
|
|
@@ -4470,6 +4714,7 @@ export {
|
|
|
4470
4714
|
createOAuthProvider,
|
|
4471
4715
|
detectFavicon,
|
|
4472
4716
|
getPackageVersion,
|
|
4717
|
+
isOAuthInteractionRequired,
|
|
4473
4718
|
isUnauthorized,
|
|
4474
4719
|
logger,
|
|
4475
4720
|
normalizeClientInfo,
|