@assistant-ui/react-mcp 0.1.16 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/auth/createOAuthProvider.d.ts +13 -2
  2. package/dist/auth/createOAuthProvider.d.ts.map +1 -1
  3. package/dist/auth/createOAuthProvider.js +109 -31
  4. package/dist/auth/createOAuthProvider.js.map +1 -1
  5. package/dist/auth/types.d.ts +5 -1
  6. package/dist/auth/types.d.ts.map +1 -1
  7. package/dist/primitives/addForm/McpAddFormAuthFields.js +71 -28
  8. package/dist/primitives/addForm/McpAddFormAuthFields.js.map +1 -1
  9. package/dist/primitives/addForm/McpAddFormError.d.ts.map +1 -1
  10. package/dist/primitives/addForm/McpAddFormError.js +30 -10
  11. package/dist/primitives/addForm/McpAddFormError.js.map +1 -1
  12. package/dist/primitives/addForm/McpAddFormNameField.js +31 -18
  13. package/dist/primitives/addForm/McpAddFormNameField.js.map +1 -1
  14. package/dist/primitives/addForm/McpAddFormRoot.d.ts.map +1 -1
  15. package/dist/primitives/addForm/McpAddFormRoot.js +32 -11
  16. package/dist/primitives/addForm/McpAddFormRoot.js.map +1 -1
  17. package/dist/primitives/addForm/McpAddFormUrlField.js +31 -18
  18. package/dist/primitives/addForm/McpAddFormUrlField.js.map +1 -1
  19. package/dist/primitives/addForm/context.d.ts +9 -1
  20. package/dist/primitives/addForm/context.d.ts.map +1 -1
  21. package/dist/primitives/addForm/context.js.map +1 -1
  22. package/dist/resources/McpManagerResource.d.ts.map +1 -1
  23. package/dist/resources/McpManagerResource.js +330 -247
  24. package/dist/resources/McpManagerResource.js.map +1 -1
  25. package/dist/resources/McpServerRemovalFence.d.ts +7 -0
  26. package/dist/resources/McpServerRemovalFence.d.ts.map +1 -0
  27. package/dist/resources/McpServerRemovalFence.js +11 -0
  28. package/dist/resources/McpServerRemovalFence.js.map +1 -0
  29. package/dist/resources/McpServerResource.d.ts.map +1 -1
  30. package/dist/resources/McpServerResource.js +41 -16
  31. package/dist/resources/McpServerResource.js.map +1 -1
  32. package/dist/resources/storage/McpLocalStorage.d.ts.map +1 -1
  33. package/dist/resources/storage/McpLocalStorage.js +16 -1
  34. package/dist/resources/storage/McpLocalStorage.js.map +1 -1
  35. package/dist/resources/storage/types.d.ts +10 -8
  36. package/dist/resources/storage/types.d.ts.map +1 -1
  37. package/dist/utils/serverUrl.d.ts +8 -0
  38. package/dist/utils/serverUrl.d.ts.map +1 -0
  39. package/dist/utils/serverUrl.js +15 -0
  40. package/dist/utils/serverUrl.js.map +1 -0
  41. package/package.json +7 -7
  42. package/src/auth/createOAuthProvider.test.ts +544 -35
  43. package/src/auth/createOAuthProvider.ts +201 -46
  44. package/src/auth/types.ts +5 -1
  45. package/src/primitives/addForm/McpAddFormAccessibility.test.tsx +261 -0
  46. package/src/primitives/addForm/McpAddFormAuthFields.tsx +33 -15
  47. package/src/primitives/addForm/McpAddFormError.tsx +17 -3
  48. package/src/primitives/addForm/McpAddFormNameField.tsx +13 -1
  49. package/src/primitives/addForm/McpAddFormRoot.tsx +51 -8
  50. package/src/primitives/addForm/McpAddFormUrlField.tsx +12 -1
  51. package/src/primitives/addForm/context.tsx +10 -0
  52. package/src/resources/McpManagerResource.test.ts +712 -9
  53. package/src/resources/McpManagerResource.ts +208 -56
  54. package/src/resources/McpServerRemovalFence.ts +21 -0
  55. package/src/resources/McpServerResource.test.ts +252 -17
  56. package/src/resources/McpServerResource.ts +40 -15
  57. package/src/resources/storage/McpLocalStorage.test.ts +28 -0
  58. package/src/resources/storage/McpLocalStorage.ts +23 -1
  59. package/src/resources/storage/types.ts +10 -8
  60. package/src/utils/serverUrl.test.ts +66 -0
  61. package/src/utils/serverUrl.ts +23 -0
@@ -5,14 +5,16 @@ import type { MCPPersistedAuthState } from "../../auth/types";
5
5
  export type MCPStorage = {
6
6
  /**
7
7
  * Stable identity of the backing store. Two storages with the same scopeId
8
- * must read and write the same persisted data. When present, server
9
- * connections and the OAuth write fence key on it, so swapping to a
10
- * differently-scoped storage reconnects instead of leaving a live OAuth flow
11
- * on the replaced store, and clearing through a same-scoped replacement still
12
- * waits for writes queued against the storage it replaced. When absent, the
13
- * fence falls back to object identity while connections never re-key at all,
14
- * so a storage rebuilt on every render has to declare a scopeId; without one
15
- * a clear runs unfenced against the writes queued by the object it replaced.
8
+ * must read and write the same persisted data. When present, custom servers,
9
+ * server connections, and the OAuth write fence key on it. Swapping to a
10
+ * differently-scoped storage rehydrates its custom servers and reconnects
11
+ * authenticated servers instead of retaining data or a live OAuth flow from
12
+ * the replaced store. Clearing through a same-scoped replacement still waits
13
+ * for writes queued against the storage it replaced. When absent, the fence
14
+ * falls back to object identity while custom servers and connections never
15
+ * re-key. Replacing an unscoped storage can therefore persist the previous
16
+ * store's custom servers into the replacement, and a clear runs unfenced
17
+ * against writes queued by the object it replaced.
16
18
  */
17
19
  scopeId?: string;
18
20
  loadCustomServers: () => Promise<MCPCustomServerRecord[]>;
@@ -0,0 +1,66 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ hasPersistedCredentials,
4
+ isAuthStateForServerUrl,
5
+ normalizeMcpServerUrl,
6
+ } from "./serverUrl";
7
+
8
+ describe("normalizeMcpServerUrl", () => {
9
+ it("normalizes host case and the default port", () => {
10
+ expect(normalizeMcpServerUrl("https://MCP.Example.com:443/mcp")).toBe(
11
+ "https://mcp.example.com/mcp",
12
+ );
13
+ });
14
+ });
15
+
16
+ describe("isAuthStateForServerUrl", () => {
17
+ it("matches equivalent spellings of the same endpoint", () => {
18
+ expect(
19
+ isAuthStateForServerUrl(
20
+ { serverUrl: "https://MCP.Example.com/mcp" },
21
+ "https://mcp.example.com:443/mcp",
22
+ ),
23
+ ).toBe(true);
24
+ });
25
+
26
+ it("rejects a different endpoint, an unbound record, and no record", () => {
27
+ expect(
28
+ isAuthStateForServerUrl(
29
+ { serverUrl: "https://a.example.com/mcp" },
30
+ "https://b.example.com/mcp",
31
+ ),
32
+ ).toBe(false);
33
+ expect(
34
+ isAuthStateForServerUrl({ token: "t" }, "https://a.example.com/mcp"),
35
+ ).toBe(false);
36
+ expect(isAuthStateForServerUrl(null, "https://a.example.com/mcp")).toBe(
37
+ false,
38
+ );
39
+ });
40
+
41
+ it("rejects an unparsable URL on either side", () => {
42
+ expect(
43
+ isAuthStateForServerUrl({ serverUrl: "not a url" }, "https://a.test/mcp"),
44
+ ).toBe(false);
45
+ expect(
46
+ isAuthStateForServerUrl({ serverUrl: "https://a.test/mcp" }, "not a url"),
47
+ ).toBe(false);
48
+ });
49
+ });
50
+
51
+ describe("hasPersistedCredentials", () => {
52
+ it("counts bearer and OAuth tokens, not flow state", () => {
53
+ expect(hasPersistedCredentials({ token: "t" })).toBe(true);
54
+ expect(hasPersistedCredentials({ token: "" })).toBe(false);
55
+ expect(
56
+ hasPersistedCredentials({
57
+ tokens: { access_token: "a", token_type: "bearer" },
58
+ }),
59
+ ).toBe(true);
60
+ expect(hasPersistedCredentials({ codeVerifier: "v", state: "s" })).toBe(
61
+ false,
62
+ );
63
+ expect(hasPersistedCredentials({})).toBe(false);
64
+ expect(hasPersistedCredentials(null)).toBe(false);
65
+ });
66
+ });
@@ -0,0 +1,23 @@
1
+ import type { MCPPersistedAuthState } from "../auth/types";
2
+
3
+ export const normalizeMcpServerUrl = (serverUrl: string): string =>
4
+ new URL(serverUrl).toString();
5
+
6
+ export const isAuthStateForServerUrl = (
7
+ state: MCPPersistedAuthState | null,
8
+ serverUrl: string,
9
+ ): boolean => {
10
+ if (state?.serverUrl === undefined) return false;
11
+ try {
12
+ return (
13
+ normalizeMcpServerUrl(state.serverUrl) ===
14
+ normalizeMcpServerUrl(serverUrl)
15
+ );
16
+ } catch {
17
+ return false;
18
+ }
19
+ };
20
+
21
+ export const hasPersistedCredentials = (
22
+ state: MCPPersistedAuthState | null,
23
+ ): boolean => Boolean(state?.tokens) || Boolean(state?.token);