@assistant-ui/react-mcp 0.1.19 → 0.1.20
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/auth/buildHeaders.d.ts +2 -4
- package/dist/auth/buildHeaders.d.ts.map +1 -1
- package/dist/auth/createOAuthProvider.d.ts +19 -20
- package/dist/auth/createOAuthProvider.d.ts.map +1 -1
- package/dist/auth/types.d.ts +12 -14
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/connector.d.ts +1 -3
- package/dist/connector.d.ts.map +1 -1
- package/dist/context/McpConnectorByIndexProvider.d.ts +2 -4
- package/dist/context/McpConnectorByIndexProvider.d.ts.map +1 -1
- package/dist/context/McpConnectorByIndexProvider.js +1 -1
- package/dist/context/McpCustomServerByIndexProvider.d.ts +2 -4
- package/dist/context/McpCustomServerByIndexProvider.d.ts.map +1 -1
- package/dist/context/McpCustomServerByIndexProvider.js +1 -1
- package/dist/context/McpServerByIdProvider.d.ts +2 -4
- package/dist/context/McpServerByIdProvider.d.ts.map +1 -1
- package/dist/context/McpServerByIdProvider.js +1 -1
- package/dist/hooks/useMcpOAuthCallback.d.ts +9 -11
- package/dist/hooks/useMcpOAuthCallback.d.ts.map +1 -1
- package/dist/hooks/useMcpOAuthCallback.js +2 -2
- package/dist/index.d.ts +19 -18
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -4
- package/dist/mcp-scope.d.ts +110 -112
- package/dist/mcp-scope.d.ts.map +1 -1
- package/dist/primitives/addForm/McpAddFormAuthFields.d.ts +12 -14
- package/dist/primitives/addForm/McpAddFormAuthFields.d.ts.map +1 -1
- package/dist/primitives/addForm/McpAddFormAuthSelect.d.ts +3 -5
- package/dist/primitives/addForm/McpAddFormAuthSelect.d.ts.map +1 -1
- package/dist/primitives/addForm/McpAddFormAuthSelect.js +1 -1
- package/dist/primitives/addForm/McpAddFormBearerTokenField.d.ts +3 -5
- package/dist/primitives/addForm/McpAddFormBearerTokenField.d.ts.map +1 -1
- package/dist/primitives/addForm/McpAddFormBearerTokenField.js +1 -1
- package/dist/primitives/addForm/McpAddFormCancel.d.ts +4 -6
- package/dist/primitives/addForm/McpAddFormCancel.d.ts.map +1 -1
- package/dist/primitives/addForm/McpAddFormCancel.js +1 -1
- package/dist/primitives/addForm/McpAddFormError.d.ts +4 -6
- package/dist/primitives/addForm/McpAddFormError.d.ts.map +1 -1
- package/dist/primitives/addForm/McpAddFormError.js +1 -1
- package/dist/primitives/addForm/McpAddFormNameField.d.ts +3 -5
- package/dist/primitives/addForm/McpAddFormNameField.d.ts.map +1 -1
- package/dist/primitives/addForm/McpAddFormNameField.js +1 -1
- package/dist/primitives/addForm/McpAddFormRoot.d.ts +9 -11
- package/dist/primitives/addForm/McpAddFormRoot.d.ts.map +1 -1
- package/dist/primitives/addForm/McpAddFormRoot.js +2 -2
- package/dist/primitives/addForm/McpAddFormScopesField.d.ts +3 -5
- package/dist/primitives/addForm/McpAddFormScopesField.d.ts.map +1 -1
- package/dist/primitives/addForm/McpAddFormScopesField.js +1 -1
- package/dist/primitives/addForm/McpAddFormSubmit.d.ts +4 -6
- package/dist/primitives/addForm/McpAddFormSubmit.d.ts.map +1 -1
- package/dist/primitives/addForm/McpAddFormSubmit.js +1 -1
- package/dist/primitives/addForm/McpAddFormUrlField.d.ts +3 -5
- package/dist/primitives/addForm/McpAddFormUrlField.d.ts.map +1 -1
- package/dist/primitives/addForm/McpAddFormUrlField.js +1 -1
- package/dist/primitives/addForm/context.d.ts +18 -20
- package/dist/primitives/addForm/context.d.ts.map +1 -1
- package/dist/primitives/addForm.d.ts +10 -15
- package/dist/primitives/addForm.d.ts.map +1 -1
- package/dist/primitives/elicitation/McpElicitationPrimitiveAccept.d.ts +4 -6
- package/dist/primitives/elicitation/McpElicitationPrimitiveAccept.d.ts.map +1 -1
- package/dist/primitives/elicitation/McpElicitationPrimitiveAccept.js +2 -2
- package/dist/primitives/elicitation/McpElicitationPrimitiveCancel.d.ts +4 -6
- package/dist/primitives/elicitation/McpElicitationPrimitiveCancel.d.ts.map +1 -1
- package/dist/primitives/elicitation/McpElicitationPrimitiveCancel.js +2 -2
- package/dist/primitives/elicitation/McpElicitationPrimitiveDecline.d.ts +4 -6
- package/dist/primitives/elicitation/McpElicitationPrimitiveDecline.d.ts.map +1 -1
- package/dist/primitives/elicitation/McpElicitationPrimitiveDecline.js +2 -2
- package/dist/primitives/elicitation/McpElicitationPrimitiveError.d.ts +4 -6
- package/dist/primitives/elicitation/McpElicitationPrimitiveError.d.ts.map +1 -1
- package/dist/primitives/elicitation/McpElicitationPrimitiveError.js +1 -1
- package/dist/primitives/elicitation/McpElicitationPrimitiveFields.d.ts +6 -8
- package/dist/primitives/elicitation/McpElicitationPrimitiveFields.d.ts.map +1 -1
- package/dist/primitives/elicitation/McpElicitationPrimitiveFields.js +1 -1
- package/dist/primitives/elicitation/McpElicitationPrimitiveItems.d.ts +6 -9
- package/dist/primitives/elicitation/McpElicitationPrimitiveItems.d.ts.map +1 -1
- package/dist/primitives/elicitation/McpElicitationPrimitiveItems.js +2 -2
- package/dist/primitives/elicitation/McpElicitationPrimitiveMessage.d.ts +4 -6
- package/dist/primitives/elicitation/McpElicitationPrimitiveMessage.d.ts.map +1 -1
- package/dist/primitives/elicitation/McpElicitationPrimitiveMessage.js +1 -1
- package/dist/primitives/elicitation/McpElicitationPrimitiveRoot.d.ts +4 -6
- package/dist/primitives/elicitation/McpElicitationPrimitiveRoot.d.ts.map +1 -1
- package/dist/primitives/elicitation/McpElicitationPrimitiveRoot.js +1 -1
- package/dist/primitives/elicitation/context.d.ts +8 -10
- package/dist/primitives/elicitation/context.d.ts.map +1 -1
- package/dist/primitives/elicitation/initialElicitationDraft.d.ts +0 -2
- package/dist/primitives/elicitation/initialElicitationDraft.d.ts.map +1 -1
- package/dist/primitives/elicitation/prepareElicitationContent.d.ts +3 -5
- package/dist/primitives/elicitation/prepareElicitationContent.d.ts.map +1 -1
- package/dist/primitives/elicitation.d.ts +8 -14
- package/dist/primitives/elicitation.d.ts.map +1 -1
- package/dist/primitives/manager/McpManagerAddCustomTrigger.d.ts +4 -6
- package/dist/primitives/manager/McpManagerAddCustomTrigger.d.ts.map +1 -1
- package/dist/primitives/manager/McpManagerAddCustomTrigger.js +1 -1
- package/dist/primitives/manager/McpManagerConnectors.d.ts +12 -14
- package/dist/primitives/manager/McpManagerConnectors.d.ts.map +1 -1
- package/dist/primitives/manager/McpManagerConnectors.js +1 -1
- package/dist/primitives/manager/McpManagerCustomServers.d.ts +12 -14
- package/dist/primitives/manager/McpManagerCustomServers.d.ts.map +1 -1
- package/dist/primitives/manager/McpManagerCustomServers.js +1 -1
- package/dist/primitives/manager/McpManagerRoot.d.ts +4 -6
- package/dist/primitives/manager/McpManagerRoot.d.ts.map +1 -1
- package/dist/primitives/manager/McpManagerRoot.js +2 -2
- package/dist/primitives/manager.d.ts +4 -9
- package/dist/primitives/manager.d.ts.map +1 -1
- package/dist/primitives/server/McpServerConnectButton.d.ts +4 -6
- package/dist/primitives/server/McpServerConnectButton.d.ts.map +1 -1
- package/dist/primitives/server/McpServerConnectButton.js +2 -2
- package/dist/primitives/server/McpServerDisconnectButton.d.ts +4 -6
- package/dist/primitives/server/McpServerDisconnectButton.d.ts.map +1 -1
- package/dist/primitives/server/McpServerDisconnectButton.js +2 -2
- package/dist/primitives/server/McpServerError.d.ts +4 -6
- package/dist/primitives/server/McpServerError.d.ts.map +1 -1
- package/dist/primitives/server/McpServerError.js +2 -2
- package/dist/primitives/server/McpServerIcon.d.ts +11 -13
- package/dist/primitives/server/McpServerIcon.d.ts.map +1 -1
- package/dist/primitives/server/McpServerIcon.js +2 -2
- package/dist/primitives/server/McpServerName.d.ts +4 -6
- package/dist/primitives/server/McpServerName.d.ts.map +1 -1
- package/dist/primitives/server/McpServerName.js +2 -2
- package/dist/primitives/server/McpServerOAuthLink.d.ts +9 -11
- package/dist/primitives/server/McpServerOAuthLink.d.ts.map +1 -1
- package/dist/primitives/server/McpServerOAuthLink.js +2 -2
- package/dist/primitives/server/McpServerRemoveButton.d.ts +4 -6
- package/dist/primitives/server/McpServerRemoveButton.d.ts.map +1 -1
- package/dist/primitives/server/McpServerRemoveButton.js +2 -2
- package/dist/primitives/server/McpServerRoot.d.ts +4 -6
- package/dist/primitives/server/McpServerRoot.d.ts.map +1 -1
- package/dist/primitives/server/McpServerRoot.js +2 -2
- package/dist/primitives/server/McpServerStatus.d.ts +4 -6
- package/dist/primitives/server/McpServerStatus.d.ts.map +1 -1
- package/dist/primitives/server/McpServerStatus.js +2 -2
- package/dist/primitives/server/McpServerToolName.d.ts +4 -6
- package/dist/primitives/server/McpServerToolName.d.ts.map +1 -1
- package/dist/primitives/server/McpServerToolName.js +1 -1
- package/dist/primitives/server/McpServerTools.d.ts +5 -7
- package/dist/primitives/server/McpServerTools.d.ts.map +1 -1
- package/dist/primitives/server/McpServerTools.js +2 -2
- package/dist/primitives/server.d.ts +11 -16
- package/dist/primitives/server.d.ts.map +1 -1
- package/dist/resources/McpManagerResource.d.ts +11 -13
- package/dist/resources/McpManagerResource.d.ts.map +1 -1
- package/dist/resources/McpManagerResource.js +2 -2
- package/dist/resources/McpServerRemovalFence.d.ts +1 -3
- package/dist/resources/McpServerRemovalFence.d.ts.map +1 -1
- package/dist/resources/McpServerResource.d.ts +18 -20
- package/dist/resources/McpServerResource.d.ts.map +1 -1
- package/dist/resources/McpServerResource.js +51 -43
- package/dist/resources/McpServerResource.js.map +1 -1
- package/dist/resources/storage/McpCustomStorage.d.ts +1 -3
- package/dist/resources/storage/McpCustomStorage.d.ts.map +1 -1
- package/dist/resources/storage/McpLocalStorage.d.ts +14 -16
- package/dist/resources/storage/McpLocalStorage.d.ts.map +1 -1
- package/dist/resources/storage/McpLocalStorage.js +1 -1
- package/dist/resources/storage/McpMemoryStorage.d.ts +1 -3
- package/dist/resources/storage/McpMemoryStorage.d.ts.map +1 -1
- package/dist/resources/storage/types.d.ts +22 -24
- package/dist/resources/storage/types.d.ts.map +1 -1
- package/dist/resources/validateElicitationContent.d.ts +3 -4
- package/dist/resources/validateElicitationContent.d.ts.map +1 -1
- package/dist/utils/createMcpId.d.ts +0 -2
- package/dist/utils/createMcpId.d.ts.map +1 -1
- package/dist/utils/invokeMcpCallback.d.ts +0 -2
- package/dist/utils/invokeMcpCallback.d.ts.map +1 -1
- package/dist/utils/serverId.d.ts +0 -2
- package/dist/utils/serverId.d.ts.map +1 -1
- package/dist/utils/serverUrl.d.ts +1 -3
- package/dist/utils/serverUrl.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/resources/McpServerResource.test.ts +112 -0
- package/src/resources/McpServerResource.ts +22 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invokeMcpCallback.d.ts","
|
|
1
|
+
{"version":3,"file":"invokeMcpCallback.d.ts","sourceRoot":"","sources":["../../src/utils/invokeMcpCallback.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB,GAAI,KAAK,SAAS,OAAO,EAAE,EACvD,MAAM,MAAM,EACZ,UAAU,CAAC,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,SAAS,EAChD,GAAG,MAAM,KAAK,KACb,IAEF,CAAC"}
|
package/dist/utils/serverId.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serverId.d.ts","
|
|
1
|
+
{"version":3,"file":"serverId.d.ts","sourceRoot":"","sources":["../../src/utils/serverId.ts"],"names":[],"mappings":"AAIA,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAcpD;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,CAUjE"}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { MCPPersistedAuthState } from "../auth/types.js";
|
|
2
|
-
//#region src/utils/serverUrl.d.ts
|
|
1
|
+
import type { MCPPersistedAuthState } from "../auth/types.js";
|
|
3
2
|
export declare const normalizeMcpServerUrl: (serverUrl: string) => string;
|
|
4
3
|
export declare const isAuthStateForServerUrl: (state: MCPPersistedAuthState | null, serverUrl: string) => boolean;
|
|
5
4
|
export declare const hasPersistedCredentials: (state: MCPPersistedAuthState | null) => boolean;
|
|
6
|
-
//#endregion
|
|
7
5
|
//# sourceMappingURL=serverUrl.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serverUrl.d.ts","
|
|
1
|
+
{"version":3,"file":"serverUrl.d.ts","sourceRoot":"","sources":["../../src/utils/serverUrl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,yBAAsB;AAE3D,eAAO,MAAM,qBAAqB,GAAI,WAAW,MAAM,KAAG,MAC3B,CAAC;AAEhC,eAAO,MAAM,uBAAuB,GAClC,OAAO,qBAAqB,GAAG,IAAI,EACnC,WAAW,MAAM,KAChB,OAUF,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,OAAO,qBAAqB,GAAG,IAAI,KAClC,OAA0D,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"description": "MCP server configuration and connection primitives for @assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -27,13 +27,13 @@
|
|
|
27
27
|
],
|
|
28
28
|
"sideEffects": false,
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@assistant-ui/core": "^0.3.
|
|
31
|
-
"@assistant-ui/store": "^0.3.
|
|
32
|
-
"@assistant-ui/tap": "^0.9.
|
|
30
|
+
"@assistant-ui/core": "^0.3.21",
|
|
31
|
+
"@assistant-ui/store": "^0.3.15",
|
|
32
|
+
"@assistant-ui/tap": "^0.9.19",
|
|
33
33
|
"@modelcontextprotocol/client": "^2.0.0",
|
|
34
34
|
"@modelcontextprotocol/core": "^2.0.0",
|
|
35
35
|
"@radix-ui/react-primitive": "^2.1.10",
|
|
36
|
-
"assistant-stream": "^0.3.
|
|
36
|
+
"assistant-stream": "^0.3.45"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@types/react": "*",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
48
|
+
"@assistant-ui/x-buildutils": "0.0.29",
|
|
49
49
|
"@testing-library/dom": "^10.4.2",
|
|
50
50
|
"@testing-library/react": "^16.3.3",
|
|
51
51
|
"@types/react": "^19.3.0",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createTapRoot, resource, useResource } from "@assistant-ui/tap";
|
|
2
2
|
import type { ClientOutput } from "@assistant-ui/store";
|
|
3
|
+
import { UnauthorizedError } from "@modelcontextprotocol/client";
|
|
3
4
|
import { useEffect, useState } from "react";
|
|
4
5
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
6
|
import type { MCPAuthConfig } from "../mcp-scope";
|
|
@@ -192,6 +193,15 @@ const mount = (
|
|
|
192
193
|
const unboundAuthMessage =
|
|
193
194
|
'MCP server "docs" has saved authentication for a different URL. Authenticate again to connect to https://example.com/mcp.';
|
|
194
195
|
|
|
196
|
+
const getOAuthProvider = (index: number) => {
|
|
197
|
+
const provider =
|
|
198
|
+
mocks.StreamableHTTPClientTransport.mock.calls[index]?.[1]?.authProvider;
|
|
199
|
+
if (!provider) throw new Error("Expected OAuth provider");
|
|
200
|
+
return provider as {
|
|
201
|
+
redirectToAuthorization: (url: URL) => Promise<void>;
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
|
|
195
205
|
describe("McpServerResource automatic authentication", () => {
|
|
196
206
|
beforeEach(resetMocks);
|
|
197
207
|
|
|
@@ -632,6 +642,70 @@ describe("McpServerResource connectionTimeout", () => {
|
|
|
632
642
|
describe("McpServerResource connection lifecycle", () => {
|
|
633
643
|
beforeEach(resetMocks);
|
|
634
644
|
|
|
645
|
+
it("publishes authorization URLs from the current connection", async () => {
|
|
646
|
+
const root = mount({ auth: { type: "oauth" } });
|
|
647
|
+
|
|
648
|
+
try {
|
|
649
|
+
await root.getValue().connect();
|
|
650
|
+
await getOAuthProvider(0).redirectToAuthorization(
|
|
651
|
+
new URL("https://auth.example.com/current"),
|
|
652
|
+
);
|
|
653
|
+
await waitForResourceUpdate(
|
|
654
|
+
() => root.getValue().getState().authorizationUrl !== null,
|
|
655
|
+
);
|
|
656
|
+
|
|
657
|
+
expect(root.getValue().getState().authorizationUrl).toBe(
|
|
658
|
+
"https://auth.example.com/current",
|
|
659
|
+
);
|
|
660
|
+
} finally {
|
|
661
|
+
root.unmount();
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
it("ignores authorization URLs after disconnect", async () => {
|
|
666
|
+
const root = mount({ auth: { type: "oauth" } });
|
|
667
|
+
|
|
668
|
+
try {
|
|
669
|
+
await root.getValue().connect();
|
|
670
|
+
const provider = getOAuthProvider(0);
|
|
671
|
+
await root.getValue().disconnect();
|
|
672
|
+
|
|
673
|
+
await provider.redirectToAuthorization(
|
|
674
|
+
new URL("https://auth.example.com/stale"),
|
|
675
|
+
);
|
|
676
|
+
await flushMacrotask();
|
|
677
|
+
|
|
678
|
+
expect(root.getValue().getState()).toMatchObject({
|
|
679
|
+
connectionState: "disconnected",
|
|
680
|
+
authorizationUrl: null,
|
|
681
|
+
});
|
|
682
|
+
} finally {
|
|
683
|
+
root.unmount();
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
it("ignores authorization URLs from a superseded connection", async () => {
|
|
688
|
+
const root = mount({ auth: { type: "oauth" } });
|
|
689
|
+
|
|
690
|
+
try {
|
|
691
|
+
await root.getValue().connect();
|
|
692
|
+
const staleProvider = getOAuthProvider(0);
|
|
693
|
+
await root.getValue().connect();
|
|
694
|
+
|
|
695
|
+
await staleProvider.redirectToAuthorization(
|
|
696
|
+
new URL("https://auth.example.com/stale"),
|
|
697
|
+
);
|
|
698
|
+
await flushMacrotask();
|
|
699
|
+
|
|
700
|
+
expect(root.getValue().getState()).toMatchObject({
|
|
701
|
+
connectionState: "connected",
|
|
702
|
+
authorizationUrl: null,
|
|
703
|
+
});
|
|
704
|
+
} finally {
|
|
705
|
+
root.unmount();
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
|
|
635
709
|
it("replaces direct resource connections when the server id changes", async () => {
|
|
636
710
|
const storage = createStorage();
|
|
637
711
|
let updateId = (_id: string) => {};
|
|
@@ -747,6 +821,44 @@ describe("McpServerResource connection lifecycle", () => {
|
|
|
747
821
|
describe("McpServerResource completeAuth", () => {
|
|
748
822
|
beforeEach(resetMocks);
|
|
749
823
|
|
|
824
|
+
it("transfers authorization URL ownership when reusing the auth transport", async () => {
|
|
825
|
+
const storage = createStorage();
|
|
826
|
+
vi.mocked(storage.loadAuthState).mockResolvedValue({
|
|
827
|
+
serverUrl: "https://example.com/mcp",
|
|
828
|
+
state: "expected",
|
|
829
|
+
});
|
|
830
|
+
mocks.connectResults.push(() =>
|
|
831
|
+
Promise.reject(new UnauthorizedError("authorization required")),
|
|
832
|
+
);
|
|
833
|
+
const root = mount({ auth: { type: "oauth" }, storage });
|
|
834
|
+
|
|
835
|
+
try {
|
|
836
|
+
await root.getValue().connect();
|
|
837
|
+
await waitForResourceUpdate(
|
|
838
|
+
() => root.getValue().getState().connectionState === "authRequired",
|
|
839
|
+
);
|
|
840
|
+
const provider = getOAuthProvider(0);
|
|
841
|
+
|
|
842
|
+
await root
|
|
843
|
+
.getValue()
|
|
844
|
+
.completeAuth("https://example.com/callback?code=abc&state=expected");
|
|
845
|
+
await provider.redirectToAuthorization(
|
|
846
|
+
new URL("https://auth.example.com/reauthorize"),
|
|
847
|
+
);
|
|
848
|
+
await waitForResourceUpdate(
|
|
849
|
+
() => root.getValue().getState().authorizationUrl !== null,
|
|
850
|
+
);
|
|
851
|
+
|
|
852
|
+
expect(root.getValue().getState()).toMatchObject({
|
|
853
|
+
connectionState: "connected",
|
|
854
|
+
authorizationUrl: "https://auth.example.com/reauthorize",
|
|
855
|
+
});
|
|
856
|
+
expect(mocks.transports).toHaveLength(1);
|
|
857
|
+
} finally {
|
|
858
|
+
root.unmount();
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
|
|
750
862
|
it("lets callback validation win over mount-time auto-connect", async () => {
|
|
751
863
|
const pendingLoads: Array<(value: MCPPersistedAuthState | null) => void> =
|
|
752
864
|
[];
|
|
@@ -105,6 +105,9 @@ const useMcpServerResourceInstance = (
|
|
|
105
105
|
const pendingTransportRef = useRef<StreamableHTTPClientTransport | null>(
|
|
106
106
|
null,
|
|
107
107
|
);
|
|
108
|
+
const transportGenerationRef = useRef(
|
|
109
|
+
new WeakMap<StreamableHTTPClientTransport, { current: number }>(),
|
|
110
|
+
);
|
|
108
111
|
const connectionGenerationRef = useRef(0);
|
|
109
112
|
const pendingAuthValidationRef = useRef<{
|
|
110
113
|
count: number;
|
|
@@ -254,19 +257,29 @@ const useMcpServerResourceInstance = (
|
|
|
254
257
|
});
|
|
255
258
|
|
|
256
259
|
const buildTransport = useEffectEvent(
|
|
257
|
-
async (): Promise<StreamableHTTPClientTransport> => {
|
|
260
|
+
async (generation: number): Promise<StreamableHTTPClientTransport> => {
|
|
258
261
|
if (props.auth.type === "oauth") {
|
|
262
|
+
const generationOwner = { current: generation };
|
|
259
263
|
const authProvider = createOAuthProvider({
|
|
260
264
|
serverId: props.id,
|
|
261
265
|
serverUrl: props.url,
|
|
262
266
|
config: props.auth,
|
|
263
267
|
storage: props.storage,
|
|
264
268
|
redirectUri: props.redirectUri,
|
|
265
|
-
onAuthorizationUrl: (url) =>
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
+
onAuthorizationUrl: (url) => {
|
|
270
|
+
if (isCurrentConnection(generationOwner.current)) {
|
|
271
|
+
setAuthorizationUrl(url.toString());
|
|
272
|
+
}
|
|
273
|
+
},
|
|
269
274
|
});
|
|
275
|
+
const transport = new StreamableHTTPClientTransport(
|
|
276
|
+
new URL(props.url),
|
|
277
|
+
{
|
|
278
|
+
authProvider,
|
|
279
|
+
},
|
|
280
|
+
);
|
|
281
|
+
transportGenerationRef.current.set(transport, generationOwner);
|
|
282
|
+
return transport;
|
|
270
283
|
}
|
|
271
284
|
if (props.auth.type === "bearer") {
|
|
272
285
|
const { state, unbound } = await loadAuthState();
|
|
@@ -443,7 +456,7 @@ const useMcpServerResourceInstance = (
|
|
|
443
456
|
setTools([]);
|
|
444
457
|
let transport: StreamableHTTPClientTransport | null = null;
|
|
445
458
|
try {
|
|
446
|
-
transport = await buildTransport();
|
|
459
|
+
transport = await buildTransport(generation);
|
|
447
460
|
if (!isCurrentConnection(generation)) {
|
|
448
461
|
await closeQueuedTransports([transport]);
|
|
449
462
|
return;
|
|
@@ -536,12 +549,14 @@ const useMcpServerResourceInstance = (
|
|
|
536
549
|
try {
|
|
537
550
|
let transport = transportRef.current;
|
|
538
551
|
if (!transport) {
|
|
539
|
-
transport = await buildTransport();
|
|
552
|
+
transport = await buildTransport(generation);
|
|
540
553
|
if (!isCurrentConnection(generation)) {
|
|
541
554
|
await closeQueuedTransports([transport]);
|
|
542
555
|
throw createInterruptedAuthError();
|
|
543
556
|
}
|
|
544
557
|
}
|
|
558
|
+
const generationOwner = transportGenerationRef.current.get(transport);
|
|
559
|
+
if (generationOwner) generationOwner.current = generation;
|
|
545
560
|
transportRef.current = null;
|
|
546
561
|
clientRef.current = null;
|
|
547
562
|
pendingTransportRef.current = transport;
|