@mcp-use/client 2.1.0 → 2.1.1-canary.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/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 +17 -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 +269 -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 +635 -408
- 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 +421 -143
- 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 +11 -1
- package/dist/transport/base.d.ts.map +1 -1
- package/dist/transport/http.d.ts +11 -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.0";
|
|
131
|
+
var VERSION = "2.1.1-canary.0";
|
|
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,21 @@ 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
|
+
/** Start optional OAuth for a connected mixed-auth server. */
|
|
2408
|
+
async authenticate() {
|
|
2409
|
+
throw new Error("This connector does not support interactive OAuth");
|
|
2410
|
+
}
|
|
2353
2411
|
/**
|
|
2354
2412
|
* Disconnects the SDK client and releases transport resources.
|
|
2355
2413
|
*
|
|
@@ -2397,13 +2455,13 @@ var BaseConnector = class {
|
|
|
2397
2455
|
icons: serverInfo.icons
|
|
2398
2456
|
} : null;
|
|
2399
2457
|
try {
|
|
2400
|
-
const listToolsRes = await this.
|
|
2401
|
-
void 0,
|
|
2402
|
-
defaultRequestOptions
|
|
2458
|
+
const listToolsRes = await this.executeRequest(
|
|
2459
|
+
() => this.client.listTools(void 0, defaultRequestOptions)
|
|
2403
2460
|
);
|
|
2404
2461
|
this.toolsCache = listToolsRes.tools ?? [];
|
|
2405
2462
|
logger.debug(`Fetched ${this.toolsCache.length} tools from server`);
|
|
2406
2463
|
} catch (err) {
|
|
2464
|
+
if (isOAuthInteractionRequired(err)) throw err;
|
|
2407
2465
|
const error = err;
|
|
2408
2466
|
if (error.code === -32601) {
|
|
2409
2467
|
logger.debug("Server does not implement tools/list, assuming no tools");
|
|
@@ -2477,9 +2535,8 @@ var BaseConnector = class {
|
|
|
2477
2535
|
const progressHandler = enhancedOptions?.onprogress;
|
|
2478
2536
|
if (progressHandler) this.activeProgressHandlers.add(progressHandler);
|
|
2479
2537
|
try {
|
|
2480
|
-
const res = await this.
|
|
2481
|
-
{ name, arguments: args },
|
|
2482
|
-
enhancedOptions
|
|
2538
|
+
const res = await this.executeRequest(
|
|
2539
|
+
() => this.client.callTool({ name, arguments: args }, enhancedOptions)
|
|
2483
2540
|
);
|
|
2484
2541
|
logger.debug(`Tool '${name}' returned`, res);
|
|
2485
2542
|
return res;
|
|
@@ -2499,7 +2556,9 @@ var BaseConnector = class {
|
|
|
2499
2556
|
throw new Error("MCP client is not connected");
|
|
2500
2557
|
}
|
|
2501
2558
|
logger.debug("[listTools] Fetching fresh tools from server...");
|
|
2502
|
-
const result = await this.
|
|
2559
|
+
const result = await this.executeRequest(
|
|
2560
|
+
() => this.client.listTools(void 0, options)
|
|
2561
|
+
);
|
|
2503
2562
|
const tools = result.tools ? [...result.tools] : [];
|
|
2504
2563
|
logger.debug(
|
|
2505
2564
|
`[listTools] Returned ${tools.length} tools:`,
|
|
@@ -2519,7 +2578,9 @@ var BaseConnector = class {
|
|
|
2519
2578
|
throw new Error("MCP client is not connected");
|
|
2520
2579
|
}
|
|
2521
2580
|
logger.debug("Listing resources", cursor ? `with cursor: ${cursor}` : "");
|
|
2522
|
-
return await this.
|
|
2581
|
+
return await this.executeRequest(
|
|
2582
|
+
() => this.client.listResources({ cursor }, options)
|
|
2583
|
+
);
|
|
2523
2584
|
}
|
|
2524
2585
|
/**
|
|
2525
2586
|
* List all resources from the server, automatically handling pagination
|
|
@@ -2537,14 +2598,16 @@ var BaseConnector = class {
|
|
|
2537
2598
|
}
|
|
2538
2599
|
try {
|
|
2539
2600
|
logger.debug("Listing all resources (with auto-pagination)");
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2601
|
+
return await this.executeRequest(async () => {
|
|
2602
|
+
const allResources = [];
|
|
2603
|
+
let cursor = void 0;
|
|
2604
|
+
do {
|
|
2605
|
+
const result = await this.client.listResources({ cursor }, options);
|
|
2606
|
+
allResources.push(...result.resources || []);
|
|
2607
|
+
cursor = result.nextCursor;
|
|
2608
|
+
} while (cursor);
|
|
2609
|
+
return { resources: allResources };
|
|
2610
|
+
});
|
|
2548
2611
|
} catch (err) {
|
|
2549
2612
|
const error = err;
|
|
2550
2613
|
if (error.code === -32601) {
|
|
@@ -2565,7 +2628,9 @@ var BaseConnector = class {
|
|
|
2565
2628
|
throw new Error("MCP client is not connected");
|
|
2566
2629
|
}
|
|
2567
2630
|
logger.debug("Listing resource templates");
|
|
2568
|
-
return await this.
|
|
2631
|
+
return await this.executeRequest(
|
|
2632
|
+
() => this.client.listResourceTemplates(void 0, options)
|
|
2633
|
+
);
|
|
2569
2634
|
}
|
|
2570
2635
|
/**
|
|
2571
2636
|
* Request completion suggestions for a prompt or resource template argument
|
|
@@ -2579,7 +2644,9 @@ var BaseConnector = class {
|
|
|
2579
2644
|
throw new Error("MCP client is not connected");
|
|
2580
2645
|
}
|
|
2581
2646
|
logger.debug("[complete] Requesting completions for:", params.ref);
|
|
2582
|
-
const result = await this.
|
|
2647
|
+
const result = await this.executeRequest(
|
|
2648
|
+
() => this.client.complete(params, options)
|
|
2649
|
+
);
|
|
2583
2650
|
logger.debug(
|
|
2584
2651
|
`[complete] Received ${result.completion.values.length} suggestions`
|
|
2585
2652
|
);
|
|
@@ -2597,7 +2664,9 @@ var BaseConnector = class {
|
|
|
2597
2664
|
throw new Error("MCP client is not connected");
|
|
2598
2665
|
}
|
|
2599
2666
|
logger.debug(`Reading resource ${uri}`);
|
|
2600
|
-
const res = await this.
|
|
2667
|
+
const res = await this.executeRequest(
|
|
2668
|
+
() => this.client.readResource({ uri }, options)
|
|
2669
|
+
);
|
|
2601
2670
|
return res;
|
|
2602
2671
|
}
|
|
2603
2672
|
/**
|
|
@@ -2611,7 +2680,9 @@ var BaseConnector = class {
|
|
|
2611
2680
|
throw new Error("MCP client is not connected");
|
|
2612
2681
|
}
|
|
2613
2682
|
logger.debug(`Subscribing to resource: ${uri}`);
|
|
2614
|
-
return await this.
|
|
2683
|
+
return await this.executeRequest(
|
|
2684
|
+
() => this.client.subscribeResource({ uri }, options)
|
|
2685
|
+
);
|
|
2615
2686
|
}
|
|
2616
2687
|
/**
|
|
2617
2688
|
* Unsubscribe from resource updates
|
|
@@ -2624,7 +2695,9 @@ var BaseConnector = class {
|
|
|
2624
2695
|
throw new Error("MCP client is not connected");
|
|
2625
2696
|
}
|
|
2626
2697
|
logger.debug(`Unsubscribing from resource: ${uri}`);
|
|
2627
|
-
return await this.
|
|
2698
|
+
return await this.executeRequest(
|
|
2699
|
+
() => this.client.unsubscribeResource({ uri }, options)
|
|
2700
|
+
);
|
|
2628
2701
|
}
|
|
2629
2702
|
/**
|
|
2630
2703
|
* Lists prompts exposed by the server.
|
|
@@ -2641,7 +2714,7 @@ var BaseConnector = class {
|
|
|
2641
2714
|
}
|
|
2642
2715
|
try {
|
|
2643
2716
|
logger.debug("Listing prompts");
|
|
2644
|
-
return await this.client.listPrompts();
|
|
2717
|
+
return await this.executeRequest(() => this.client.listPrompts());
|
|
2645
2718
|
} catch (err) {
|
|
2646
2719
|
const error = err;
|
|
2647
2720
|
if (error.code === -32601) {
|
|
@@ -2663,7 +2736,9 @@ var BaseConnector = class {
|
|
|
2663
2736
|
throw new Error("MCP client is not connected");
|
|
2664
2737
|
}
|
|
2665
2738
|
logger.debug(`Getting prompt ${name}`);
|
|
2666
|
-
return await this.
|
|
2739
|
+
return await this.executeRequest(
|
|
2740
|
+
() => this.client.getPrompt({ name, arguments: args })
|
|
2741
|
+
);
|
|
2667
2742
|
}
|
|
2668
2743
|
/**
|
|
2669
2744
|
* Sends a raw, potentially non-standard request through the SDK client.
|
|
@@ -2678,10 +2753,12 @@ var BaseConnector = class {
|
|
|
2678
2753
|
throw new Error("MCP client is not connected");
|
|
2679
2754
|
}
|
|
2680
2755
|
logger.debug(`Sending raw request '${method}' with params`, params);
|
|
2681
|
-
return await this.
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2756
|
+
return await this.executeRequest(
|
|
2757
|
+
() => this.client.request(
|
|
2758
|
+
{ method, params: params ?? {} },
|
|
2759
|
+
passthroughResultSchema,
|
|
2760
|
+
options
|
|
2761
|
+
)
|
|
2685
2762
|
);
|
|
2686
2763
|
}
|
|
2687
2764
|
/**
|
|
@@ -2714,6 +2791,7 @@ var BaseConnector = class {
|
|
|
2714
2791
|
}
|
|
2715
2792
|
}
|
|
2716
2793
|
this.toolsCache = null;
|
|
2794
|
+
this.authorizationCache = void 0;
|
|
2717
2795
|
if (issues.length) {
|
|
2718
2796
|
logger.warn(`Resource cleanup finished with ${issues.length} issue(s)`);
|
|
2719
2797
|
}
|
|
@@ -2721,6 +2799,7 @@ var BaseConnector = class {
|
|
|
2721
2799
|
};
|
|
2722
2800
|
|
|
2723
2801
|
// src/transport/http.ts
|
|
2802
|
+
var MIXED_AUTH_DISCOVERY_TIMEOUT_MS = 2e3;
|
|
2724
2803
|
function detectUnauthorized(err, depth = 0) {
|
|
2725
2804
|
if (!err || depth > 5) return false;
|
|
2726
2805
|
if (err instanceof UnauthorizedError2) return true;
|
|
@@ -2734,6 +2813,11 @@ function detectUnauthorized(err, depth = 0) {
|
|
|
2734
2813
|
}
|
|
2735
2814
|
return false;
|
|
2736
2815
|
}
|
|
2816
|
+
function isOAuthClientProvider(provider) {
|
|
2817
|
+
return Boolean(
|
|
2818
|
+
provider && "redirectToAuthorization" in provider && typeof provider.redirectToAuthorization === "function" && "tokens" in provider && typeof provider.tokens === "function"
|
|
2819
|
+
);
|
|
2820
|
+
}
|
|
2737
2821
|
function createMcpProxyFetch(logicalServerUrl, proxyUrl, baseFetch, serverId) {
|
|
2738
2822
|
const logical = new URL(logicalServerUrl);
|
|
2739
2823
|
const proxy = proxyUrl.replace(/\/$/, "");
|
|
@@ -2759,6 +2843,31 @@ function createMcpProxyFetch(logicalServerUrl, proxyUrl, baseFetch, serverId) {
|
|
|
2759
2843
|
);
|
|
2760
2844
|
};
|
|
2761
2845
|
}
|
|
2846
|
+
function createDeadlineFetch(baseFetch, deadlineSignal) {
|
|
2847
|
+
return async (input, init) => {
|
|
2848
|
+
const requestSignal = init?.signal;
|
|
2849
|
+
if (!requestSignal) {
|
|
2850
|
+
return baseFetch(input, { ...init, signal: deadlineSignal });
|
|
2851
|
+
}
|
|
2852
|
+
const controller = new AbortController();
|
|
2853
|
+
const abortFromRequest = () => controller.abort(requestSignal.reason);
|
|
2854
|
+
const abortFromDeadline = () => controller.abort(deadlineSignal.reason);
|
|
2855
|
+
if (requestSignal.aborted) abortFromRequest();
|
|
2856
|
+
else
|
|
2857
|
+
requestSignal.addEventListener("abort", abortFromRequest, { once: true });
|
|
2858
|
+
if (deadlineSignal.aborted) abortFromDeadline();
|
|
2859
|
+
else
|
|
2860
|
+
deadlineSignal.addEventListener("abort", abortFromDeadline, {
|
|
2861
|
+
once: true
|
|
2862
|
+
});
|
|
2863
|
+
try {
|
|
2864
|
+
return await baseFetch(input, { ...init, signal: controller.signal });
|
|
2865
|
+
} finally {
|
|
2866
|
+
requestSignal.removeEventListener("abort", abortFromRequest);
|
|
2867
|
+
deadlineSignal.removeEventListener("abort", abortFromDeadline);
|
|
2868
|
+
}
|
|
2869
|
+
};
|
|
2870
|
+
}
|
|
2762
2871
|
var HttpConnector = class extends BaseConnector {
|
|
2763
2872
|
baseUrl;
|
|
2764
2873
|
headers;
|
|
@@ -2769,8 +2878,11 @@ var HttpConnector = class extends BaseConnector {
|
|
|
2769
2878
|
gatewayUrl;
|
|
2770
2879
|
serverId;
|
|
2771
2880
|
reconnectionOptions;
|
|
2881
|
+
detectMixedAuth;
|
|
2772
2882
|
transportType = null;
|
|
2773
2883
|
streamableTransport = null;
|
|
2884
|
+
hadAccessTokenAtConnect = false;
|
|
2885
|
+
pendingOAuthCompletion = null;
|
|
2774
2886
|
/**
|
|
2775
2887
|
* Creates an HTTP connector.
|
|
2776
2888
|
*
|
|
@@ -2801,6 +2913,99 @@ var HttpConnector = class extends BaseConnector {
|
|
|
2801
2913
|
};
|
|
2802
2914
|
this.protocolNegotiation = opts.protocolNegotiation ?? "auto";
|
|
2803
2915
|
this.reconnectionOptions = opts.reconnectionOptions;
|
|
2916
|
+
this.detectMixedAuth = opts.detectMixedAuth ?? true;
|
|
2917
|
+
}
|
|
2918
|
+
get oauthProvider() {
|
|
2919
|
+
return isOAuthClientProvider(this.opts.authProvider) ? this.opts.authProvider : void 0;
|
|
2920
|
+
}
|
|
2921
|
+
async completeInteractiveAuthorization() {
|
|
2922
|
+
const provider = this.oauthProvider;
|
|
2923
|
+
if (!provider) {
|
|
2924
|
+
throw new Error("No OAuth client provider is configured");
|
|
2925
|
+
}
|
|
2926
|
+
if (!this.pendingOAuthCompletion) {
|
|
2927
|
+
this.pendingOAuthCompletion = completeOAuthFlow(provider, this.baseUrl, {
|
|
2928
|
+
fetchFn: this.customFetch,
|
|
2929
|
+
finishAuthorization: async (code, iss) => {
|
|
2930
|
+
const transport = this.streamableTransport;
|
|
2931
|
+
if (!transport) {
|
|
2932
|
+
throw new Error("OAuth transport is no longer connected");
|
|
2933
|
+
}
|
|
2934
|
+
await transport.finishAuth(code, iss);
|
|
2935
|
+
}
|
|
2936
|
+
}).then(() => {
|
|
2937
|
+
if (this.authorizationCache) {
|
|
2938
|
+
this.authorizationCache = {
|
|
2939
|
+
...this.authorizationCache,
|
|
2940
|
+
authenticated: true
|
|
2941
|
+
};
|
|
2942
|
+
}
|
|
2943
|
+
}).finally(() => {
|
|
2944
|
+
this.pendingOAuthCompletion = null;
|
|
2945
|
+
});
|
|
2946
|
+
}
|
|
2947
|
+
await this.pendingOAuthCompletion;
|
|
2948
|
+
}
|
|
2949
|
+
async executeRequest(operation) {
|
|
2950
|
+
try {
|
|
2951
|
+
return await operation();
|
|
2952
|
+
} catch (error) {
|
|
2953
|
+
const provider = this.oauthProvider;
|
|
2954
|
+
if (!provider || provider.preventAutoAuth === true || !isOAuthInteractionRequired(error)) {
|
|
2955
|
+
throw error;
|
|
2956
|
+
}
|
|
2957
|
+
await this.completeInteractiveAuthorization();
|
|
2958
|
+
return operation();
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
/** Authenticate an already-connected server without requiring a 401 first. */
|
|
2962
|
+
async authenticate() {
|
|
2963
|
+
if (!this.connected || !this.streamableTransport) {
|
|
2964
|
+
throw new Error("MCP client is not connected");
|
|
2965
|
+
}
|
|
2966
|
+
await this.completeInteractiveAuthorization();
|
|
2967
|
+
}
|
|
2968
|
+
async initialize(defaultRequestOptions = this.opts.defaultRequestOptions ?? {}) {
|
|
2969
|
+
const capabilities = await super.initialize(defaultRequestOptions);
|
|
2970
|
+
if (!this.detectMixedAuth || !this.oauthProvider || this.hadAccessTokenAtConnect) {
|
|
2971
|
+
return capabilities;
|
|
2972
|
+
}
|
|
2973
|
+
const controller = new AbortController();
|
|
2974
|
+
let timeout;
|
|
2975
|
+
const discoveryTimeout = new Promise((_, reject) => {
|
|
2976
|
+
timeout = setTimeout(() => {
|
|
2977
|
+
const error = new Error(
|
|
2978
|
+
`Mixed-auth metadata discovery timed out after ${MIXED_AUTH_DISCOVERY_TIMEOUT_MS}ms`
|
|
2979
|
+
);
|
|
2980
|
+
controller.abort(error);
|
|
2981
|
+
reject(error);
|
|
2982
|
+
}, MIXED_AUTH_DISCOVERY_TIMEOUT_MS);
|
|
2983
|
+
});
|
|
2984
|
+
const baseFetch = this.customFetch ?? globalThis.fetch.bind(globalThis);
|
|
2985
|
+
try {
|
|
2986
|
+
const metadata = await Promise.race([
|
|
2987
|
+
discoverOAuthProtectedResourceMetadata(
|
|
2988
|
+
this.baseUrl,
|
|
2989
|
+
{ protocolVersion: this.negotiatedProtocolVersion },
|
|
2990
|
+
createDeadlineFetch(baseFetch, controller.signal)
|
|
2991
|
+
),
|
|
2992
|
+
discoveryTimeout
|
|
2993
|
+
]);
|
|
2994
|
+
this.authorizationCache = {
|
|
2995
|
+
mode: "mixed",
|
|
2996
|
+
authenticated: false,
|
|
2997
|
+
...metadata.resource ? { resource: metadata.resource } : {},
|
|
2998
|
+
...metadata.scopes_supported ? { scopesSupported: [...metadata.scopes_supported] } : {}
|
|
2999
|
+
};
|
|
3000
|
+
logger.info(
|
|
3001
|
+
"OAuth protected-resource metadata found after anonymous connection; server uses mixed auth"
|
|
3002
|
+
);
|
|
3003
|
+
} catch (error) {
|
|
3004
|
+
logger.debug("Mixed-auth metadata was not discovered:", error);
|
|
3005
|
+
} finally {
|
|
3006
|
+
if (timeout) clearTimeout(timeout);
|
|
3007
|
+
}
|
|
3008
|
+
return capabilities;
|
|
2804
3009
|
}
|
|
2805
3010
|
buildClientOptions() {
|
|
2806
3011
|
return {
|
|
@@ -2907,6 +3112,16 @@ var HttpConnector = class extends BaseConnector {
|
|
|
2907
3112
|
}
|
|
2908
3113
|
const baseUrl = this.baseUrl;
|
|
2909
3114
|
logger.debug(`Connecting to MCP implementation via HTTP: ${baseUrl}`);
|
|
3115
|
+
const oauthProvider = this.oauthProvider;
|
|
3116
|
+
if (oauthProvider) {
|
|
3117
|
+
try {
|
|
3118
|
+
this.hadAccessTokenAtConnect = Boolean(
|
|
3119
|
+
(await oauthProvider.tokens())?.access_token
|
|
3120
|
+
);
|
|
3121
|
+
} catch {
|
|
3122
|
+
this.hadAccessTokenAtConnect = false;
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
2910
3125
|
try {
|
|
2911
3126
|
await this.connectWithStreamableHttp(baseUrl);
|
|
2912
3127
|
logger.debug("\u2705 Successfully connected via streamable HTTP");
|
|
@@ -3258,6 +3473,7 @@ function createConnectorFromConfig(serverConfig, connectorOptions) {
|
|
|
3258
3473
|
fetch: serverConfig.fetch,
|
|
3259
3474
|
authToken: serverConfig.authToken,
|
|
3260
3475
|
authProvider: serverConfig.authProvider,
|
|
3476
|
+
detectMixedAuth: serverConfig.detectMixedAuth,
|
|
3261
3477
|
protocolNegotiation: serverConfig.protocolNegotiation,
|
|
3262
3478
|
timeout: serverConfig.timeout,
|
|
3263
3479
|
roots: serverConfig.roots,
|
|
@@ -3500,6 +3716,14 @@ var MCPConnection = class {
|
|
|
3500
3716
|
get serverInfo() {
|
|
3501
3717
|
return this.connector.serverInfo;
|
|
3502
3718
|
}
|
|
3719
|
+
/** OAuth state discovered for this connection, when available. */
|
|
3720
|
+
get authorization() {
|
|
3721
|
+
return this.connector.authorization;
|
|
3722
|
+
}
|
|
3723
|
+
/** Authenticate an already-connected mixed-auth server. */
|
|
3724
|
+
async authenticate() {
|
|
3725
|
+
await this.connector.authenticate();
|
|
3726
|
+
}
|
|
3503
3727
|
/**
|
|
3504
3728
|
* The negotiated protocol era for this session's connection:
|
|
3505
3729
|
* `"legacy"` (2025-era) or `"modern"` (2026-07-28-era).
|
|
@@ -3532,7 +3756,8 @@ var MCPConnection = class {
|
|
|
3532
3756
|
...server ? { server } : {},
|
|
3533
3757
|
capabilities,
|
|
3534
3758
|
instructions: this.connector.instructions,
|
|
3535
|
-
extensions
|
|
3759
|
+
extensions,
|
|
3760
|
+
...this.authorization ? { authorization: this.authorization } : {}
|
|
3536
3761
|
};
|
|
3537
3762
|
}
|
|
3538
3763
|
/**
|
|
@@ -3763,7 +3988,7 @@ var MCPConnection = class {
|
|
|
3763
3988
|
};
|
|
3764
3989
|
|
|
3765
3990
|
// src/core/base.ts
|
|
3766
|
-
function
|
|
3991
|
+
function isOAuthClientProvider2(provider) {
|
|
3767
3992
|
return !!provider && typeof provider === "object" && "redirectUrl" in provider && "clientMetadata" in provider;
|
|
3768
3993
|
}
|
|
3769
3994
|
var BaseMCPClient = class {
|
|
@@ -3996,7 +4221,7 @@ var BaseMCPClient = class {
|
|
|
3996
4221
|
...serverConfig,
|
|
3997
4222
|
authProvider: oauthProvider
|
|
3998
4223
|
};
|
|
3999
|
-
} else if ("authProvider" in serverConfig && serverConfig.authProvider &&
|
|
4224
|
+
} else if ("authProvider" in serverConfig && serverConfig.authProvider && isOAuthClientProvider2(serverConfig.authProvider)) {
|
|
4000
4225
|
oauthProvider = serverConfig.authProvider;
|
|
4001
4226
|
}
|
|
4002
4227
|
const openSession = async () => {
|
|
@@ -4336,6 +4561,7 @@ var BrowserMCPClient = class _BrowserMCPClient extends BaseMCPClient {
|
|
|
4336
4561
|
fetch: configuredFetch,
|
|
4337
4562
|
authToken,
|
|
4338
4563
|
authProvider,
|
|
4564
|
+
detectMixedAuth,
|
|
4339
4565
|
wrapTransport,
|
|
4340
4566
|
clientOptions,
|
|
4341
4567
|
protocolNegotiation,
|
|
@@ -4360,6 +4586,7 @@ var BrowserMCPClient = class _BrowserMCPClient extends BaseMCPClient {
|
|
|
4360
4586
|
fetch: configuredFetch ?? globalThis.fetch.bind(globalThis),
|
|
4361
4587
|
authToken,
|
|
4362
4588
|
authProvider,
|
|
4589
|
+
detectMixedAuth,
|
|
4363
4590
|
wrapTransport,
|
|
4364
4591
|
clientOptions,
|
|
4365
4592
|
onSampling: resolved.onSampling,
|
|
@@ -4470,6 +4697,7 @@ export {
|
|
|
4470
4697
|
createOAuthProvider,
|
|
4471
4698
|
detectFavicon,
|
|
4472
4699
|
getPackageVersion,
|
|
4700
|
+
isOAuthInteractionRequired,
|
|
4473
4701
|
isUnauthorized,
|
|
4474
4702
|
logger,
|
|
4475
4703
|
normalizeClientInfo,
|