@assistant-ui/react-mcp 0.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.
Files changed (221) hide show
  1. package/dist/auth/buildHeaders.d.ts +9 -0
  2. package/dist/auth/buildHeaders.d.ts.map +1 -0
  3. package/dist/auth/buildHeaders.js +15 -0
  4. package/dist/auth/buildHeaders.js.map +1 -0
  5. package/dist/auth/createOAuthProvider.d.ts +22 -0
  6. package/dist/auth/createOAuthProvider.d.ts.map +1 -0
  7. package/dist/auth/createOAuthProvider.js +148 -0
  8. package/dist/auth/createOAuthProvider.js.map +1 -0
  9. package/dist/auth/types.d.ts +9 -0
  10. package/dist/auth/types.d.ts.map +1 -0
  11. package/dist/auth/types.js +2 -0
  12. package/dist/auth/types.js.map +1 -0
  13. package/dist/connector.d.ts +3 -0
  14. package/dist/connector.d.ts.map +1 -0
  15. package/dist/connector.js +6 -0
  16. package/dist/connector.js.map +1 -0
  17. package/dist/context/McpConnectorByIndexProvider.d.ts +5 -0
  18. package/dist/context/McpConnectorByIndexProvider.d.ts.map +1 -0
  19. package/dist/context/McpConnectorByIndexProvider.js +13 -0
  20. package/dist/context/McpConnectorByIndexProvider.js.map +1 -0
  21. package/dist/context/McpCustomServerByIndexProvider.d.ts +5 -0
  22. package/dist/context/McpCustomServerByIndexProvider.d.ts.map +1 -0
  23. package/dist/context/McpCustomServerByIndexProvider.js +13 -0
  24. package/dist/context/McpCustomServerByIndexProvider.js.map +1 -0
  25. package/dist/context/McpServerByIdProvider.d.ts +5 -0
  26. package/dist/context/McpServerByIdProvider.d.ts.map +1 -0
  27. package/dist/context/McpServerByIdProvider.js +13 -0
  28. package/dist/context/McpServerByIdProvider.js.map +1 -0
  29. package/dist/hooks/useMcpOAuthCallback.d.ts +17 -0
  30. package/dist/hooks/useMcpOAuthCallback.d.ts.map +1 -0
  31. package/dist/hooks/useMcpOAuthCallback.js +59 -0
  32. package/dist/hooks/useMcpOAuthCallback.js.map +1 -0
  33. package/dist/index.d.ts +18 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +26 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/mcp-scope.d.ts +109 -0
  38. package/dist/mcp-scope.d.ts.map +1 -0
  39. package/dist/mcp-scope.js +2 -0
  40. package/dist/mcp-scope.js.map +1 -0
  41. package/dist/primitives/addForm/McpAddFormAuthFields.d.ts +15 -0
  42. package/dist/primitives/addForm/McpAddFormAuthFields.d.ts.map +1 -0
  43. package/dist/primitives/addForm/McpAddFormAuthFields.js +18 -0
  44. package/dist/primitives/addForm/McpAddFormAuthFields.js.map +1 -0
  45. package/dist/primitives/addForm/McpAddFormAuthSelect.d.ts +8 -0
  46. package/dist/primitives/addForm/McpAddFormAuthSelect.d.ts.map +1 -0
  47. package/dist/primitives/addForm/McpAddFormAuthSelect.js +24 -0
  48. package/dist/primitives/addForm/McpAddFormAuthSelect.js.map +1 -0
  49. package/dist/primitives/addForm/McpAddFormCancel.d.ts +10 -0
  50. package/dist/primitives/addForm/McpAddFormCancel.d.ts.map +1 -0
  51. package/dist/primitives/addForm/McpAddFormCancel.js +15 -0
  52. package/dist/primitives/addForm/McpAddFormCancel.js.map +1 -0
  53. package/dist/primitives/addForm/McpAddFormError.d.ts +10 -0
  54. package/dist/primitives/addForm/McpAddFormError.d.ts.map +1 -0
  55. package/dist/primitives/addForm/McpAddFormError.js +12 -0
  56. package/dist/primitives/addForm/McpAddFormError.js.map +1 -0
  57. package/dist/primitives/addForm/McpAddFormNameField.d.ts +8 -0
  58. package/dist/primitives/addForm/McpAddFormNameField.d.ts.map +1 -0
  59. package/dist/primitives/addForm/McpAddFormNameField.js +10 -0
  60. package/dist/primitives/addForm/McpAddFormNameField.js.map +1 -0
  61. package/dist/primitives/addForm/McpAddFormRoot.d.ts +16 -0
  62. package/dist/primitives/addForm/McpAddFormRoot.d.ts.map +1 -0
  63. package/dist/primitives/addForm/McpAddFormRoot.js +102 -0
  64. package/dist/primitives/addForm/McpAddFormRoot.js.map +1 -0
  65. package/dist/primitives/addForm/McpAddFormSubmit.d.ts +10 -0
  66. package/dist/primitives/addForm/McpAddFormSubmit.d.ts.map +1 -0
  67. package/dist/primitives/addForm/McpAddFormSubmit.js +10 -0
  68. package/dist/primitives/addForm/McpAddFormSubmit.js.map +1 -0
  69. package/dist/primitives/addForm/McpAddFormUrlField.d.ts +8 -0
  70. package/dist/primitives/addForm/McpAddFormUrlField.d.ts.map +1 -0
  71. package/dist/primitives/addForm/McpAddFormUrlField.js +10 -0
  72. package/dist/primitives/addForm/McpAddFormUrlField.js.map +1 -0
  73. package/dist/primitives/addForm/context.d.ts +21 -0
  74. package/dist/primitives/addForm/context.d.ts.map +1 -0
  75. package/dist/primitives/addForm/context.js +10 -0
  76. package/dist/primitives/addForm/context.js.map +1 -0
  77. package/dist/primitives/addForm.d.ts +9 -0
  78. package/dist/primitives/addForm.d.ts.map +1 -0
  79. package/dist/primitives/addForm.js +9 -0
  80. package/dist/primitives/addForm.js.map +1 -0
  81. package/dist/primitives/manager/McpManagerAddCustomTrigger.d.ts +10 -0
  82. package/dist/primitives/manager/McpManagerAddCustomTrigger.d.ts.map +1 -0
  83. package/dist/primitives/manager/McpManagerAddCustomTrigger.js +9 -0
  84. package/dist/primitives/manager/McpManagerAddCustomTrigger.js.map +1 -0
  85. package/dist/primitives/manager/McpManagerConnectors.d.ts +31 -0
  86. package/dist/primitives/manager/McpManagerConnectors.d.ts.map +1 -0
  87. package/dist/primitives/manager/McpManagerConnectors.js +41 -0
  88. package/dist/primitives/manager/McpManagerConnectors.js.map +1 -0
  89. package/dist/primitives/manager/McpManagerCustomServers.d.ts +31 -0
  90. package/dist/primitives/manager/McpManagerCustomServers.d.ts.map +1 -0
  91. package/dist/primitives/manager/McpManagerCustomServers.js +37 -0
  92. package/dist/primitives/manager/McpManagerCustomServers.js.map +1 -0
  93. package/dist/primitives/manager/McpManagerRoot.d.ts +10 -0
  94. package/dist/primitives/manager/McpManagerRoot.d.ts.map +1 -0
  95. package/dist/primitives/manager/McpManagerRoot.js +10 -0
  96. package/dist/primitives/manager/McpManagerRoot.js.map +1 -0
  97. package/dist/primitives/manager.d.ts +5 -0
  98. package/dist/primitives/manager.d.ts.map +1 -0
  99. package/dist/primitives/manager.js +5 -0
  100. package/dist/primitives/manager.js.map +1 -0
  101. package/dist/primitives/server/McpServerConnectButton.d.ts +10 -0
  102. package/dist/primitives/server/McpServerConnectButton.d.ts.map +1 -0
  103. package/dist/primitives/server/McpServerConnectButton.js +20 -0
  104. package/dist/primitives/server/McpServerConnectButton.js.map +1 -0
  105. package/dist/primitives/server/McpServerDisconnectButton.d.ts +10 -0
  106. package/dist/primitives/server/McpServerDisconnectButton.d.ts.map +1 -0
  107. package/dist/primitives/server/McpServerDisconnectButton.js +20 -0
  108. package/dist/primitives/server/McpServerDisconnectButton.js.map +1 -0
  109. package/dist/primitives/server/McpServerError.d.ts +10 -0
  110. package/dist/primitives/server/McpServerError.d.ts.map +1 -0
  111. package/dist/primitives/server/McpServerError.js +12 -0
  112. package/dist/primitives/server/McpServerError.js.map +1 -0
  113. package/dist/primitives/server/McpServerIcon.d.ts +18 -0
  114. package/dist/primitives/server/McpServerIcon.d.ts.map +1 -0
  115. package/dist/primitives/server/McpServerIcon.js +14 -0
  116. package/dist/primitives/server/McpServerIcon.js.map +1 -0
  117. package/dist/primitives/server/McpServerName.d.ts +10 -0
  118. package/dist/primitives/server/McpServerName.d.ts.map +1 -0
  119. package/dist/primitives/server/McpServerName.js +10 -0
  120. package/dist/primitives/server/McpServerName.js.map +1 -0
  121. package/dist/primitives/server/McpServerOAuthLink.d.ts +16 -0
  122. package/dist/primitives/server/McpServerOAuthLink.d.ts.map +1 -0
  123. package/dist/primitives/server/McpServerOAuthLink.js +13 -0
  124. package/dist/primitives/server/McpServerOAuthLink.js.map +1 -0
  125. package/dist/primitives/server/McpServerRemoveButton.d.ts +10 -0
  126. package/dist/primitives/server/McpServerRemoveButton.d.ts.map +1 -0
  127. package/dist/primitives/server/McpServerRemoveButton.js +18 -0
  128. package/dist/primitives/server/McpServerRemoveButton.js.map +1 -0
  129. package/dist/primitives/server/McpServerRoot.d.ts +10 -0
  130. package/dist/primitives/server/McpServerRoot.d.ts.map +1 -0
  131. package/dist/primitives/server/McpServerRoot.js +13 -0
  132. package/dist/primitives/server/McpServerRoot.js.map +1 -0
  133. package/dist/primitives/server/McpServerStatus.d.ts +10 -0
  134. package/dist/primitives/server/McpServerStatus.d.ts.map +1 -0
  135. package/dist/primitives/server/McpServerStatus.js +10 -0
  136. package/dist/primitives/server/McpServerStatus.js.map +1 -0
  137. package/dist/primitives/server/McpServerToolName.d.ts +10 -0
  138. package/dist/primitives/server/McpServerToolName.d.ts.map +1 -0
  139. package/dist/primitives/server/McpServerToolName.js +10 -0
  140. package/dist/primitives/server/McpServerToolName.js.map +1 -0
  141. package/dist/primitives/server/McpServerTools.d.ts +10 -0
  142. package/dist/primitives/server/McpServerTools.d.ts.map +1 -0
  143. package/dist/primitives/server/McpServerTools.js +19 -0
  144. package/dist/primitives/server/McpServerTools.js.map +1 -0
  145. package/dist/primitives/server.d.ts +12 -0
  146. package/dist/primitives/server.d.ts.map +1 -0
  147. package/dist/primitives/server.js +12 -0
  148. package/dist/primitives/server.js.map +1 -0
  149. package/dist/resources/McpManagerResource.d.ts +13 -0
  150. package/dist/resources/McpManagerResource.d.ts.map +1 -0
  151. package/dist/resources/McpManagerResource.js +191 -0
  152. package/dist/resources/McpManagerResource.js.map +1 -0
  153. package/dist/resources/McpServerResource.d.ts +17 -0
  154. package/dist/resources/McpServerResource.d.ts.map +1 -0
  155. package/dist/resources/McpServerResource.js +239 -0
  156. package/dist/resources/McpServerResource.js.map +1 -0
  157. package/dist/resources/storage/McpCustomStorage.d.ts +3 -0
  158. package/dist/resources/storage/McpCustomStorage.d.ts.map +1 -0
  159. package/dist/resources/storage/McpCustomStorage.js +3 -0
  160. package/dist/resources/storage/McpCustomStorage.js.map +1 -0
  161. package/dist/resources/storage/McpLocalStorage.d.ts +9 -0
  162. package/dist/resources/storage/McpLocalStorage.d.ts.map +1 -0
  163. package/dist/resources/storage/McpLocalStorage.js +67 -0
  164. package/dist/resources/storage/McpLocalStorage.js.map +1 -0
  165. package/dist/resources/storage/McpMemoryStorage.d.ts +3 -0
  166. package/dist/resources/storage/McpMemoryStorage.d.ts.map +1 -0
  167. package/dist/resources/storage/McpMemoryStorage.js +19 -0
  168. package/dist/resources/storage/McpMemoryStorage.js.map +1 -0
  169. package/dist/resources/storage/types.d.ts +12 -0
  170. package/dist/resources/storage/types.d.ts.map +1 -0
  171. package/dist/resources/storage/types.js +2 -0
  172. package/dist/resources/storage/types.js.map +1 -0
  173. package/dist/utils/serverId.d.ts +2 -0
  174. package/dist/utils/serverId.d.ts.map +1 -0
  175. package/dist/utils/serverId.js +16 -0
  176. package/dist/utils/serverId.js.map +1 -0
  177. package/package.json +67 -0
  178. package/src/auth/buildHeaders.ts +19 -0
  179. package/src/auth/createOAuthProvider.ts +172 -0
  180. package/src/auth/types.ts +12 -0
  181. package/src/connector.ts +7 -0
  182. package/src/context/McpConnectorByIndexProvider.tsx +16 -0
  183. package/src/context/McpCustomServerByIndexProvider.tsx +16 -0
  184. package/src/context/McpServerByIdProvider.tsx +17 -0
  185. package/src/hooks/useMcpOAuthCallback.tsx +79 -0
  186. package/src/index.ts +68 -0
  187. package/src/mcp-scope.ts +110 -0
  188. package/src/primitives/addForm/McpAddFormAuthFields.tsx +51 -0
  189. package/src/primitives/addForm/McpAddFormAuthSelect.tsx +55 -0
  190. package/src/primitives/addForm/McpAddFormCancel.tsx +33 -0
  191. package/src/primitives/addForm/McpAddFormError.tsx +27 -0
  192. package/src/primitives/addForm/McpAddFormNameField.tsx +34 -0
  193. package/src/primitives/addForm/McpAddFormRoot.tsx +141 -0
  194. package/src/primitives/addForm/McpAddFormSubmit.tsx +30 -0
  195. package/src/primitives/addForm/McpAddFormUrlField.tsx +35 -0
  196. package/src/primitives/addForm/context.tsx +37 -0
  197. package/src/primitives/addForm.ts +8 -0
  198. package/src/primitives/manager/McpManagerAddCustomTrigger.tsx +21 -0
  199. package/src/primitives/manager/McpManagerConnectors.tsx +76 -0
  200. package/src/primitives/manager/McpManagerCustomServers.tsx +72 -0
  201. package/src/primitives/manager/McpManagerRoot.tsx +28 -0
  202. package/src/primitives/manager.ts +4 -0
  203. package/src/primitives/server/McpServerConnectButton.tsx +39 -0
  204. package/src/primitives/server/McpServerDisconnectButton.tsx +38 -0
  205. package/src/primitives/server/McpServerError.tsx +27 -0
  206. package/src/primitives/server/McpServerIcon.tsx +35 -0
  207. package/src/primitives/server/McpServerName.tsx +26 -0
  208. package/src/primitives/server/McpServerOAuthLink.tsx +38 -0
  209. package/src/primitives/server/McpServerRemoveButton.tsx +35 -0
  210. package/src/primitives/server/McpServerRoot.tsx +35 -0
  211. package/src/primitives/server/McpServerStatus.tsx +26 -0
  212. package/src/primitives/server/McpServerToolName.tsx +26 -0
  213. package/src/primitives/server/McpServerTools.tsx +39 -0
  214. package/src/primitives/server.ts +14 -0
  215. package/src/resources/McpManagerResource.ts +253 -0
  216. package/src/resources/McpServerResource.ts +292 -0
  217. package/src/resources/storage/McpCustomStorage.ts +6 -0
  218. package/src/resources/storage/McpLocalStorage.ts +77 -0
  219. package/src/resources/storage/McpMemoryStorage.ts +22 -0
  220. package/src/resources/storage/types.ts +16 -0
  221. package/src/utils/serverId.ts +19 -0
@@ -0,0 +1,38 @@
1
+ import {
2
+ type ComponentPropsWithoutRef,
3
+ type ComponentRef,
4
+ forwardRef,
5
+ } from "react";
6
+ import { Primitive } from "@radix-ui/react-primitive";
7
+ import { useAui, useAuiState } from "@assistant-ui/store";
8
+
9
+ export namespace McpServerPrimitiveDisconnectButton {
10
+ export type Element = ComponentRef<typeof Primitive.button>;
11
+ export type Props = ComponentPropsWithoutRef<typeof Primitive.button>;
12
+ }
13
+
14
+ const DISCONNECTABLE = new Set(["connected", "connecting", "authPending"]);
15
+
16
+ export const McpServerPrimitiveDisconnectButton = forwardRef<
17
+ McpServerPrimitiveDisconnectButton.Element,
18
+ McpServerPrimitiveDisconnectButton.Props
19
+ >((props, ref) => {
20
+ const state = useAuiState((s) => s.mcpServer.connectionState);
21
+ const aui = useAui();
22
+ if (!DISCONNECTABLE.has(state)) return null;
23
+ return (
24
+ <Primitive.button
25
+ {...props}
26
+ type="button"
27
+ ref={ref}
28
+ onClick={(e) => {
29
+ props.onClick?.(e);
30
+ if (e.defaultPrevented) return;
31
+ void aui.mcpServer().disconnect();
32
+ }}
33
+ />
34
+ );
35
+ });
36
+
37
+ McpServerPrimitiveDisconnectButton.displayName =
38
+ "McpServerPrimitive.DisconnectButton";
@@ -0,0 +1,27 @@
1
+ import {
2
+ type ComponentPropsWithoutRef,
3
+ type ComponentRef,
4
+ forwardRef,
5
+ } from "react";
6
+ import { Primitive } from "@radix-ui/react-primitive";
7
+ import { useAuiState } from "@assistant-ui/store";
8
+
9
+ export namespace McpServerPrimitiveError {
10
+ export type Element = ComponentRef<typeof Primitive.div>;
11
+ export type Props = ComponentPropsWithoutRef<typeof Primitive.div>;
12
+ }
13
+
14
+ export const McpServerPrimitiveError = forwardRef<
15
+ McpServerPrimitiveError.Element,
16
+ McpServerPrimitiveError.Props
17
+ >((props, ref) => {
18
+ const message = useAuiState((s) => s.mcpServer.lastError?.message ?? null);
19
+ if (message === null) return null;
20
+ return (
21
+ <Primitive.div {...props} ref={ref}>
22
+ {props.children ?? message}
23
+ </Primitive.div>
24
+ );
25
+ });
26
+
27
+ McpServerPrimitiveError.displayName = "McpServerPrimitive.Error";
@@ -0,0 +1,35 @@
1
+ import {
2
+ type ComponentPropsWithoutRef,
3
+ type ComponentRef,
4
+ forwardRef,
5
+ } from "react";
6
+ import { Primitive } from "@radix-ui/react-primitive";
7
+ import { useAuiState } from "@assistant-ui/store";
8
+
9
+ export namespace McpServerPrimitiveIcon {
10
+ export type Element = ComponentRef<typeof Primitive.img>;
11
+ export type Props = Omit<
12
+ ComponentPropsWithoutRef<typeof Primitive.img>,
13
+ "src" | "alt"
14
+ > & {
15
+ /** Override the icon URL. Defaults to the server's icon prop. */
16
+ src?: string;
17
+ alt?: string;
18
+ };
19
+ }
20
+
21
+ export const McpServerPrimitiveIcon = forwardRef<
22
+ McpServerPrimitiveIcon.Element,
23
+ McpServerPrimitiveIcon.Props
24
+ >(({ src, alt, ...props }, ref) => {
25
+ const iconUrl = useAuiState((s) => s.mcpServer.icon ?? null);
26
+ const name = useAuiState((s) => s.mcpServer.name);
27
+ const effective = src ?? iconUrl;
28
+ if (!effective) return null;
29
+ return (
30
+ // biome-ignore lint/performance/noImgElement: unstyled primitive — consumers can swap via asChild
31
+ <Primitive.img {...props} ref={ref} src={effective} alt={alt ?? name} />
32
+ );
33
+ });
34
+
35
+ McpServerPrimitiveIcon.displayName = "McpServerPrimitive.Icon";
@@ -0,0 +1,26 @@
1
+ import {
2
+ type ComponentPropsWithoutRef,
3
+ type ComponentRef,
4
+ forwardRef,
5
+ } from "react";
6
+ import { Primitive } from "@radix-ui/react-primitive";
7
+ import { useAuiState } from "@assistant-ui/store";
8
+
9
+ export namespace McpServerPrimitiveName {
10
+ export type Element = ComponentRef<typeof Primitive.span>;
11
+ export type Props = ComponentPropsWithoutRef<typeof Primitive.span>;
12
+ }
13
+
14
+ export const McpServerPrimitiveName = forwardRef<
15
+ McpServerPrimitiveName.Element,
16
+ McpServerPrimitiveName.Props
17
+ >((props, ref) => {
18
+ const name = useAuiState((s) => s.mcpServer.name);
19
+ return (
20
+ <Primitive.span {...props} ref={ref}>
21
+ {props.children ?? name}
22
+ </Primitive.span>
23
+ );
24
+ });
25
+
26
+ McpServerPrimitiveName.displayName = "McpServerPrimitive.Name";
@@ -0,0 +1,38 @@
1
+ import {
2
+ type ComponentPropsWithoutRef,
3
+ type ComponentRef,
4
+ forwardRef,
5
+ } from "react";
6
+ import { Primitive } from "@radix-ui/react-primitive";
7
+ import { useAuiState } from "@assistant-ui/store";
8
+
9
+ export namespace McpServerPrimitiveOAuthLink {
10
+ export type Element = ComponentRef<typeof Primitive.a>;
11
+ export type Props = Omit<
12
+ ComponentPropsWithoutRef<typeof Primitive.a>,
13
+ "href"
14
+ > & {
15
+ /** Optional href override; defaults to the server's authorizationUrl. */
16
+ href?: string;
17
+ };
18
+ }
19
+
20
+ export const McpServerPrimitiveOAuthLink = forwardRef<
21
+ McpServerPrimitiveOAuthLink.Element,
22
+ McpServerPrimitiveOAuthLink.Props
23
+ >(({ href, ...props }, ref) => {
24
+ const url = useAuiState((s) => s.mcpServer.authorizationUrl);
25
+ const effective = href ?? url;
26
+ if (!effective) return null;
27
+ return (
28
+ <Primitive.a
29
+ target="_blank"
30
+ {...props}
31
+ ref={ref}
32
+ href={effective}
33
+ rel={props.rel ?? "noopener noreferrer"}
34
+ />
35
+ );
36
+ });
37
+
38
+ McpServerPrimitiveOAuthLink.displayName = "McpServerPrimitive.OAuthLink";
@@ -0,0 +1,35 @@
1
+ import {
2
+ type ComponentPropsWithoutRef,
3
+ type ComponentRef,
4
+ forwardRef,
5
+ } from "react";
6
+ import { Primitive } from "@radix-ui/react-primitive";
7
+ import { useAui, useAuiState } from "@assistant-ui/store";
8
+
9
+ export namespace McpServerPrimitiveRemoveButton {
10
+ export type Element = ComponentRef<typeof Primitive.button>;
11
+ export type Props = ComponentPropsWithoutRef<typeof Primitive.button>;
12
+ }
13
+
14
+ export const McpServerPrimitiveRemoveButton = forwardRef<
15
+ McpServerPrimitiveRemoveButton.Element,
16
+ McpServerPrimitiveRemoveButton.Props
17
+ >((props, ref) => {
18
+ const kind = useAuiState((s) => s.mcpServer.kind);
19
+ const aui = useAui();
20
+ if (kind !== "custom") return null;
21
+ return (
22
+ <Primitive.button
23
+ {...props}
24
+ type="button"
25
+ ref={ref}
26
+ onClick={(e) => {
27
+ props.onClick?.(e);
28
+ if (e.defaultPrevented) return;
29
+ void aui.mcpServer().remove();
30
+ }}
31
+ />
32
+ );
33
+ });
34
+
35
+ McpServerPrimitiveRemoveButton.displayName = "McpServerPrimitive.RemoveButton";
@@ -0,0 +1,35 @@
1
+ import {
2
+ type ComponentPropsWithoutRef,
3
+ type ComponentRef,
4
+ forwardRef,
5
+ } from "react";
6
+ import { Primitive } from "@radix-ui/react-primitive";
7
+ import { useAuiState } from "@assistant-ui/store";
8
+
9
+ export namespace McpServerPrimitiveRoot {
10
+ export type Element = ComponentRef<typeof Primitive.div>;
11
+ export type Props = ComponentPropsWithoutRef<typeof Primitive.div>;
12
+ }
13
+
14
+ export const McpServerPrimitiveRoot = forwardRef<
15
+ McpServerPrimitiveRoot.Element,
16
+ McpServerPrimitiveRoot.Props
17
+ >((props, ref) => {
18
+ const id = useAuiState((s) => s.mcpServer.id);
19
+ const kind = useAuiState((s) => s.mcpServer.kind);
20
+ const connectionState = useAuiState((s) => s.mcpServer.connectionState);
21
+ const hasError = useAuiState((s) => s.mcpServer.lastError !== null);
22
+
23
+ return (
24
+ <Primitive.div
25
+ {...props}
26
+ ref={ref}
27
+ data-server-id={id}
28
+ data-kind={kind}
29
+ data-connection-state={connectionState}
30
+ data-has-error={hasError || undefined}
31
+ />
32
+ );
33
+ });
34
+
35
+ McpServerPrimitiveRoot.displayName = "McpServerPrimitive.Root";
@@ -0,0 +1,26 @@
1
+ import {
2
+ type ComponentPropsWithoutRef,
3
+ type ComponentRef,
4
+ forwardRef,
5
+ } from "react";
6
+ import { Primitive } from "@radix-ui/react-primitive";
7
+ import { useAuiState } from "@assistant-ui/store";
8
+
9
+ export namespace McpServerPrimitiveStatus {
10
+ export type Element = ComponentRef<typeof Primitive.span>;
11
+ export type Props = ComponentPropsWithoutRef<typeof Primitive.span>;
12
+ }
13
+
14
+ export const McpServerPrimitiveStatus = forwardRef<
15
+ McpServerPrimitiveStatus.Element,
16
+ McpServerPrimitiveStatus.Props
17
+ >((props, ref) => {
18
+ const state = useAuiState((s) => s.mcpServer.connectionState);
19
+ return (
20
+ <Primitive.span {...props} ref={ref} data-state={state}>
21
+ {props.children ?? state}
22
+ </Primitive.span>
23
+ );
24
+ });
25
+
26
+ McpServerPrimitiveStatus.displayName = "McpServerPrimitive.Status";
@@ -0,0 +1,26 @@
1
+ import {
2
+ type ComponentPropsWithoutRef,
3
+ type ComponentRef,
4
+ forwardRef,
5
+ } from "react";
6
+ import { Primitive } from "@radix-ui/react-primitive";
7
+ import { useMcpServerTool } from "./McpServerTools";
8
+
9
+ export namespace McpServerPrimitiveToolName {
10
+ export type Element = ComponentRef<typeof Primitive.span>;
11
+ export type Props = ComponentPropsWithoutRef<typeof Primitive.span>;
12
+ }
13
+
14
+ export const McpServerPrimitiveToolName = forwardRef<
15
+ McpServerPrimitiveToolName.Element,
16
+ McpServerPrimitiveToolName.Props
17
+ >((props, ref) => {
18
+ const tool = useMcpServerTool();
19
+ return (
20
+ <Primitive.span {...props} ref={ref} data-tool-name={tool.name}>
21
+ {props.children ?? tool.name}
22
+ </Primitive.span>
23
+ );
24
+ });
25
+
26
+ McpServerPrimitiveToolName.displayName = "McpServerPrimitive.ToolName";
@@ -0,0 +1,39 @@
1
+ import { type FC, type ReactNode, createContext, useContext } from "react";
2
+ import { useAuiState } from "@assistant-ui/store";
3
+ import type { MCPToolInfo } from "../../mcp-scope";
4
+
5
+ const ToolContext = createContext<MCPToolInfo | null>(null);
6
+
7
+ export const useMcpServerTool = (): MCPToolInfo => {
8
+ const tool = useContext(ToolContext);
9
+ if (!tool) {
10
+ throw new Error(
11
+ "useMcpServerTool must be used inside <McpServerPrimitive.Tools>",
12
+ );
13
+ }
14
+ return tool;
15
+ };
16
+
17
+ export namespace McpServerPrimitiveTools {
18
+ export type Props = {
19
+ children: ReactNode | ((tool: MCPToolInfo) => ReactNode);
20
+ };
21
+ }
22
+
23
+ export const McpServerPrimitiveTools: FC<McpServerPrimitiveTools.Props> = ({
24
+ children,
25
+ }) => {
26
+ const tools = useAuiState((s) => s.mcpServer.tools);
27
+ if (tools.length === 0) return null;
28
+ return (
29
+ <>
30
+ {tools.map((tool) => (
31
+ <ToolContext.Provider key={tool.name} value={tool}>
32
+ {typeof children === "function" ? children(tool) : children}
33
+ </ToolContext.Provider>
34
+ ))}
35
+ </>
36
+ );
37
+ };
38
+
39
+ McpServerPrimitiveTools.displayName = "McpServerPrimitive.Tools";
@@ -0,0 +1,14 @@
1
+ export { McpServerPrimitiveRoot as Root } from "./server/McpServerRoot";
2
+ export { McpServerPrimitiveName as Name } from "./server/McpServerName";
3
+ export { McpServerPrimitiveStatus as Status } from "./server/McpServerStatus";
4
+ export { McpServerPrimitiveError as Error } from "./server/McpServerError";
5
+ export { McpServerPrimitiveIcon as Icon } from "./server/McpServerIcon";
6
+ export { McpServerPrimitiveConnectButton as ConnectButton } from "./server/McpServerConnectButton";
7
+ export { McpServerPrimitiveDisconnectButton as DisconnectButton } from "./server/McpServerDisconnectButton";
8
+ export { McpServerPrimitiveRemoveButton as RemoveButton } from "./server/McpServerRemoveButton";
9
+ export { McpServerPrimitiveOAuthLink as OAuthLink } from "./server/McpServerOAuthLink";
10
+ export {
11
+ McpServerPrimitiveTools as Tools,
12
+ useMcpServerTool,
13
+ } from "./server/McpServerTools";
14
+ export { McpServerPrimitiveToolName as ToolName } from "./server/McpServerToolName";
@@ -0,0 +1,253 @@
1
+ import {
2
+ resource,
3
+ tapState,
4
+ tapResource,
5
+ tapEffect,
6
+ tapMemo,
7
+ tapEffectEvent,
8
+ tapRef,
9
+ withKey,
10
+ } from "@assistant-ui/tap";
11
+ import {
12
+ tapClientLookup,
13
+ tapAssistantClientRef,
14
+ attachTransformScopes,
15
+ type ClientOutput,
16
+ } from "@assistant-ui/store";
17
+ import { ModelContext } from "@assistant-ui/core/store";
18
+ import type { Tool } from "assistant-stream";
19
+ import { McpServerResource } from "./McpServerResource";
20
+ import { McpLocalStorage } from "./storage/McpLocalStorage";
21
+ import type { MCPStorageElement } from "./storage/types";
22
+ import type {
23
+ MCPAuthConfig,
24
+ MCPConnector,
25
+ MCPCustomServerRecord,
26
+ MCPManagerState,
27
+ } from "../mcp-scope";
28
+
29
+ export type McpManagerResourceProps = {
30
+ connectors?: MCPConnector[] | undefined;
31
+ storage?: MCPStorageElement | undefined;
32
+ /** OAuth redirect target. Defaults to `${origin}/mcp/callback`. */
33
+ oauthRedirectUri?: string | undefined;
34
+ /** Connect on mount when usable auth exists. Default true. */
35
+ autoConnect?: boolean | undefined;
36
+ };
37
+
38
+ function defaultRedirectUri(): string {
39
+ if (typeof window === "undefined") return "";
40
+ return `${window.location.origin}/mcp/callback`;
41
+ }
42
+
43
+ export const McpManagerResource = resource(
44
+ (props: McpManagerResourceProps): ClientOutput<"mcp"> => {
45
+ const connectors = props.connectors ?? [];
46
+ const autoConnect = props.autoConnect ?? true;
47
+ const redirectUri = props.oauthRedirectUri ?? defaultRedirectUri();
48
+
49
+ const storageElement = props.storage ?? McpLocalStorage();
50
+ const storage = tapResource(storageElement);
51
+
52
+ const [customServers, setCustomServers] = tapState<MCPCustomServerRecord[]>(
53
+ [],
54
+ );
55
+ const [isHydrated, setIsHydrated] = tapState(false);
56
+
57
+ const hydratedRef = tapRef(false);
58
+
59
+ const hydrate = tapEffectEvent(async (signal: { cancelled: boolean }) => {
60
+ try {
61
+ const records = await storage.loadCustomServers();
62
+ if (signal.cancelled) return;
63
+ // Merge rather than replace so any addCustomServer calls that
64
+ // happened before hydration resolved aren't silently overwritten.
65
+ // Persisted order wins; pre-hydration locals append.
66
+ setCustomServers((prev) => {
67
+ if (prev.length === 0) return records;
68
+ const persistedIds = new Set(records.map((r) => r.id));
69
+ return [...records, ...prev.filter((r) => !persistedIds.has(r.id))];
70
+ });
71
+ } finally {
72
+ if (!signal.cancelled) {
73
+ hydratedRef.current = true;
74
+ setIsHydrated(true);
75
+ }
76
+ }
77
+ });
78
+
79
+ tapEffect(() => {
80
+ const signal = { cancelled: false };
81
+ void hydrate(signal);
82
+ return () => {
83
+ signal.cancelled = true;
84
+ };
85
+ }, []);
86
+
87
+ const persistCustomServers = tapEffectEvent(
88
+ async (records: MCPCustomServerRecord[]) => {
89
+ if (!hydratedRef.current) return;
90
+ await storage.saveCustomServers(records);
91
+ },
92
+ );
93
+
94
+ tapEffect(() => {
95
+ void persistCustomServers(customServers);
96
+ }, [customServers]);
97
+
98
+ const serverElements = tapMemo(() => {
99
+ const connectorElements = connectors.map((c) =>
100
+ withKey(
101
+ c.id,
102
+ McpServerResource({
103
+ id: c.id,
104
+ kind: "connector",
105
+ name: c.name,
106
+ url: c.url,
107
+ icon: c.icon,
108
+ auth: c.auth,
109
+ storage,
110
+ redirectUri,
111
+ autoConnect,
112
+ onRemove: async () => {
113
+ // connectors cannot be removed
114
+ },
115
+ }),
116
+ ),
117
+ );
118
+ const customElements = customServers.map((s) =>
119
+ withKey(
120
+ s.id,
121
+ McpServerResource({
122
+ id: s.id,
123
+ kind: "custom",
124
+ name: s.name,
125
+ url: s.url,
126
+ auth: s.auth,
127
+ storage,
128
+ redirectUri,
129
+ autoConnect,
130
+ onRemove: async () => {
131
+ setCustomServers((prev) => prev.filter((x) => x.id !== s.id));
132
+ },
133
+ }),
134
+ ),
135
+ );
136
+ return [...connectorElements, ...customElements];
137
+ }, [connectors, customServers, storage, redirectUri, autoConnect]);
138
+
139
+ const lookup = tapClientLookup(() => serverElements, [serverElements]);
140
+
141
+ const state = tapMemo<MCPManagerState>(() => {
142
+ const all = lookup.state;
143
+ return {
144
+ servers: all,
145
+ connectors: all.filter((s) => s.kind === "connector"),
146
+ customServers: all.filter((s) => s.kind === "custom"),
147
+ isHydrated,
148
+ };
149
+ }, [lookup.state, isHydrated]);
150
+
151
+ // ─── Auto-register MCP tools as frontend tools in modelContext ─────
152
+ // Build the toolkit from connected servers; re-register when the visible
153
+ // tool surface changes. Tool names are prefixed with the server id to
154
+ // avoid collisions across connected servers — connector ids must not
155
+ // contain `__` (enforced by `defineConnector`); `addCustomServer`
156
+ // generates UUIDs that satisfy the constraint by construction.
157
+ const toolkit = tapMemo<Record<string, Tool<any, any>>>(() => {
158
+ const out: Record<string, Tool<any, any>> = {};
159
+ for (const server of state.servers) {
160
+ if (server.connectionState !== "connected") continue;
161
+ for (const tool of server.tools) {
162
+ const fullName = `${server.id}__${tool.name}`;
163
+ out[fullName] = {
164
+ type: "frontend",
165
+ ...(tool.description !== undefined
166
+ ? { description: tool.description }
167
+ : {}),
168
+ parameters: tool.inputSchema as never,
169
+ execute: (args) =>
170
+ lookup
171
+ .get({ key: server.id })
172
+ .callTool(tool.name, args as unknown),
173
+ };
174
+ }
175
+ }
176
+ return out;
177
+ }, [state, lookup]);
178
+
179
+ const clientRef = tapAssistantClientRef();
180
+
181
+ tapEffect(() => {
182
+ const client = clientRef.current;
183
+ if (!client) return;
184
+ return client.modelContext().register({
185
+ getModelContext: () => ({ tools: toolkit }),
186
+ });
187
+ }, [toolkit, clientRef]);
188
+
189
+ const serverByKind = (kind: "connector" | "custom", index: number) => {
190
+ const list =
191
+ kind === "connector" ? state.connectors : state.customServers;
192
+ const entry = list[index];
193
+ if (!entry) {
194
+ throw new Error(
195
+ `McpManagerResource: no ${kind} at index ${index} (length ${list.length})`,
196
+ );
197
+ }
198
+ return lookup.get({ key: entry.id });
199
+ };
200
+
201
+ return {
202
+ getState: () => state,
203
+ server: (query) => {
204
+ if ("id" in query) return lookup.get({ key: query.id });
205
+ return serverByKind(query.kind, query.index);
206
+ },
207
+ connector: ({ index }) => serverByKind("connector", index),
208
+ customServer: ({ index }) => serverByKind("custom", index),
209
+ addCustomServer: async ({ name, url, auth }) => {
210
+ const record: MCPCustomServerRecord = {
211
+ id:
212
+ typeof crypto !== "undefined" && "randomUUID" in crypto
213
+ ? crypto.randomUUID()
214
+ : `mcp-${Date.now()}-${Math.random().toString(36).slice(2)}`,
215
+ name,
216
+ url,
217
+ auth: auth as MCPAuthConfig,
218
+ createdAt: Date.now(),
219
+ };
220
+ setCustomServers((prev) => [...prev, record]);
221
+ return record.id;
222
+ },
223
+ removeServer: async (id) => {
224
+ // removeServer is custom-server only — connectors are app-defined
225
+ // and not user-removable. Refuse rather than silently no-op.
226
+ if (state.connectors.some((c) => c.id === id)) {
227
+ throw new Error(
228
+ `Cannot remove connector "${id}" — connectors are app-defined and not removable. Use a custom server id instead.`,
229
+ );
230
+ }
231
+ // Delegate to McpServerResource.remove() which disconnects,
232
+ // clears auth state, and unregisters from customServers in one
233
+ // place. Fallback to manual cleanup if the lookup is empty
234
+ // (server already gone).
235
+ try {
236
+ await lookup.get({ key: id }).remove();
237
+ } catch {
238
+ await storage.clearAuthState(id);
239
+ setCustomServers((prev) => prev.filter((s) => s.id !== id));
240
+ }
241
+ },
242
+ };
243
+ },
244
+ );
245
+
246
+ // Ensure modelContext exists as a sibling when the manager mounts. If an
247
+ // ancestor (e.g. a chat runtime) already provides modelContext, this is a
248
+ // no-op; otherwise it's auto-mounted alongside `mcp`.
249
+ attachTransformScopes(McpManagerResource, (scopes, parent) => {
250
+ if (!scopes.modelContext && parent.modelContext.source === null) {
251
+ scopes.modelContext = ModelContext();
252
+ }
253
+ });