@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.
- package/dist/auth/createOAuthProvider.d.ts +13 -2
- package/dist/auth/createOAuthProvider.d.ts.map +1 -1
- package/dist/auth/createOAuthProvider.js +109 -31
- package/dist/auth/createOAuthProvider.js.map +1 -1
- package/dist/auth/types.d.ts +5 -1
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/primitives/addForm/McpAddFormAuthFields.js +71 -28
- package/dist/primitives/addForm/McpAddFormAuthFields.js.map +1 -1
- package/dist/primitives/addForm/McpAddFormError.d.ts.map +1 -1
- package/dist/primitives/addForm/McpAddFormError.js +30 -10
- package/dist/primitives/addForm/McpAddFormError.js.map +1 -1
- package/dist/primitives/addForm/McpAddFormNameField.js +31 -18
- package/dist/primitives/addForm/McpAddFormNameField.js.map +1 -1
- package/dist/primitives/addForm/McpAddFormRoot.d.ts.map +1 -1
- package/dist/primitives/addForm/McpAddFormRoot.js +32 -11
- package/dist/primitives/addForm/McpAddFormRoot.js.map +1 -1
- package/dist/primitives/addForm/McpAddFormUrlField.js +31 -18
- package/dist/primitives/addForm/McpAddFormUrlField.js.map +1 -1
- package/dist/primitives/addForm/context.d.ts +9 -1
- package/dist/primitives/addForm/context.d.ts.map +1 -1
- package/dist/primitives/addForm/context.js.map +1 -1
- package/dist/resources/McpManagerResource.d.ts.map +1 -1
- package/dist/resources/McpManagerResource.js +330 -247
- package/dist/resources/McpManagerResource.js.map +1 -1
- package/dist/resources/McpServerRemovalFence.d.ts +7 -0
- package/dist/resources/McpServerRemovalFence.d.ts.map +1 -0
- package/dist/resources/McpServerRemovalFence.js +11 -0
- package/dist/resources/McpServerRemovalFence.js.map +1 -0
- package/dist/resources/McpServerResource.d.ts.map +1 -1
- package/dist/resources/McpServerResource.js +41 -16
- package/dist/resources/McpServerResource.js.map +1 -1
- package/dist/resources/storage/McpLocalStorage.d.ts.map +1 -1
- package/dist/resources/storage/McpLocalStorage.js +16 -1
- package/dist/resources/storage/McpLocalStorage.js.map +1 -1
- package/dist/resources/storage/types.d.ts +10 -8
- package/dist/resources/storage/types.d.ts.map +1 -1
- package/dist/utils/serverUrl.d.ts +8 -0
- package/dist/utils/serverUrl.d.ts.map +1 -0
- package/dist/utils/serverUrl.js +15 -0
- package/dist/utils/serverUrl.js.map +1 -0
- package/package.json +7 -7
- package/src/auth/createOAuthProvider.test.ts +544 -35
- package/src/auth/createOAuthProvider.ts +201 -46
- package/src/auth/types.ts +5 -1
- package/src/primitives/addForm/McpAddFormAccessibility.test.tsx +261 -0
- package/src/primitives/addForm/McpAddFormAuthFields.tsx +33 -15
- package/src/primitives/addForm/McpAddFormError.tsx +17 -3
- package/src/primitives/addForm/McpAddFormNameField.tsx +13 -1
- package/src/primitives/addForm/McpAddFormRoot.tsx +51 -8
- package/src/primitives/addForm/McpAddFormUrlField.tsx +12 -1
- package/src/primitives/addForm/context.tsx +10 -0
- package/src/resources/McpManagerResource.test.ts +712 -9
- package/src/resources/McpManagerResource.ts +208 -56
- package/src/resources/McpServerRemovalFence.ts +21 -0
- package/src/resources/McpServerResource.test.ts +252 -17
- package/src/resources/McpServerResource.ts +40 -15
- package/src/resources/storage/McpLocalStorage.test.ts +28 -0
- package/src/resources/storage/McpLocalStorage.ts +23 -1
- package/src/resources/storage/types.ts +10 -8
- package/src/utils/serverUrl.test.ts +66 -0
- package/src/utils/serverUrl.ts +23 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpManagerResource.js","names":["useState","useEffect","useMemo","useEffectEvent","useRef","useResource","resource","withKey","useClientLookup","useAssistantClientRef","attachTransformScopes","ClientOutput","useAssistantScopeEffect","ModelContext","createMcpId","clearOAuthProviderAuthState","Tool","McpServerResource","McpLocalStorage","MCPStorage","MCPStorageElement","assertUniqueServerIds","MCPAuthConfig","MCPConnector","MCPCustomServerRecord","MCPManagerState","McpManagerResourceProps","connectors","storage","oauthRedirectUri","autoConnect","connectionTimeout","defaultRedirectUri","window","location","origin","NO_CONNECTORS","reportCustomStorageFailure","operation","error","console","persistCustomServers","records","saveCustomServers","useMcpManagerResource","props","$","_c","t0","redirectUri","t1","storageElement","t2","Symbol","for","customServers","setCustomServers","isHydrated","setIsHydrated","hydratedRef","storageRef","t3","Promise","resolve","persistenceQueueRef","t4","t5","current","t6","signal","markHydrated","cancelled","loadCustomServers","t7","prev","length","persistedIds","Set","map","_temp","filter","r_0","has","r","id","hydrate","signal_0","t8","t10","t9","targetStorage","then","_temp2","_temp3","t11","t12","c_0","c","kind","name","url","icon","auth","cache","undefined","elicitation","onRemove","_temp4","connectorElements","t13","s_0","s","prev_0","x","customElements","serverElements","lookup","all","state","_temp5","_temp6","t14","servers","out","server","connectionState","tool","tools","fullName","type","description","parameters","inputSchema","execute","args","get","key","callTool","toolkit","clientRef","t15","client","modelContext","register","getModelContext","t16","t17","index","list","entry","Error","serverByKind","t18","t19","query","t20","t21","t22","index_0","t23","index_1","connectionTimeout_0","record","createdAt","Date","now","prev_1","some","c_1","remove","prev_2","s_3","t24","getState","connector","customServer","addCustomServer","removeServer","McpManagerResource","scopes","parent","source","s_1","s_2"],"sources":["../../src/resources/McpManagerResource.ts"],"sourcesContent":["import { useState, useEffect, useMemo, useEffectEvent, useRef } from \"react\";\nimport { useResource, resource, withKey } from \"@assistant-ui/tap\";\nimport {\n useClientLookup,\n useAssistantClientRef,\n attachTransformScopes,\n type ClientOutput,\n} from \"@assistant-ui/store\";\nimport { useAssistantScopeEffect } from \"@assistant-ui/store/client\";\nimport { ModelContext } from \"@assistant-ui/core/store\";\nimport { createMcpId } from \"../utils/createMcpId\";\nimport { clearOAuthProviderAuthState } from \"../auth/createOAuthProvider\";\nimport type { Tool } from \"assistant-stream\";\nimport { McpServerResource } from \"./McpServerResource\";\nimport { McpLocalStorage } from \"./storage/McpLocalStorage\";\nimport type { MCPStorage, MCPStorageElement } from \"./storage/types\";\nimport { assertUniqueServerIds } from \"../utils/serverId\";\nimport type {\n MCPAuthConfig,\n MCPConnector,\n MCPCustomServerRecord,\n MCPManagerState,\n} from \"../mcp-scope\";\n\nexport type McpManagerResourceProps = {\n connectors?: MCPConnector[] | undefined;\n storage?: MCPStorageElement | undefined;\n /** OAuth redirect target. Defaults to `${origin}/mcp/callback`. */\n oauthRedirectUri?: string | undefined;\n /** Connect on mount when usable auth exists. Default true. */\n autoConnect?: boolean | undefined;\n /** Optional timeout in milliseconds for connect/listTools calls. Disabled by default. */\n connectionTimeout?: number | undefined;\n};\n\nfunction defaultRedirectUri(): string {\n if (typeof window === \"undefined\") return \"\";\n return `${window.location.origin}/mcp/callback`;\n}\n\n// Stable empty fallback so an absent `connectors` prop doesn't produce a fresh\n// array each render (which would invalidate the serverElements memo below).\nconst NO_CONNECTORS: MCPConnector[] = [];\n\nconst reportCustomStorageFailure = (\n operation: \"load\" | \"save\",\n error: unknown,\n) => {\n console.error(\n `[assistant-ui/react-mcp] failed to ${operation} custom servers:`,\n error,\n );\n};\n\nconst persistCustomServers = async (\n storage: MCPStorage,\n records: MCPCustomServerRecord[],\n) => {\n try {\n await storage.saveCustomServers(records);\n } catch (error) {\n reportCustomStorageFailure(\"save\", error);\n }\n};\n\nconst useMcpManagerResource = (\n props: McpManagerResourceProps,\n): ClientOutput<\"mcp\"> => {\n const connectors = props.connectors ?? NO_CONNECTORS;\n const autoConnect = props.autoConnect ?? true;\n const redirectUri = props.oauthRedirectUri ?? defaultRedirectUri();\n const connectionTimeout = props.connectionTimeout;\n\n const storageElement = props.storage ?? McpLocalStorage();\n const storage = useResource(storageElement);\n\n const [customServers, setCustomServers] = useState<MCPCustomServerRecord[]>(\n [],\n );\n const [isHydrated, setIsHydrated] = useState(false);\n\n const hydratedRef = useRef(false);\n const storageRef = useRef(storage);\n const persistenceQueueRef = useRef(Promise.resolve());\n\n useEffect(() => {\n storageRef.current = storage;\n }, [storage]);\n\n const hydrate = useEffectEvent(async (signal: { cancelled: boolean }) => {\n const markHydrated = () => {\n if (!signal.cancelled) {\n hydratedRef.current = true;\n setIsHydrated(true);\n }\n };\n\n let records: Awaited<ReturnType<typeof storage.loadCustomServers>>;\n try {\n records = await storage.loadCustomServers();\n } catch (error) {\n if (!signal.cancelled) {\n reportCustomStorageFailure(\"load\", error);\n }\n markHydrated();\n return;\n }\n\n if (signal.cancelled) return;\n // Merge rather than replace so any addCustomServer calls that\n // happened before hydration resolved aren't silently overwritten.\n // Persisted order wins; pre-hydration locals append.\n setCustomServers((prev) => {\n if (prev.length === 0) return records;\n const persistedIds = new Set(records.map((r) => r.id));\n return [...records, ...prev.filter((r) => !persistedIds.has(r.id))];\n });\n markHydrated();\n });\n\n useEffect(() => {\n const signal = { cancelled: false };\n void hydrate(signal);\n return () => {\n signal.cancelled = true;\n };\n }, []);\n\n useEffect(() => {\n if (!hydratedRef.current) return;\n const targetStorage = storageRef.current;\n persistenceQueueRef.current = persistenceQueueRef.current.then(() =>\n persistCustomServers(targetStorage, customServers),\n );\n }, [customServers]);\n\n const serverElements = useMemo(() => {\n assertUniqueServerIds([\n ...connectors.map((c) => c.id),\n ...customServers.map((s) => s.id),\n ]);\n\n const connectorElements = connectors.map((c) =>\n withKey(\n c.id,\n McpServerResource({\n id: c.id,\n kind: \"connector\",\n name: c.name,\n url: c.url,\n icon: c.icon,\n auth: c.auth,\n storage,\n redirectUri,\n autoConnect,\n connectionTimeout: c.connectionTimeout ?? connectionTimeout,\n ...(c.cache !== undefined ? { cache: c.cache } : {}),\n ...(c.elicitation !== undefined\n ? { elicitation: c.elicitation }\n : {}),\n onRemove: async () => {\n // connectors cannot be removed\n },\n }),\n ),\n );\n const customElements = customServers.map((s) =>\n withKey(\n s.id,\n McpServerResource({\n id: s.id,\n kind: \"custom\",\n name: s.name,\n url: s.url,\n auth: s.auth,\n storage,\n redirectUri,\n autoConnect,\n connectionTimeout: s.connectionTimeout ?? connectionTimeout,\n ...(s.cache !== undefined ? { cache: s.cache } : {}),\n ...(s.elicitation !== undefined\n ? { elicitation: s.elicitation }\n : {}),\n onRemove: async () => {\n setCustomServers((prev) => prev.filter((x) => x.id !== s.id));\n },\n }),\n ),\n );\n return [...connectorElements, ...customElements];\n }, [\n connectors,\n customServers,\n storage,\n redirectUri,\n autoConnect,\n connectionTimeout,\n ]);\n\n const lookup = useClientLookup(serverElements);\n\n const state = useMemo<MCPManagerState>(() => {\n const all = lookup.state;\n return {\n servers: all,\n connectors: all.filter((s) => s.kind === \"connector\"),\n customServers: all.filter((s) => s.kind === \"custom\"),\n isHydrated,\n };\n }, [lookup.state, isHydrated]);\n\n // ─── Auto-register MCP tools as frontend tools in modelContext ─────\n // Build the toolkit from connected servers; re-register when the visible\n // tool surface changes. Tool names are prefixed with the server id to\n // avoid collisions across connected servers — connector ids must not\n // contain `__` (enforced by `defineConnector`); `addCustomServer`\n // generates UUIDs that satisfy the constraint by construction.\n const toolkit = useMemo<Record<string, Tool<any, any>>>(() => {\n const out: Record<string, Tool<any, any>> = {};\n for (const server of state.servers) {\n if (server.connectionState !== \"connected\") continue;\n for (const tool of server.tools) {\n const fullName = `${server.id}__${tool.name}`;\n out[fullName] = {\n type: \"frontend\",\n ...(tool.description !== undefined\n ? { description: tool.description }\n : {}),\n parameters: tool.inputSchema as never,\n execute: (args) =>\n lookup.get({ key: server.id }).callTool(tool.name, args as unknown),\n };\n }\n }\n return out;\n }, [state, lookup]);\n\n const clientRef = useAssistantClientRef();\n\n useAssistantScopeEffect(\n \"modelContext\",\n () => {\n const client = clientRef.current;\n if (!client) return;\n return client.modelContext.register({\n getModelContext: () => ({ tools: toolkit }),\n });\n },\n [toolkit],\n );\n\n const serverByKind = (kind: \"connector\" | \"custom\", index: number) => {\n const list = kind === \"connector\" ? state.connectors : state.customServers;\n const entry = list[index];\n if (!entry) {\n throw new Error(\n `McpManagerResource: no ${kind} at index ${index} (length ${list.length})`,\n );\n }\n return lookup.get({ key: entry.id });\n };\n\n return {\n getState: () => state,\n server: (query) => {\n if (\"id\" in query) return lookup.get({ key: query.id });\n return serverByKind(query.kind, query.index);\n },\n connector: ({ index }) => serverByKind(\"connector\", index),\n customServer: ({ index }) => serverByKind(\"custom\", index),\n addCustomServer: async ({\n name,\n url,\n auth,\n connectionTimeout,\n cache,\n elicitation,\n }) => {\n const record: MCPCustomServerRecord = {\n id: createMcpId(),\n name,\n url,\n auth: auth as MCPAuthConfig,\n connectionTimeout,\n ...(cache !== undefined ? { cache } : {}),\n ...(elicitation !== undefined ? { elicitation } : {}),\n createdAt: Date.now(),\n };\n setCustomServers((prev) => [...prev, record]);\n return record.id;\n },\n removeServer: async (id) => {\n // removeServer is custom-server only — connectors are app-defined\n // and not user-removable. Refuse rather than silently no-op.\n if (state.connectors.some((c) => c.id === id)) {\n throw new Error(\n `Cannot remove connector \"${id}\" — connectors are app-defined and not removable. Use a custom server id instead.`,\n );\n }\n // Delegate to McpServerResource.remove() which disconnects,\n // clears auth state, and unregisters from customServers in one\n // place. Fallback to manual cleanup if the lookup is empty\n // (server already gone).\n try {\n await lookup.get({ key: id }).remove();\n } catch {\n await clearOAuthProviderAuthState(storage, id);\n setCustomServers((prev) => prev.filter((s) => s.id !== id));\n }\n },\n };\n};\n\nexport const McpManagerResource = resource(useMcpManagerResource);\n\n// Ensure modelContext exists as a sibling when the manager mounts. If an\n// ancestor (e.g. a chat runtime) already provides modelContext, this is a\n// no-op; otherwise it's auto-mounted alongside `mcp`.\nattachTransformScopes(useMcpManagerResource, (scopes, parent) => {\n if (!scopes.modelContext && parent.modelContext.source === null) {\n scopes.modelContext = ModelContext();\n }\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"McpManagerResource.js","names":["useState","useEffect","useMemo","useEffectEvent","useRef","useCallback","useResource","resource","withKey","useClientLookup","useAssistantClientRef","attachTransformScopes","ClientOutput","useAssistantScopeEffect","ModelContext","createMcpId","clearOAuthProviderAuthState","Tool","McpServerResource","withMcpServerRemovalFence","McpLocalStorage","MCPStorage","MCPStorageElement","assertUniqueServerIds","MCPAuthConfig","MCPConnector","MCPCustomServerRecord","MCPManagerState","McpManagerResourceProps","connectors","storage","oauthRedirectUri","autoConnect","connectionTimeout","defaultRedirectUri","window","location","origin","NO_CONNECTORS","reportCustomStorageFailure","operation","error","console","reportBlockedCustomServerPersistence","persistCustomServers","records","saveCustomServers","CustomServerPersistenceQueues","Map","Promise","enqueueCustomServerTask","persistenceQueues","scopeKey","task","previous","get","next","resolve","then","set","delete","enqueueCustomServerPersistence","holdCustomServerPersistence","release","gate","McpCustomServersResourceProps","useMcpCustomServersResource","t0","$","_c","t1","Symbol","for","customServers","setCustomServers","isHydrated","setIsHydrated","t2","customServersRef","hydrationStateRef","hasPendingMutationRef","reportedBlockedPersistenceRef","t3","signal","pendingPersistence","cancelled","loadCustomServers","t4","current","hadPendingMutation","mergedRecords","prev","length","persistedIds","Set","map","_temp","filter","r_0","has","r","id","hydrate","signal_0","t5","t6","updater","updateCustomServers","t7","McpCustomServersResource","useMcpManagerResource","props","redirectUri","storageElement","_temp2","storageScopeKey","scopeId","undefined","_temp3","_temp4","c_0","c","kind","name","url","icon","auth","cache","elicitation","onRemove","_temp5","connectorElements","s_0","s","record","customElements","serverElements","lookup","all","state","_temp6","_temp7","servers","out","server","connectionState","tool","tools","fullName","type","description","parameters","inputSchema","execute","args","key","callTool","toolkit","clientRef","client","modelContext","register","getModelContext","t8","t9","index","list","entry","Error","serverByKind","t10","t11","query","t12","t13","t14","index_0","t15","index_1","connectionTimeout_0","record_0","createdAt","Date","now","prev_0","some","c_1","remove","releasePersistence","prev_1","record_1","t16","getState","connector","customServer","addCustomServer","removeServer","McpManagerResource","scopes","parent","source","s_1","s_2"],"sources":["../../src/resources/McpManagerResource.ts"],"sourcesContent":["import {\n useState,\n useEffect,\n useMemo,\n useEffectEvent,\n useRef,\n useCallback,\n} from \"react\";\nimport { useResource, resource, withKey } from \"@assistant-ui/tap\";\nimport {\n useClientLookup,\n useAssistantClientRef,\n attachTransformScopes,\n type ClientOutput,\n} from \"@assistant-ui/store\";\nimport { useAssistantScopeEffect } from \"@assistant-ui/store/client\";\nimport { ModelContext } from \"@assistant-ui/core/store\";\nimport { createMcpId } from \"../utils/createMcpId\";\nimport { clearOAuthProviderAuthState } from \"../auth/createOAuthProvider\";\nimport type { Tool } from \"assistant-stream\";\nimport { McpServerResource } from \"./McpServerResource\";\nimport { withMcpServerRemovalFence } from \"./McpServerRemovalFence\";\nimport { McpLocalStorage } from \"./storage/McpLocalStorage\";\nimport type { MCPStorage, MCPStorageElement } from \"./storage/types\";\nimport { assertUniqueServerIds } from \"../utils/serverId\";\nimport type {\n MCPAuthConfig,\n MCPConnector,\n MCPCustomServerRecord,\n MCPManagerState,\n} from \"../mcp-scope\";\n\nexport type McpManagerResourceProps = {\n connectors?: MCPConnector[] | undefined;\n storage?: MCPStorageElement | undefined;\n /** OAuth redirect target. Defaults to `${origin}/mcp/callback`. */\n oauthRedirectUri?: string | undefined;\n /** Connect on mount when usable auth exists. Default true. */\n autoConnect?: boolean | undefined;\n /** Optional timeout in milliseconds for connect/listTools calls. Disabled by default. */\n connectionTimeout?: number | undefined;\n};\n\nfunction defaultRedirectUri(): string {\n if (typeof window === \"undefined\") return \"\";\n return `${window.location.origin}/mcp/callback`;\n}\n\n// Stable empty fallback so an absent `connectors` prop doesn't produce a fresh\n// array each render (which would invalidate the serverElements memo below).\nconst NO_CONNECTORS: MCPConnector[] = [];\n\nconst reportCustomStorageFailure = (\n operation: \"load\" | \"save\",\n error: unknown,\n) => {\n console.error(\n `[assistant-ui/react-mcp] failed to ${operation} custom servers:`,\n error,\n );\n};\n\nconst reportBlockedCustomServerPersistence = () => {\n console.error(\n \"[assistant-ui/react-mcp] custom server changes remain in memory because loading the persisted list failed; remount the manager to retry\",\n );\n};\n\nconst persistCustomServers = async (\n storage: MCPStorage,\n records: MCPCustomServerRecord[],\n) => {\n try {\n await storage.saveCustomServers(records);\n } catch (error) {\n reportCustomStorageFailure(\"save\", error);\n }\n};\n\ntype CustomServerPersistenceQueues = Map<string, Promise<void>>;\n\nconst enqueueCustomServerTask = (\n persistenceQueues: CustomServerPersistenceQueues,\n scopeKey: string,\n task: () => Promise<void>,\n) => {\n const previous = persistenceQueues.get(scopeKey);\n const next = (previous ?? Promise.resolve()).then(task);\n persistenceQueues.set(scopeKey, next);\n void next.then(() => {\n if (persistenceQueues.get(scopeKey) === next) {\n persistenceQueues.delete(scopeKey);\n }\n });\n};\n\nconst enqueueCustomServerPersistence = (\n persistenceQueues: CustomServerPersistenceQueues,\n scopeKey: string,\n storage: MCPStorage,\n records: MCPCustomServerRecord[],\n) =>\n enqueueCustomServerTask(persistenceQueues, scopeKey, () =>\n persistCustomServers(storage, records),\n );\n\nconst holdCustomServerPersistence = (\n persistenceQueues: CustomServerPersistenceQueues,\n scopeKey: string,\n) => {\n let release = () => {};\n const gate = new Promise<void>((resolve) => {\n release = resolve;\n });\n enqueueCustomServerTask(persistenceQueues, scopeKey, () => gate);\n return release;\n};\n\ntype McpCustomServersResourceProps = {\n storage: MCPStorage;\n scopeKey: string;\n persistenceQueues: CustomServerPersistenceQueues;\n};\n\nconst useMcpCustomServersResource = ({\n storage,\n scopeKey,\n persistenceQueues,\n}: McpCustomServersResourceProps) => {\n const [customServers, setCustomServers] = useState<MCPCustomServerRecord[]>(\n [],\n );\n const [isHydrated, setIsHydrated] = useState(false);\n\n const customServersRef = useRef<MCPCustomServerRecord[]>([]);\n const hydrationStateRef = useRef<\"pending\" | \"succeeded\" | \"failed\">(\n \"pending\",\n );\n const hasPendingMutationRef = useRef(false);\n const reportedBlockedPersistenceRef = useRef(false);\n\n const hydrate = useEffectEvent(async (signal: { cancelled: boolean }) => {\n // A revisited scope must not read behind writes still queued against it.\n while (true) {\n const pendingPersistence = persistenceQueues.get(scopeKey);\n if (!pendingPersistence) break;\n await pendingPersistence;\n if (signal.cancelled) return;\n if (persistenceQueues.get(scopeKey) === pendingPersistence) break;\n }\n\n let records: Awaited<ReturnType<typeof storage.loadCustomServers>>;\n try {\n records = await storage.loadCustomServers();\n } catch (error) {\n if (!signal.cancelled) {\n reportCustomStorageFailure(\"load\", error);\n hydrationStateRef.current = \"failed\";\n if (hasPendingMutationRef.current) {\n reportBlockedCustomServerPersistence();\n reportedBlockedPersistenceRef.current = true;\n }\n setIsHydrated(true);\n }\n return;\n }\n\n // Merge rather than replace so any addCustomServer calls that\n // happened before hydration resolved aren't silently overwritten.\n // Persisted order wins; pre-hydration locals append.\n const hadPendingMutation = hasPendingMutationRef.current;\n const mergedRecords = (() => {\n const prev = customServersRef.current;\n if (prev.length === 0) return records;\n const persistedIds = new Set(records.map((r) => r.id));\n return [...records, ...prev.filter((r) => !persistedIds.has(r.id))];\n })();\n customServersRef.current = mergedRecords;\n hydrationStateRef.current = \"succeeded\";\n hasPendingMutationRef.current = false;\n if (hadPendingMutation) {\n enqueueCustomServerPersistence(\n persistenceQueues,\n scopeKey,\n storage,\n mergedRecords,\n );\n }\n if (signal.cancelled) return;\n setCustomServers(mergedRecords);\n setIsHydrated(true);\n });\n\n useEffect(() => {\n const signal = { cancelled: false };\n // Hydration reads persisted records asynchronously; there is no earlier\n // point than mount at which to start it.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n void hydrate(signal);\n return () => {\n signal.cancelled = true;\n };\n }, []);\n\n const updateCustomServers = useCallback(\n (\n updater: (records: MCPCustomServerRecord[]) => MCPCustomServerRecord[],\n ) => {\n const next = updater(customServersRef.current);\n customServersRef.current = next;\n setCustomServers(next);\n\n if (hydrationStateRef.current === \"succeeded\") {\n enqueueCustomServerPersistence(\n persistenceQueues,\n scopeKey,\n storage,\n next,\n );\n return;\n }\n\n hasPendingMutationRef.current = true;\n if (\n hydrationStateRef.current === \"failed\" &&\n !reportedBlockedPersistenceRef.current\n ) {\n reportBlockedCustomServerPersistence();\n reportedBlockedPersistenceRef.current = true;\n }\n },\n [persistenceQueues, scopeKey, storage],\n );\n\n return { customServers, isHydrated, updateCustomServers };\n};\n\nconst McpCustomServersResource = resource(useMcpCustomServersResource);\n\nconst useMcpManagerResource = (\n props: McpManagerResourceProps,\n): ClientOutput<\"mcp\"> => {\n const connectors = props.connectors ?? NO_CONNECTORS;\n const autoConnect = props.autoConnect ?? true;\n const redirectUri = props.oauthRedirectUri ?? defaultRedirectUri();\n const connectionTimeout = props.connectionTimeout;\n\n const storageElement = props.storage ?? McpLocalStorage();\n const storage = useResource(storageElement);\n const [persistenceQueues] = useState<CustomServerPersistenceQueues>(\n () => new Map(),\n );\n const storageScopeKey =\n storage.scopeId === undefined ? \"unscoped\" : `scoped:${storage.scopeId}`;\n const { customServers, isHydrated, updateCustomServers } = useResource(\n withKey(\n storageScopeKey,\n McpCustomServersResource({\n storage,\n scopeKey: storageScopeKey,\n persistenceQueues,\n }),\n ),\n );\n\n const serverElements = useMemo(() => {\n assertUniqueServerIds([\n ...connectors.map((c) => c.id),\n ...customServers.map((s) => s.id),\n ]);\n\n const connectorElements = connectors.map((c) =>\n withKey(\n c.id,\n McpServerResource({\n id: c.id,\n kind: \"connector\",\n name: c.name,\n url: c.url,\n icon: c.icon,\n auth: c.auth,\n storage,\n redirectUri,\n autoConnect,\n connectionTimeout: c.connectionTimeout ?? connectionTimeout,\n ...(c.cache !== undefined ? { cache: c.cache } : {}),\n ...(c.elicitation !== undefined\n ? { elicitation: c.elicitation }\n : {}),\n onRemove: async () => {\n // connectors cannot be removed\n },\n }),\n ),\n );\n const customElements = customServers.map((s) =>\n withKey(\n s.id,\n McpServerResource(\n withMcpServerRemovalFence(\n {\n id: s.id,\n kind: \"custom\",\n name: s.name,\n url: s.url,\n auth: s.auth,\n storage,\n redirectUri,\n autoConnect,\n connectionTimeout: s.connectionTimeout ?? connectionTimeout,\n ...(s.cache !== undefined ? { cache: s.cache } : {}),\n ...(s.elicitation !== undefined\n ? { elicitation: s.elicitation }\n : {}),\n onRemove: async () => {\n updateCustomServers((prev) =>\n prev.filter((record) => record.id !== s.id),\n );\n },\n },\n () =>\n holdCustomServerPersistence(persistenceQueues, storageScopeKey),\n ),\n ),\n ),\n );\n return [...connectorElements, ...customElements];\n }, [\n connectors,\n customServers,\n storage,\n redirectUri,\n autoConnect,\n connectionTimeout,\n updateCustomServers,\n persistenceQueues,\n storageScopeKey,\n ]);\n\n const lookup = useClientLookup(serverElements);\n\n const state = useMemo<MCPManagerState>(() => {\n const all = lookup.state;\n return {\n servers: all,\n connectors: all.filter((s) => s.kind === \"connector\"),\n customServers: all.filter((s) => s.kind === \"custom\"),\n isHydrated,\n };\n }, [lookup.state, isHydrated]);\n\n // ─── Auto-register MCP tools as frontend tools in modelContext ─────\n // Build the toolkit from connected servers; re-register when the visible\n // tool surface changes. Tool names are prefixed with the server id to\n // avoid collisions across connected servers — connector ids must not\n // contain `__` (enforced by `defineConnector`); `addCustomServer`\n // generates UUIDs that satisfy the constraint by construction.\n const toolkit = useMemo<Record<string, Tool<any, any>>>(() => {\n const out: Record<string, Tool<any, any>> = {};\n for (const server of state.servers) {\n if (server.connectionState !== \"connected\") continue;\n for (const tool of server.tools) {\n const fullName = `${server.id}__${tool.name}`;\n out[fullName] = {\n type: \"frontend\",\n ...(tool.description !== undefined\n ? { description: tool.description }\n : {}),\n parameters: tool.inputSchema as never,\n execute: (args) =>\n lookup.get({ key: server.id }).callTool(tool.name, args as unknown),\n };\n }\n }\n return out;\n }, [state, lookup]);\n\n const clientRef = useAssistantClientRef();\n\n useAssistantScopeEffect(\n \"modelContext\",\n () => {\n const client = clientRef.current;\n if (!client) return;\n return client.modelContext.register({\n getModelContext: () => ({ tools: toolkit }),\n });\n },\n [toolkit],\n );\n\n const serverByKind = (kind: \"connector\" | \"custom\", index: number) => {\n const list = kind === \"connector\" ? state.connectors : state.customServers;\n const entry = list[index];\n if (!entry) {\n throw new Error(\n `McpManagerResource: no ${kind} at index ${index} (length ${list.length})`,\n );\n }\n return lookup.get({ key: entry.id });\n };\n\n return {\n getState: () => state,\n server: (query) => {\n if (\"id\" in query) return lookup.get({ key: query.id });\n return serverByKind(query.kind, query.index);\n },\n connector: ({ index }) => serverByKind(\"connector\", index),\n customServer: ({ index }) => serverByKind(\"custom\", index),\n addCustomServer: async ({\n name,\n url,\n auth,\n connectionTimeout,\n cache,\n elicitation,\n }) => {\n const record: MCPCustomServerRecord = {\n id: createMcpId(),\n name,\n url,\n auth: auth as MCPAuthConfig,\n connectionTimeout,\n ...(cache !== undefined ? { cache } : {}),\n ...(elicitation !== undefined ? { elicitation } : {}),\n createdAt: Date.now(),\n };\n updateCustomServers((prev) => [...prev, record]);\n return record.id;\n },\n removeServer: async (id) => {\n // removeServer is custom-server only — connectors are app-defined\n // and not user-removable. Refuse rather than silently no-op.\n if (state.connectors.some((c) => c.id === id)) {\n throw new Error(\n `Cannot remove connector \"${id}\" — connectors are app-defined and not removable. Use a custom server id instead.`,\n );\n }\n // Delegate to McpServerResource.remove() which disconnects,\n // clears auth state, and unregisters from customServers in one\n // place. Fallback to manual cleanup if the lookup is empty\n // (server already gone).\n try {\n await lookup.get({ key: id }).remove();\n } catch {\n const releasePersistence = holdCustomServerPersistence(\n persistenceQueues,\n storageScopeKey,\n );\n try {\n await clearOAuthProviderAuthState(storage, id);\n updateCustomServers((prev) =>\n prev.filter((record) => record.id !== id),\n );\n } catch (error) {\n releasePersistence();\n throw error;\n }\n releasePersistence();\n }\n },\n };\n};\n\nexport const McpManagerResource = resource(useMcpManagerResource);\n\n// Ensure modelContext exists as a sibling when the manager mounts. If an\n// ancestor (e.g. a chat runtime) already provides modelContext, this is a\n// no-op; otherwise it's auto-mounted alongside `mcp`.\nattachTransformScopes(useMcpManagerResource, (scopes, parent) => {\n if (!scopes.modelContext && parent.modelContext.source === null) {\n scopes.modelContext = ModelContext();\n }\n});\n"],"mappings":";;;;;;;;;;;;;AA2CA,SAASkC,qBAA6B;CACpC,IAAI,OAAOC,WAAW,aAAa,OAAO;CAC1C,OAAO,GAAGA,OAAOC,SAASC,OAAM;AAClC;AAIA,MAAMC,gBAAgC,CAAA;AAEtC,MAAMC,8BACJC,WACAC,UACG;CACHC,QAAQD,MACN,sCAAsCD,UAAS,mBAC/CC,KACF;AACF;AAEA,MAAME,6CAA6C;CACjDD,QAAQD,MACN,yIACF;AACF;AAEA,MAAMG,uBAAuB,OAC3Bd,SACAe,YACG;CACH,IAAI;EACF,MAAMf,QAAQgB,kBAAkBD,OAAO;CACzC,SAASJ,OAAO;EACdF,2BAA2B,QAAQE,KAAK;CAC1C;AACF;AAIA,MAAMS,2BACJC,mBACAC,UACAC,SACG;CAEH,MAAMG,QADWL,kBAAkBI,IAAIH,QACzBE,KAAYL,QAAQQ,QAAQ,EAAA,CAAGC,KAAKL,IAAI;CACtDF,kBAAkBQ,IAAIP,UAAUI,IAAI;CACpC,KAAUE,WAAW;EACnB,IAAIP,kBAAkBI,IAAIH,QAAQ,MAAMI,MACtCL,kBAAkBS,OAAOR,QAAQ;CAErC,CAAC;AACH;AAEA,MAAMS,kCACJV,mBACAC,UACAtB,SACAe,YAEAK,wBAAwBC,mBAAmBC,gBACzCR,qBAAqBd,SAASe,OAAO,CACvC;AAEF,MAAMiB,+BACJX,mBACAC,aACG;CACH,IAAIW,gBAAgB,CAAC;CACrB,MAAMC,OAAO,IAAIf,SAAeQ,YAAY;EAC1CM,UAAUN;CACZ,CAAC;CACDP,wBAAwBC,mBAAmBC,gBAAgBY,IAAI;CAC/D,OAAOD;AACT;AAQA,MAAMG,+BAA8BC,OAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAAC,MAAA,EAAAvC,SAAAsB,UAAAD,sBAAAgB;CAIL,IAAAG;CAAA,IAAAF,EAAA,OAAAG,OAAAC,IAAA,2BAAA,GAAA;EAE5BF,KAAA,CAAA;EAAEF,EAAA,KAAAE;CAAA,OAAAA,KAAAF,EAAA;CADJ,MAAA,CAAAK,eAAAC,oBAA0C1E,SACxCsE,EACF;CACA,MAAA,CAAAK,YAAAC,iBAAoC5E,SAAS,KAAK;CAAE,IAAA6E;CAAA,IAAAT,EAAA,OAAAG,OAAAC,IAAA,2BAAA,GAAA;EAEKK,KAAA,CAAA;EAAET,EAAA,KAAAS;CAAA,OAAAA,KAAAT,EAAA;CAA3D,MAAAU,mBAAyB1E,OAAgCyE,EAAE;CAC3D,MAAAE,oBAA0B3E,OACxB,SACF;CACA,MAAA4E,wBAA8B5E,OAAO,KAAK;CAC1C,MAAA6E,gCAAsC7E,OAAO,KAAK;CAAE,IAAA8E;CAAA,IAAAd,EAAA,OAAAjB,qBAAAiB,EAAA,OAAAhB,YAAAgB,EAAA,OAAAtC,SAAA;EAErBoD,KAAA,OAAAC,WAAA;GAE7B,OAAO,MAAI;IACT,MAAAC,qBAA2BjC,kBAAiBI,IAAKH,QAAQ;IACzD,IAAI,CAACgC,oBAAoB;IACzB,MAAMA;IACN,IAAID,OAAME,WAAU;IACpB,IAAIlC,kBAAiBI,IAAKH,QAAQ,MAAMgC,oBAAoB;GAAM;GAGhEvC,IAAAA;GACJ,IAAA;IACEA,UAAUA,MAAMf,QAAOwD,kBAAmB;GAAnC,SAAAC,IAAA;IACA9C,MAAAA,QAAAA;IACP,IAAI,CAAC0C,OAAME,WAAU;KACnB9C,2BAA2B,QAAQE,KAAK;KACxCsC,kBAAiBS,UAAW;KAC5B,IAAIR,sBAAqBQ,SAAQ;MAC/B7C,qCAAqC;MACrCsC,8BAA6BO,UAAW;KAAH;KAEvCZ,cAAc,IAAI;IAAC;IACpB;GAAA;GAOH,MAAAa,qBAA2BT,sBAAqBQ;GAChD,MAAAE,uBAAuB;IACrB,MAAAC,OAAab,iBAAgBU;IAC7B,IAAIG,KAAIC,WAAY,GAAC,OAAS/C;IAC9B,MAAAgD,eAAqB,IAAIC,IAAIjD,QAAOkD,IAAKC,KAAW,CAAC;IAAE,OAChD,CAAA,GAAInD,SAAO,GAAK8C,KAAIM,QAAQC,QAAO,CAACL,aAAYM,IAAKC,IAACC,EAAG,CAAC,CAAC;GAAC,EAAA,CAClE;GACHvB,iBAAgBU,UAAWE;GAC3BX,kBAAiBS,UAAW;GAC5BR,sBAAqBQ,UAAW;GAChC,IAAIC,oBACF5B,+BACEV,mBACAC,UACAtB,SACA4D,aACF;GAEF,IAAIP,OAAME,WAAU;GACpBX,iBAAiBgB,aAAa;GAC9Bd,cAAc,IAAI;EAAC;EACpBR,EAAA,KAAAjB;EAAAiB,EAAA,KAAAhB;EAAAgB,EAAA,KAAAtC;EAAAsC,EAAA,KAAAc;CAAA,OAAAA,KAAAd,EAAA;CAlDD,MAAAkC,UAAgBnG,eAAe+E,EAkD9B;CAAE,IAAAK;CAAA,IAAAnB,EAAA,OAAAkC,SAAA;EAEOf,WAAA;GACR,MAAAgB,WAAe,EAAAlB,WAAa,MAAM;GAI7BiB,QAAQnB,QAAM;GAAC,aACb;IACLA,SAAME,YAAa;GAAH;EACjB;EACFjB,EAAA,KAAAkC;EAAAlC,EAAA,KAAAmB;CAAA,OAAAA,KAAAnB,EAAA;CAAA,IAAAoC;CAAA,IAAApC,EAAA,OAAAG,OAAAC,IAAA,2BAAA,GAAA;EAAEgC,KAAA,CAAA;EAAEpC,EAAA,KAAAoC;CAAA,OAAAA,KAAApC,EAAA;CATLnE,UAAUsF,IASPiB,EAAE;CAAC,IAAAC;CAAA,IAAArC,EAAA,OAAAjB,qBAAAiB,EAAA,QAAAhB,YAAAgB,EAAA,QAAAtC,SAAA;EAGJ2E,MAAAC,YAAA;GAGE,MAAAlD,OAAakD,QAAQ5B,iBAAgBU,OAAQ;GAC7CV,iBAAgBU,UAAWhC;GAC3BkB,iBAAiBlB,IAAI;GAErB,IAAIuB,kBAAiBS,YAAa,aAAW;IAC3C3B,+BACEV,mBACAC,UACAtB,SACA0B,IACF;IAAC;GAAA;GAIHwB,sBAAqBQ,UAAW;GAChC,IACET,kBAAiBS,YAAa,YAA9B,CACCP,8BAA6BO,SAAQ;IAEtC7C,qCAAqC;IACrCsC,8BAA6BO,UAAW;GAAH;EACtC;EACFpB,EAAA,KAAAjB;EAAAiB,EAAA,MAAAhB;EAAAgB,EAAA,MAAAtC;EAAAsC,EAAA,MAAAqC;CAAA,OAAAA,KAAArC,EAAA;CA1BH,MAAAuC,sBAA4BF;CA4B1B,IAAAG;CAAA,IAAAxC,EAAA,QAAAK,iBAAAL,EAAA,QAAAO,cAAAP,EAAA,QAAAuC,qBAAA;EAEKC,KAAA;GAAAnC;GAAAE;GAAAgC;EAAiD;EAACvC,EAAA,MAAAK;EAAAL,EAAA,MAAAO;EAAAP,EAAA,MAAAuC;EAAAvC,EAAA,MAAAwC;CAAA,OAAAA,KAAAxC,EAAA;CAAA,OAAlDwC;AAAkD;AAG3D,MAAMC,2BAA2BtG,SAAS2D,2BAA2B;AAErE,MAAM4C,yBAAwBC,UAAA;CAAA,MAAA3C,IAAAC,EAAA,EAAA;CAG5B,MAAAxC,aAAmBkF,MAAKlF,cAALS;CACnB,MAAAN,cAAoB+E,MAAK/E,eAAL;CAA0B,IAAAmC;CAAA,IAAAC,EAAA,OAAA2C,MAAAhF,kBAAA;EAC1BoC,KAAA4C,MAAKhF,oBAAqBG,mBAAmB;EAACkC,EAAA,KAAA2C,MAAAhF;EAAAqC,EAAA,KAAAD;CAAA,OAAAA,KAAAC,EAAA;CAAlE,MAAA4C,cAAoB7C;CACpB,MAAAlC,oBAA0B8E,MAAK9E;CAAmB,IAAAqC;CAAA,IAAAF,EAAA,OAAA2C,MAAAjF,SAAA;EAE3BwC,KAAAyC,MAAKjF,WAAYV,gBAAgB;EAACgD,EAAA,KAAA2C,MAAAjF;EAAAsC,EAAA,KAAAE;CAAA,OAAAA,KAAAF,EAAA;CACzD,MAAAtC,UAAgBxB,YAAY2G,EAAc;CAC1C,MAAA,CAAA9D,qBAA4BnD,SAC1BkH,MACF;CACA,MAAAC,kBACErF,QAAOsF,YAAaC,KAAAA,IAApB,aAAA,UAAuDvF,QAAOsF;CAAW,IAAAvC;CAAA,IAAAT,EAAA,OAAAjB,qBAAAiB,EAAA,OAAAtC,WAAAsC,EAAA,OAAA+C,iBAAA;EAEzEtC,KAAArE,QACE2G,iBACAN,yBAAyB;GAAA/E;GAAAsB,UAEb+D;GAAehE;EAE3B,CAAC,CACH;EAACiB,EAAA,KAAAjB;EAAAiB,EAAA,KAAAtC;EAAAsC,EAAA,KAAA+C;EAAA/C,EAAA,KAAAS;CAAA,OAAAA,KAAAT,EAAA;CARH,MAAA,EAAAK,eAAAE,YAAAgC,wBAA2DrG,YACzDuE,EAQF;CAGEtD,sBAAsB,CAAA,GACjBM,WAAUkE,IAAKuB,MAAW,GAAC,GAC3B7C,cAAasB,IAAKwB,MAAW,CAAC,CAClC;CAAC,IAAArC;CAAA,IAAAd,EAAA,OAAApC,eAAAoC,EAAA,OAAAnC,qBAAAmC,EAAA,QAAAvC,cAAAuC,EAAA,QAAAK,iBAAAL,EAAA,QAAAjB,qBAAAiB,EAAA,QAAA4C,eAAA5C,EAAA,QAAAtC,WAAAsC,EAAA,QAAA+C,mBAAA/C,EAAA,QAAAuC,qBAAA;EAAA,IAAApB;EAAA,IAAAnB,EAAA,QAAApC,eAAAoC,EAAA,QAAAnC,qBAAAmC,EAAA,QAAA4C,eAAA5C,EAAA,QAAAtC,SAAA;GAEuCyD,MAAAiC,QACvChH,QACEiH,IAACpB,IACDnF,kBAAkB;IAAAmF,IACZoB,IAACpB;IAAGqB,MACF;IAAWC,MACXF,IAACE;IAAKC,KACPH,IAACG;IAAIC,MACJJ,IAACI;IAAKC,MACNL,IAACK;IAAKhG;IAAAkF;IAAAhF;IAAAC,mBAIOwF,IAACxF,qBAADA;IAAwC,GACvDwF,IAACM,UAAWV,KAAAA,IAAZ,EAAAU,OAAiCN,IAACM,MAAY,IAA9C,CAA8C;IAAC,GAC/CN,IAACO,gBAAiBX,KAAAA,IAAlB,EAAAW,aACeP,IAACO,YACf,IAFD,CAEC;IAACC,UACIC;GAGZ,CAAC,CACH;GAAC9D,EAAA,MAAApC;GAAAoC,EAAA,MAAAnC;GAAAmC,EAAA,MAAA4C;GAAA5C,EAAA,MAAAtC;GAAAsC,EAAA,MAAAmB;EAAA,OAAAA,KAAAnB,EAAA;EAtBH,MAAA+D,oBAA0BtG,WAAUkE,IAAKR,EAuBzC;EAAE,IAAAiB;EAAA,IAAApC,EAAA,QAAApC,eAAAoC,EAAA,QAAAnC,qBAAAmC,EAAA,QAAAjB,qBAAAiB,EAAA,QAAA4C,eAAA5C,EAAA,QAAAtC,WAAAsC,EAAA,QAAA+C,mBAAA/C,EAAA,QAAAuC,qBAAA;GACuCH,MAAA4B,QACvC5H,QACE6H,IAAChC,IACDnF,kBACEC,0BACE;IAAAkF,IACMgC,IAAChC;IAAGqB,MACF;IAAQC,MACRU,IAACV;IAAKC,KACPS,IAACT;IAAIE,MACJO,IAACP;IAAKhG;IAAAkF;IAAAhF;IAAAC,mBAIOoG,IAACpG,qBAADA;IAAwC,GACvDoG,IAACN,UAAWV,KAAAA,IAAZ,EAAAU,OAAiCM,IAACN,MAAY,IAA9C,CAA8C;IAAC,GAC/CM,IAACL,gBAAiBX,KAAAA,IAAlB,EAAAW,aACeK,IAACL,YACf,IAFD,CAEC;IAACC,UACI,YAAA;KACRtB,qBAAoBhB,SAClBA,KAAIM,QAAQqC,WAAYA,OAAMjC,OAAQgC,IAAChC,EAAG,CAC5C;IAAC;GAEL,SAEEvC,4BAA4BX,mBAAmBgE,eAAe,CAClE,CACF,CACF;GAAC/C,EAAA,MAAApC;GAAAoC,EAAA,MAAAnC;GAAAmC,EAAA,MAAAjB;GAAAiB,EAAA,MAAA4C;GAAA5C,EAAA,MAAAtC;GAAAsC,EAAA,MAAA+C;GAAA/C,EAAA,MAAAuC;GAAAvC,EAAA,MAAAoC;EAAA,OAAAA,KAAApC,EAAA;EA7BH,MAAAmE,iBAAuB9D,cAAasB,IAAKS,EA8BzC;EACOtB,KAAA,CAAA,GAAIiD,mBAAiB,GAAKI,cAAc;EAACnE,EAAA,KAAApC;EAAAoC,EAAA,KAAAnC;EAAAmC,EAAA,MAAAvC;EAAAuC,EAAA,MAAAK;EAAAL,EAAA,MAAAjB;EAAAiB,EAAA,MAAA4C;EAAA5C,EAAA,MAAAtC;EAAAsC,EAAA,MAAA+C;EAAA/C,EAAA,MAAAuC;EAAAvC,EAAA,MAAAc;CAAA,OAAAA,KAAAd,EAAA;CAalD,MAAAqE,SAAehI,gBAAgB+H,EAAc;CAG3C,MAAAE,MAAYD,OAAME;CAAO,IAAApD;CAAA,IAAAnB,EAAA,QAAAsE,KAAA;EAGXnD,KAAAmD,IAAGzC,OAAQ2C,MAA6B;EAACxE,EAAA,MAAAsE;EAAAtE,EAAA,MAAAmB;CAAA,OAAAA,KAAAnB,EAAA;CAAA,IAAAoC;CAAA,IAAApC,EAAA,QAAAsE,KAAA;EACtClC,KAAAkC,IAAGzC,OAAQ4C,MAA0B;EAACzE,EAAA,MAAAsE;EAAAtE,EAAA,MAAAoC;CAAA,OAAAA,KAAApC,EAAA;CAAA,IAAAqC;CAAA,IAAArC,EAAA,QAAAsE,OAAAtE,EAAA,QAAAO,cAAAP,EAAA,QAAAmB,MAAAnB,EAAA,QAAAoC,IAAA;EAHhDC,KAAA;GAAAqC,SACIJ;GAAG7G,YACA0D;GAAyCd,eACtC+B;GAAsC7B;EAEvD;EAACP,EAAA,MAAAsE;EAAAtE,EAAA,MAAAO;EAAAP,EAAA,MAAAmB;EAAAnB,EAAA,MAAAoC;EAAApC,EAAA,MAAAqC;CAAA,OAAAA,KAAArC,EAAA;CAPH,MAAAuE,QAEElC;CAM6B,IAAAsC;CAAA,IAAA3E,EAAA,QAAAqE,UAAArE,EAAA,QAAAuE,MAAAG,SAAA;EAS7BC,MAA4C,CAAC;EAC7C,KAAK,MAAAC,UAAgBL,MAAKG,SAAQ;GAChC,IAAIE,OAAMC,oBAAqB,aAAa;GAC5C,KAAK,MAAAC,QAAcF,OAAMG,OAAM;IAC7B,MAAAC,WAAiB,GAAGJ,OAAM3C,GAAG,IAAK6C,KAAIvB;IACtCoB,IAAIK,YAAY;KAAAC,MACR;KAAU,GACZH,KAAII,gBAAiBjC,KAAAA,IAArB,EAAAiC,aACeJ,KAAII,YAClB,IAFD,CAEC;KAACC,YACML,KAAIM;KAAqBC,UAC5BC,SACPjB,OAAMlF,IAAK,EAAAoG,KAAOX,OAAM3C,GAAI,CAAC,CAAC,CAAAuD,SAAUV,KAAIvB,MAAO+B,IAAe;IACtE;GARa;EASd;EACFtF,EAAA,MAAAqE;EAAArE,EAAA,MAAAuE,MAAAG;EAAA1E,EAAA,MAAA2E;CAAA,OAAAA,MAAA3E,EAAA;CAhBH,MAAAyF,UAiBEd;CAGF,MAAAe,YAAkBpJ,sBAAsB;CAAE,IAAAkG;CAAA,IAAAxC,EAAA,QAAA0F,aAAA1F,EAAA,QAAAyF,SAAA;EAIxCjD,WAAA;GACE,MAAAmD,SAAeD,UAAStE;GACxB,IAAI,CAACuE,QAAM;GAAS,OACbA,OAAMC,aAAaC,SAAU,EAAAC,wBACV,EAAAf,OAASU,QAAQ,GAC3C,CAAC;EAAC;EACHzF,EAAA,MAAA0F;EAAA1F,EAAA,MAAAyF;EAAAzF,EAAA,MAAAwC;CAAA,OAAAA,KAAAxC,EAAA;CAAA,IAAA+F;CAAA,IAAA/F,EAAA,QAAAyF,SAAA;EACDM,KAAA,CAACN,OAAO;EAACzF,EAAA,MAAAyF;EAAAzF,EAAA,MAAA+F;CAAA,OAAAA,KAAA/F,EAAA;CATXvD,wBACE,gBACA+F,IAOAuD,EACF;CAAC,IAAAC;CAAA,IAAAhG,EAAA,QAAAqE,UAAArE,EAAA,QAAAuE,OAAA;EAEoByB,MAAA1C,MAAA2C,UAAA;GACnB,MAAAC,OAAa5C,SAAS,cAAciB,MAAK9G,aAAc8G,MAAKlE;GAC5D,MAAA8F,QAAcD,KAAKD;GACnB,IAAI,CAACE,OACH,MAAM,IAAIC,MACR,0BAA0B9C,KAAI,YAAa2C,MAAK,WAAYC,KAAI1E,OAAO,EACzE;GACD,OACM6C,OAAMlF,IAAK,EAAAoG,KAAOY,MAAKlE,GAAI,CAAC;EAAC;EACrCjC,EAAA,MAAAqE;EAAArE,EAAA,MAAAuE;EAAAvE,EAAA,MAAAgG;CAAA,OAAAA,KAAAhG,EAAA;CATD,MAAAqG,eAAqBL;CASnB,IAAAM;CAAA,IAAAtG,EAAA,QAAAuE,OAAA;EAGU+B,YAAM/B;EAAKvE,EAAA,MAAAuE;EAAAvE,EAAA,MAAAsG;CAAA,OAAAA,MAAAtG,EAAA;CAAA,IAAAuG;CAAA,IAAAvG,EAAA,QAAAqE,UAAArE,EAAA,QAAAqG,cAAA;EACbE,OAAAC,UAAA;GACN,IAAI,QAAQA,OAAK,OAASnC,OAAMlF,IAAK,EAAAoG,KAAOiB,MAAKvE,GAAI,CAAC;GAAE,OACjDoE,aAAaG,MAAKlD,MAAOkD,MAAKP,KAAM;EAAC;EAC7CjG,EAAA,MAAAqE;EAAArE,EAAA,MAAAqG;EAAArG,EAAA,MAAAuG;CAAA,OAAAA,MAAAvG,EAAA;CAAA,IAAAyG;CAAA,IAAAC;CAAA,IAAA1G,EAAA,QAAAqG,cAAA;EACUI,OAAAE,QAAA;GAAC,MAAA,EAAAV,OAAAW,YAAAD;GAAS,OAAKN,aAAa,aAAaJ,OAAK;EAAC;EAC5CS,OAAAG,QAAA;GAAC,MAAA,EAAAZ,OAAAa,YAAAD;GAAS,OAAKR,aAAa,UAAUJ,OAAK;EAAC;EAAAjG,EAAA,MAAAqG;EAAArG,EAAA,MAAAyG;EAAAzG,EAAA,MAAA0G;CAAA,OAAA;EAAAD,MAAAzG,EAAA;EAAA0G,MAAA1G,EAAA;CAAA;CAAA,IAAA2G;CAAA,IAAA3G,EAAA,QAAAuC,qBAAA;EACzCoE,MAAA,OAAAE,QAAA;GAAO,MAAA,EAAAtD,MAAAC,KAAAE,MAAA7F,mBAAAkJ,qBAAApD,OAAAC,gBAAAiD;GAQtB,MAAAG,WAAsC;IAAA/E,IAChCtF,YAAY;IAAC4G;IAAAC;IAGXE;IAAqB7F,mBAC3BA;IAAiB,GACb8F,UAAUV,KAAAA,IAAV,EAAAU,MAAmC,IAAnC,CAAmC;IAAC,GACpCC,gBAAgBX,KAAAA,IAAhB,EAAAW,YAA+C,IAA/C,CAA+C;IAACqD,WACzCC,KAAIC,IAAK;GACtB;GACA5E,qBAAoB6E,WAAU,CAAA,GAAI7F,QAAM2C,QAAM,CAAC;GAAC,OACzCA,SAAMjC;EAAG;EACjBjC,EAAA,MAAAuC;EAAAvC,EAAA,MAAA2G;CAAA,OAAAA,MAAA3G,EAAA;CAAA,IAAA6G;CAAA,IAAA7G,EAAA,QAAAqE,UAAArE,EAAA,QAAAjB,qBAAAiB,EAAA,QAAAuE,SAAAvE,EAAA,QAAAtC,WAAAsC,EAAA,QAAA+C,mBAAA/C,EAAA,QAAAuC,qBAAA;EACasE,MAAA,OAAA5E,OAAA;GAGZ,IAAIsC,MAAK9G,WAAW4J,MAAMC,QAAOjE,IAACpB,OAAQA,EAAE,GAC1C,MAAM,IAAImE,MACR,4BAA4BnE,GAAE,kFAChC;GAMF,IAAA;IACE,MAAMoC,OAAMlF,IAAK,EAAAoG,KAAOtD,GAAG,CAAC,CAAC,CAAAsF,OAAQ;GAAC,QAAA;IAEtC,MAAAC,qBAA2B9H,4BACzBX,mBACAgE,eACF;IACA,IAAA;KACE,MAAMnG,4BAA4Bc,SAASuE,EAAE;KAC7CM,qBAAoBkF,WAClBlG,OAAIM,QAAQ6F,aAAYxD,SAAMjC,OAAQA,EAAE,CAC1C;IAAC,SAAA0F,KAAA;KACMtJ,MAAAA,QAAAA;KACPmJ,mBAAmB;KACnB,MAAMnJ;IAAM;IAEdmJ,mBAAmB;GAAC;EACrB;EACFxH,EAAA,MAAAqE;EAAArE,EAAA,MAAAjB;EAAAiB,EAAA,MAAAuE;EAAAvE,EAAA,MAAAtC;EAAAsC,EAAA,MAAA+C;EAAA/C,EAAA,MAAAuC;EAAAvC,EAAA,MAAA6G;CAAA,OAAAA,MAAA7G,EAAA;CAAA,IAAA2H;CAAA,IAAA3H,EAAA,QAAAsG,OAAAtG,EAAA,QAAAuG,OAAAvG,EAAA,QAAAyG,OAAAzG,EAAA,QAAA0G,OAAA1G,EAAA,QAAA2G,OAAA3G,EAAA,QAAA6G,KAAA;EA3DIc,MAAA;GAAAC,UACKtB;GAAW1B,QACb2B;GAGPsB,WACUpB;GAA+CqB,cAC5CpB;GAA4CqB,iBACzCpB;GAoBhBqB,cACanB;EA+BhB;EAAC7G,EAAA,MAAAsG;EAAAtG,EAAA,MAAAuG;EAAAvG,EAAA,MAAAyG;EAAAzG,EAAA,MAAA0G;EAAA1G,EAAA,MAAA2G;EAAA3G,EAAA,MAAA6G;EAAA7G,EAAA,MAAA2H;CAAA,OAAAA,MAAA3H,EAAA;CAAA,OA5DM2H;AA4DN;AAGH,MAAaM,qBAAqB9L,SAASuG,qBAAqB;AAKhEnG,sBAAsBmG,wBAAwBwF,QAAQC,WAAW;CAC/D,IAAI,CAACD,OAAOtC,gBAAgBuC,OAAOvC,aAAawC,WAAW,MACzDF,OAAOtC,eAAelJ,aAAa;AAEvC,CAAC;AA9VmC,SAAAkF,MAAAI,GAAA;CAAA,OAkDkBA,EAACC;AAAG;AAiE5B,SAAAa,SAAA;CAAA,uBAWpB,IAAIlE,IAAI;AAAC;AAXW,SAAAsE,OAAAG,GAAA;CAAA,OA4BCA,EAACpB;AAAG;AA5BL,SAAAkB,OAAAc,GAAA;CAAA,OA6BIA,EAAChC;AAAG;AA7BR,eAAA6B,SAAA,CAAA;AAAA,SAAAU,OAAA6D,KAAA;CAAA,OA0GMpE,IAACX,SAAU;AAAW;AA1G5B,SAAAmB,OAAA6D,KAAA;CAAA,OA2GSrE,IAACX,SAAU;AAAQ"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { McpServerResourceProps } from "./McpServerResource.js";
|
|
2
|
+
//#region src/resources/McpServerRemovalFence.d.ts
|
|
3
|
+
declare const withMcpServerRemovalFence: (props: McpServerResourceProps, beginRemovalFence: () => () => void) => McpServerResourceProps;
|
|
4
|
+
declare const beginMcpServerRemovalFence: (props: McpServerResourceProps) => (() => void) | undefined;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { beginMcpServerRemovalFence, withMcpServerRemovalFence };
|
|
7
|
+
//# sourceMappingURL=McpServerRemovalFence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"McpServerRemovalFence.d.ts","names":[],"sources":["../../src/resources/McpServerRemovalFence.ts"],"mappings":";;cAQa,4BACX,OAAO,wBACP,wCACC;cAMU,6BACX,OAAO"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/resources/McpServerRemovalFence.ts
|
|
2
|
+
const beginRemovalFenceSymbol = Symbol();
|
|
3
|
+
const withMcpServerRemovalFence = (props, beginRemovalFence) => ({
|
|
4
|
+
...props,
|
|
5
|
+
[beginRemovalFenceSymbol]: beginRemovalFence
|
|
6
|
+
});
|
|
7
|
+
const beginMcpServerRemovalFence = (props) => props[beginRemovalFenceSymbol]?.();
|
|
8
|
+
//#endregion
|
|
9
|
+
export { beginMcpServerRemovalFence, withMcpServerRemovalFence };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=McpServerRemovalFence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"McpServerRemovalFence.js","names":["McpServerResourceProps","beginRemovalFenceSymbol","Symbol","McpServerResourceInternalProps","withMcpServerRemovalFence","props","beginRemovalFence","beginMcpServerRemovalFence"],"sources":["../../src/resources/McpServerRemovalFence.ts"],"sourcesContent":["import type { McpServerResourceProps } from \"./McpServerResource\";\n\nconst beginRemovalFenceSymbol = Symbol();\n\ntype McpServerResourceInternalProps = McpServerResourceProps & {\n [beginRemovalFenceSymbol]?: () => () => void;\n};\n\nexport const withMcpServerRemovalFence = (\n props: McpServerResourceProps,\n beginRemovalFence: () => () => void,\n): McpServerResourceProps =>\n ({\n ...props,\n [beginRemovalFenceSymbol]: beginRemovalFence,\n }) as McpServerResourceProps;\n\nexport const beginMcpServerRemovalFence = (\n props: McpServerResourceProps,\n): (() => void) | undefined =>\n (props as McpServerResourceInternalProps)[beginRemovalFenceSymbol]?.();\n"],"mappings":";AAEA,MAAMC,0BAA0BC,OAAO;AAMvC,MAAaE,6BACXC,OACAC,uBAEC;CACC,GAAGD;EACFJ,0BAA0BK;AAC7B;AAEF,MAAaC,8BACXF,UAECA,MAAyCJ,wBAAwB,GAAG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpServerResource.d.ts","names":[],"sources":["../../src/resources/McpServerResource.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"McpServerResource.d.ts","names":[],"sources":["../../src/resources/McpServerResource.ts"],"mappings":";;;;KAsCY;EACV;EACA,MAAM;EACN;EACA;EACA;EACA,MAAM;EACN,SAAS;EACT;EACA;EACA;EACA;aAAmB;;WACV;EACT,gBAAgB;;cAOL,4BACX,OAAO;cAisBI,+CAAiB,SAAA,4BAAA,OAAA"}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { assertValidServerId } from "../utils/serverId.js";
|
|
2
2
|
import { createMcpId } from "../utils/createMcpId.js";
|
|
3
|
-
import {
|
|
3
|
+
import { hasPersistedCredentials, isAuthStateForServerUrl } from "../utils/serverUrl.js";
|
|
4
|
+
import { clearOAuthProviderAuthState, createOAuthProvider, hasUsableOAuthTokens } from "../auth/createOAuthProvider.js";
|
|
4
5
|
import { buildHeaders } from "../auth/buildHeaders.js";
|
|
5
6
|
import { validateElicitationContent } from "./validateElicitationContent.js";
|
|
7
|
+
import { beginMcpServerRemovalFence } from "./McpServerRemovalFence.js";
|
|
6
8
|
import { useEffect, useEffectEvent, useMemo, useRef, useState } from "@assistant-ui/tap/react-shim";
|
|
7
9
|
import { resource, useResource, withKey } from "@assistant-ui/tap";
|
|
10
|
+
import { shallowEqual } from "@assistant-ui/store/internal";
|
|
8
11
|
import { Client, StreamableHTTPClientTransport, UnauthorizedError } from "@modelcontextprotocol/client";
|
|
9
12
|
//#region src/resources/McpServerResource.ts
|
|
10
13
|
const getConnectionDependencies = (props) => {
|
|
@@ -33,7 +36,6 @@ const getConnectionDependencies = (props) => {
|
|
|
33
36
|
props.elicitation !== false
|
|
34
37
|
];
|
|
35
38
|
};
|
|
36
|
-
const areConnectionDependenciesEqual = (left, right) => left.length === right.length && left.every((value, index) => Object.is(value, right[index]));
|
|
37
39
|
const useMcpServerResourceInstance = (props) => {
|
|
38
40
|
assertValidServerId(props.id);
|
|
39
41
|
const [connectionState, setConnectionState] = useState("disconnected");
|
|
@@ -115,10 +117,23 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
115
117
|
if (timeout !== void 0) clearTimeout(timeout);
|
|
116
118
|
}
|
|
117
119
|
});
|
|
120
|
+
const unboundAuthMessage = () => `MCP server "${props.id}" has saved authentication for a different URL. Authenticate again to connect to ${props.url}.`;
|
|
121
|
+
const loadAuthState = useEffectEvent(async () => {
|
|
122
|
+
const state = await props.storage.loadAuthState(props.id);
|
|
123
|
+
if (isAuthStateForServerUrl(state, props.url)) return {
|
|
124
|
+
state,
|
|
125
|
+
unbound: false
|
|
126
|
+
};
|
|
127
|
+
return {
|
|
128
|
+
state: null,
|
|
129
|
+
unbound: hasPersistedCredentials(state)
|
|
130
|
+
};
|
|
131
|
+
});
|
|
118
132
|
const buildTransport = useEffectEvent(async () => {
|
|
119
133
|
if (props.auth.type === "oauth") {
|
|
120
134
|
const authProvider = createOAuthProvider({
|
|
121
135
|
serverId: props.id,
|
|
136
|
+
serverUrl: props.url,
|
|
122
137
|
config: props.auth,
|
|
123
138
|
storage: props.storage,
|
|
124
139
|
redirectUri: props.redirectUri,
|
|
@@ -127,8 +142,9 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
127
142
|
return new StreamableHTTPClientTransport(new URL(props.url), { authProvider });
|
|
128
143
|
}
|
|
129
144
|
if (props.auth.type === "bearer") {
|
|
130
|
-
const
|
|
131
|
-
const headers = buildHeaders(props.auth,
|
|
145
|
+
const { state: state_0, unbound } = await loadAuthState();
|
|
146
|
+
const headers = buildHeaders(props.auth, state_0);
|
|
147
|
+
if (!headers && unbound) throw new Error(unboundAuthMessage());
|
|
132
148
|
const transportOpts = {};
|
|
133
149
|
if (headers) transportOpts.requestInit = { headers };
|
|
134
150
|
return new StreamableHTTPClientTransport(new URL(props.url), transportOpts);
|
|
@@ -257,8 +273,8 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
257
273
|
const doCompleteAuth = useEffectEvent(async (callbackUrl) => {
|
|
258
274
|
const validationGeneration = connectionGenerationRef.current;
|
|
259
275
|
const url_0 = new URL(callbackUrl);
|
|
260
|
-
const
|
|
261
|
-
if (!
|
|
276
|
+
const state_1 = url_0.searchParams.get("state");
|
|
277
|
+
if (!state_1) throw new Error("missing \"state\" parameter");
|
|
262
278
|
let pendingAuthValidation = pendingAuthValidationRef.current;
|
|
263
279
|
if (!pendingAuthValidation) {
|
|
264
280
|
let resolve_0;
|
|
@@ -273,10 +289,11 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
273
289
|
}
|
|
274
290
|
pendingAuthValidation.count += 1;
|
|
275
291
|
try {
|
|
276
|
-
const persisted = await
|
|
292
|
+
const { state: persisted, unbound } = await loadAuthState();
|
|
277
293
|
if (!isCurrentConnection(validationGeneration)) throw createInterruptedAuthError();
|
|
294
|
+
if (unbound) throw new Error(unboundAuthMessage());
|
|
278
295
|
if (!persisted?.state) throw new Error("no pending OAuth authorization request for this server");
|
|
279
|
-
if (persisted.state !==
|
|
296
|
+
if (persisted.state !== state_1) throw new Error("OAuth state does not match the authorization request");
|
|
280
297
|
if (!url_0.searchParams.get("code") && !url_0.searchParams.get("error")) throw new Error("missing authorization code in callback URL");
|
|
281
298
|
} finally {
|
|
282
299
|
pendingAuthValidation.count -= 1;
|
|
@@ -327,9 +344,9 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
327
344
|
return;
|
|
328
345
|
}
|
|
329
346
|
const generation_4 = connectionGenerationRef.current;
|
|
330
|
-
let
|
|
347
|
+
let loaded;
|
|
331
348
|
try {
|
|
332
|
-
|
|
349
|
+
loaded = await loadAuthState();
|
|
333
350
|
} catch (error_2) {
|
|
334
351
|
if (signal.cancelled || !isCurrentConnection(generation_4)) return;
|
|
335
352
|
const message_0 = error_2 instanceof Error ? error_2.message : String(error_2);
|
|
@@ -338,9 +355,14 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
338
355
|
return;
|
|
339
356
|
}
|
|
340
357
|
if (signal.cancelled || !isCurrentConnection(generation_4)) return;
|
|
358
|
+
if (loaded.unbound) {
|
|
359
|
+
setLastError({ message: unboundAuthMessage() });
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const persisted = loaded.state;
|
|
341
363
|
if (props.auth.type === "oauth") {
|
|
342
|
-
if (!
|
|
343
|
-
} else if (!
|
|
364
|
+
if (!hasUsableOAuthTokens(persisted, props.auth)) return;
|
|
365
|
+
} else if (!persisted?.token) return;
|
|
344
366
|
const pendingAuthValidation_0 = pendingAuthValidationRef.current;
|
|
345
367
|
if (pendingAuthValidation_0) {
|
|
346
368
|
await pendingAuthValidation_0.promise;
|
|
@@ -369,7 +391,7 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
369
391
|
transportCloseQueueRef.current = task_0;
|
|
370
392
|
};
|
|
371
393
|
}, [props.transportCloseQueueRef]);
|
|
372
|
-
const
|
|
394
|
+
const state_2 = useMemo(() => ({
|
|
373
395
|
id: props.id,
|
|
374
396
|
kind: props.kind,
|
|
375
397
|
name: props.name,
|
|
@@ -393,18 +415,21 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
393
415
|
pendingElicitations
|
|
394
416
|
]);
|
|
395
417
|
return {
|
|
396
|
-
getState: () =>
|
|
418
|
+
getState: () => state_2,
|
|
397
419
|
connect: doConnect,
|
|
398
420
|
disconnect: doDisconnect,
|
|
399
421
|
remove: async () => {
|
|
400
|
-
|
|
422
|
+
const releaseRemovalFence = beginMcpServerRemovalFence(props);
|
|
401
423
|
try {
|
|
424
|
+
await doDisconnect();
|
|
402
425
|
await clearOAuthProviderAuthState(props.storage, props.id);
|
|
403
426
|
await props.onRemove();
|
|
404
427
|
} catch (err_1) {
|
|
428
|
+
releaseRemovalFence?.();
|
|
405
429
|
setLastError({ message: err_1 instanceof Error ? err_1.message : String(err_1) });
|
|
406
430
|
throw err_1;
|
|
407
431
|
}
|
|
432
|
+
releaseRemovalFence?.();
|
|
408
433
|
},
|
|
409
434
|
callTool: async (name, args) => {
|
|
410
435
|
const client_1 = clientRef.current;
|
|
@@ -469,7 +494,7 @@ const McpServerResource = resource(function useMcpServerResource(props) {
|
|
|
469
494
|
generation: 0
|
|
470
495
|
});
|
|
471
496
|
let currentConnection = connection;
|
|
472
|
-
if (!
|
|
497
|
+
if (!shallowEqual(connection.dependencies, dependencies)) {
|
|
473
498
|
currentConnection = {
|
|
474
499
|
dependencies,
|
|
475
500
|
generation: connection.generation + 1
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpServerResource.js","names":["useState","useRef","useEffect","useMemo","useEffectEvent","resource","useResource","withKey","ClientOutput","Client","StreamableHTTPClientTransport","UnauthorizedError","ClientOptions","ElicitRequest","ElicitResult","StreamableHTTPClientTransportOptions","clearOAuthProviderAuthState","createOAuthProvider","buildHeaders","assertValidServerId","validateElicitationContent","MCPStorage","MCPAuthConfig","MCPConnectionState","MCPElicitation","MCPElicitationResponse","MCPServerKind","MCPServerState","MCPToolInfo","createMcpId","McpServerResourceProps","id","kind","name","url","icon","auth","storage","redirectUri","autoConnect","connectionTimeout","cache","defaultTtlMs","elicitation","onRemove","Promise","McpServerResourceInstanceProps","transportCloseQueueRef","current","getConnectionDependencies","props","authDependencies","type","token","scopeId","scopes","length","authorizationEndpoint","tokenEndpoint","registrationEndpoint","clientId","clientSecret","areConnectionDependenciesEqual","left","right","every","value","index","Object","is","useMcpServerResourceInstance","connectionState","setConnectionState","tools","setTools","lastError","setLastError","message","authorizationUrl","setAuthorizationUrl","pendingElicitations","setPendingElicitations","clientRef","transportRef","pendingTransportRef","connectionGenerationRef","pendingAuthValidationRef","count","promise","resolve","elicitationResolversRef","Map","result","signal","AbortSignal","onAbort","requestedSchema","pendingDisposalRef","cancelled","mountedRef","closeTransportSafely","transport","close","closeQueuedTransports","transports","task","then","all","map","closePendingTransport","resolvePendingElicitation","entry","get","delete","removeEventListener","filter","setPendingElicitationError","error","NonNullable","has","cancelPendingElicitations","action","detachTransports","pendingTransport","activeTransport","Set","closeTransports","isCurrentConnection","generation","createInterruptedAuthError","cause","Error","undefined","withConnectionTimeout","T","phase","startedAt","timeoutMs","remainingMs","Date","now","timeoutError","timeout","ReturnType","setTimeout","race","_","reject","clearTimeout","buildTransport","authProvider","serverId","config","onAuthorizationUrl","toString","URL","persisted","loadAuthState","headers","transportOpts","requestInit","applyToolsList","list","Array","description","inputSchema","t","info","syncTools","client","options","listPromise","listTools","finalizeConnect","clientOptions","capabilities","listChanged","autoRefresh","debounceMs","onChanged","items","defaultCacheTtlMs","version","setRequestHandler","request","context","params","set","aborted","addEventListener","once","connect","synced","doConnect","err","String","doDisconnect","doCompleteAuth","callbackUrl","validationGeneration","state","searchParams","pendingAuthValidation","resolvePromise","finishAuth","connected","tryAutoConnect","Awaited","tokens","previousDisposal","pendingDisposal","queueMicrotask","getState","disconnect","remove","callTool","args","arguments","Record","listResources","readResource","uri","completeAuth","answerElicitation","response","property","content","isArray","errors","properties","join","McpServerResourceInstance","McpServerResource","useMcpServerResource","dependencies","connection","setConnection","currentConnection"],"sources":["../../src/resources/McpServerResource.ts"],"sourcesContent":["import { useState, useRef, useEffect, useMemo, useEffectEvent } from \"react\";\nimport { resource, useResource, withKey } from \"@assistant-ui/tap\";\nimport type { ClientOutput } from \"@assistant-ui/store\";\nimport {\n Client,\n StreamableHTTPClientTransport,\n UnauthorizedError,\n type ClientOptions,\n type ElicitRequest,\n type ElicitResult,\n type StreamableHTTPClientTransportOptions,\n} from \"@modelcontextprotocol/client\";\nimport {\n clearOAuthProviderAuthState,\n createOAuthProvider,\n} from \"../auth/createOAuthProvider\";\nimport { buildHeaders } from \"../auth/buildHeaders\";\nimport { assertValidServerId } from \"../utils/serverId\";\nimport { validateElicitationContent } from \"./validateElicitationContent\";\nimport type { MCPStorage } from \"./storage/types\";\nimport type {\n MCPAuthConfig,\n MCPConnectionState,\n MCPElicitation,\n MCPElicitationResponse,\n MCPServerKind,\n MCPServerState,\n MCPToolInfo,\n} from \"../mcp-scope\";\nimport { createMcpId } from \"../utils/createMcpId\";\n\nexport type McpServerResourceProps = {\n id: string;\n kind: MCPServerKind;\n name: string;\n url: string;\n icon?: string | undefined;\n auth: MCPAuthConfig;\n storage: MCPStorage;\n redirectUri: string;\n autoConnect: boolean;\n connectionTimeout?: number | undefined;\n cache?: { readonly defaultTtlMs?: number } | undefined;\n readonly elicitation?: boolean;\n onRemove: () => Promise<void>;\n};\n\ntype McpServerResourceInstanceProps = McpServerResourceProps & {\n transportCloseQueueRef: { current: Promise<void> };\n};\n\nexport const getConnectionDependencies = (\n props: McpServerResourceProps,\n): readonly unknown[] => {\n const auth = props.auth;\n const authDependencies =\n auth.type === \"bearer\"\n ? [auth.type, auth.token, props.storage.scopeId]\n : auth.type === \"oauth\"\n ? [\n auth.type,\n auth.scopes?.length,\n ...(auth.scopes ?? []),\n auth.authorizationEndpoint,\n auth.tokenEndpoint,\n auth.registrationEndpoint,\n auth.clientId,\n auth.clientSecret,\n props.storage.scopeId,\n ]\n : [auth.type];\n\n return [\n props.id,\n props.url,\n ...authDependencies,\n props.redirectUri,\n props.cache?.defaultTtlMs,\n props.elicitation !== false,\n ];\n};\n\nconst areConnectionDependenciesEqual = (\n left: readonly unknown[],\n right: readonly unknown[],\n) =>\n left.length === right.length &&\n left.every((value, index) => Object.is(value, right[index]));\n\nconst useMcpServerResourceInstance = (\n props: McpServerResourceInstanceProps,\n): ClientOutput<\"mcpServer\"> => {\n assertValidServerId(props.id);\n const [connectionState, setConnectionState] =\n useState<MCPConnectionState>(\"disconnected\");\n const [tools, setTools] = useState<MCPToolInfo[]>([]);\n const [lastError, setLastError] = useState<{ message: string } | null>(null);\n const [authorizationUrl, setAuthorizationUrl] = useState<string | null>(null);\n const [pendingElicitations, setPendingElicitations] = useState<\n MCPElicitation[]\n >([]);\n\n const clientRef = useRef<Client | null>(null);\n const transportRef = useRef<StreamableHTTPClientTransport | null>(null);\n const pendingTransportRef = useRef<StreamableHTTPClientTransport | null>(\n null,\n );\n const connectionGenerationRef = useRef(0);\n const pendingAuthValidationRef = useRef<{\n count: number;\n promise: Promise<void>;\n resolve: () => void;\n } | null>(null);\n const elicitationResolversRef = useRef(\n new Map<\n string,\n {\n resolve: (result: ElicitResult) => void;\n signal: AbortSignal;\n onAbort: () => void;\n requestedSchema: unknown;\n }\n >(),\n );\n const pendingDisposalRef = useRef<{ cancelled: boolean } | null>(null);\n const mountedRef = useRef(true);\n\n const closeTransportSafely = async (\n transport: StreamableHTTPClientTransport,\n ): Promise<void> => {\n try {\n await transport.close();\n } catch {\n // ignore close errors\n }\n };\n\n const closeQueuedTransports = (\n transports: StreamableHTTPClientTransport[],\n ): Promise<void> => {\n const task = props.transportCloseQueueRef.current.then(async () => {\n await Promise.all(transports.map(closeTransportSafely));\n });\n props.transportCloseQueueRef.current = task;\n return task;\n };\n\n const closePendingTransport = async () => {\n const transport = pendingTransportRef.current;\n pendingTransportRef.current = null;\n await closeQueuedTransports(transport ? [transport] : []);\n };\n\n const resolvePendingElicitation = (id: string, result: ElicitResult) => {\n const entry = elicitationResolversRef.current.get(id);\n if (!entry) return false;\n elicitationResolversRef.current.delete(id);\n entry.signal.removeEventListener(\"abort\", entry.onAbort);\n setPendingElicitations((current) =>\n current.filter((elicitation) => elicitation.id !== id),\n );\n entry.resolve(result);\n return true;\n };\n\n const setPendingElicitationError = (\n id: string,\n error: NonNullable<MCPElicitation[\"error\"]>,\n ) => {\n if (!elicitationResolversRef.current.has(id)) return false;\n setPendingElicitations((current) =>\n current.map((elicitation) =>\n elicitation.id === id ? { ...elicitation, error } : elicitation,\n ),\n );\n return true;\n };\n\n const cancelPendingElicitations = () => {\n for (const [id] of elicitationResolversRef.current) {\n resolvePendingElicitation(id, { action: \"cancel\" });\n }\n };\n\n const detachTransports = () => {\n cancelPendingElicitations();\n const pendingTransport = pendingTransportRef.current;\n const activeTransport = transportRef.current;\n pendingTransportRef.current = null;\n transportRef.current = null;\n clientRef.current = null;\n\n return [\n ...new Set(\n [pendingTransport, activeTransport].filter(\n (transport): transport is StreamableHTTPClientTransport =>\n transport !== null,\n ),\n ),\n ];\n };\n\n const closeTransports = async () => {\n await closeQueuedTransports(detachTransports());\n };\n\n const isCurrentConnection = (generation: number) =>\n mountedRef.current && generation === connectionGenerationRef.current;\n\n const createInterruptedAuthError = (cause?: unknown) =>\n new Error(\n `MCP server \"${props.id}\" authorization was interrupted before completion.`,\n cause === undefined ? undefined : { cause },\n );\n\n const withConnectionTimeout = useEffectEvent(\n async <T>(\n promise: Promise<T>,\n phase: \"connecting\" | \"listing tools\",\n startedAt: number,\n ): Promise<T> => {\n const timeoutMs = props.connectionTimeout;\n if (timeoutMs === undefined) return await promise;\n const remainingMs = timeoutMs - (Date.now() - startedAt);\n const timeoutError = () =>\n new Error(\n `MCP server \"${props.id}\" timed out while ${phase} after ${timeoutMs}ms.`,\n );\n if (remainingMs <= 0) throw timeoutError();\n\n let timeout: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timeout = setTimeout(() => reject(timeoutError()), remainingMs);\n }),\n ]);\n } finally {\n if (timeout !== undefined) clearTimeout(timeout);\n }\n },\n );\n\n const buildTransport = useEffectEvent(\n async (): Promise<StreamableHTTPClientTransport> => {\n if (props.auth.type === \"oauth\") {\n const authProvider = createOAuthProvider({\n serverId: props.id,\n config: props.auth,\n storage: props.storage,\n redirectUri: props.redirectUri,\n onAuthorizationUrl: (url) => setAuthorizationUrl(url.toString()),\n });\n return new StreamableHTTPClientTransport(new URL(props.url), {\n authProvider,\n });\n }\n if (props.auth.type === \"bearer\") {\n const persisted = await props.storage.loadAuthState(props.id);\n const headers = buildHeaders(props.auth, persisted);\n const transportOpts: StreamableHTTPClientTransportOptions = {};\n if (headers) transportOpts.requestInit = { headers };\n return new StreamableHTTPClientTransport(\n new URL(props.url),\n transportOpts,\n );\n }\n return new StreamableHTTPClientTransport(new URL(props.url));\n },\n );\n\n const applyToolsList = useEffectEvent(\n (list: {\n tools: Array<{\n name: string;\n description?: string | undefined;\n inputSchema: unknown;\n }>;\n }) => {\n setTools(\n list.tools.map((t) => {\n const info: MCPToolInfo = {\n name: t.name,\n inputSchema: t.inputSchema,\n };\n if (t.description !== undefined) info.description = t.description;\n return info;\n }),\n );\n },\n );\n\n const syncTools = useEffectEvent(\n async (\n client: Client,\n generation: number,\n options?: { startedAt?: number | undefined },\n ): Promise<boolean> => {\n const listPromise = client.listTools();\n const list =\n options?.startedAt === undefined\n ? await listPromise\n : await withConnectionTimeout(\n listPromise,\n \"listing tools\",\n options.startedAt,\n );\n if (!isCurrentConnection(generation)) return false;\n applyToolsList(list);\n return true;\n },\n );\n\n const finalizeConnect = useEffectEvent(\n async (\n transport: StreamableHTTPClientTransport,\n generation: number,\n ): Promise<boolean> => {\n const clientOptions: ClientOptions = {\n ...(props.elicitation === false\n ? {}\n : { capabilities: { elicitation: {} } }),\n listChanged: {\n tools: {\n autoRefresh: true,\n debounceMs: 300,\n onChanged: (error, items) => {\n if (!isCurrentConnection(generation)) return;\n if (error !== null) {\n setLastError({ message: error.message });\n return;\n }\n if (items === null) return;\n setLastError(null);\n applyToolsList({ tools: items });\n },\n },\n },\n };\n if (props.cache?.defaultTtlMs !== undefined) {\n clientOptions.defaultCacheTtlMs = props.cache.defaultTtlMs;\n }\n const client = new Client(\n {\n name: \"assistant-ui-mcp\",\n version: \"0.0.0\",\n },\n clientOptions,\n );\n if (props.elicitation !== false) {\n client.setRequestHandler(\n \"elicitation/create\",\n (request: ElicitRequest, context): Promise<ElicitResult> => {\n if (!isCurrentConnection(generation)) {\n return Promise.resolve({ action: \"cancel\" });\n }\n if (!(\"requestedSchema\" in request.params)) {\n return Promise.resolve({ action: \"cancel\" });\n }\n const { message, requestedSchema } = request.params;\n\n const id = createMcpId();\n const promise = new Promise<ElicitResult>((resolve) => {\n const onAbort = () => {\n resolvePendingElicitation(id, { action: \"cancel\" });\n };\n elicitationResolversRef.current.set(id, {\n resolve,\n signal: context.signal,\n onAbort,\n requestedSchema,\n });\n });\n setPendingElicitations((current) => [\n ...current,\n {\n id,\n message,\n requestedSchema,\n },\n ]);\n const entry = elicitationResolversRef.current.get(id);\n if (entry) {\n if (context.signal.aborted) {\n entry.onAbort();\n } else {\n context.signal.addEventListener(\"abort\", entry.onAbort, {\n once: true,\n });\n }\n }\n return promise;\n },\n );\n }\n const startedAt = Date.now();\n await withConnectionTimeout(\n client.connect(transport),\n \"connecting\",\n startedAt,\n );\n if (!isCurrentConnection(generation)) return false;\n // Defer ref assignment until listTools() also succeeds — otherwise a\n // post-connect failure leaves stale refs that `callTool()` would\n // happily walk into, producing confusing SDK errors instead of\n // \"not connected\".\n const synced = await syncTools(client, generation, { startedAt });\n if (!synced) return false;\n\n pendingTransportRef.current = null;\n clientRef.current = client;\n transportRef.current = transport;\n setConnectionState(\"connected\");\n return true;\n },\n );\n\n const doConnect = useEffectEvent(async () => {\n const generation = ++connectionGenerationRef.current;\n // Close any prior transport/client so a re-connect doesn't leak.\n await closeTransports();\n if (!isCurrentConnection(generation)) return;\n\n setConnectionState(\"connecting\");\n setLastError(null);\n setAuthorizationUrl(null);\n // Clear tools so a reconnect (error → connect, or authRequired\n // → connect) doesn't expose the previous attempt's tool list.\n setTools([]);\n let transport: StreamableHTTPClientTransport | null = null;\n try {\n transport = await buildTransport();\n if (!isCurrentConnection(generation)) {\n await closeQueuedTransports([transport]);\n return;\n }\n pendingTransportRef.current = transport;\n // Don't assign to transportRef until connect succeeds — otherwise a\n // failed `listTools()` leaves an orphaned transport that future\n // doConnect / doDisconnect calls treat as live.\n await finalizeConnect(transport, generation);\n } catch (err) {\n if (!isCurrentConnection(generation)) return;\n\n if (err instanceof UnauthorizedError) {\n // OAuth: keep the transport alive so completeAuth can call\n // finishAuth on it. Closing it before storing would leave a\n // closed transport on transportRef.\n pendingTransportRef.current = null;\n transportRef.current = transport;\n setConnectionState(\"authRequired\");\n } else {\n cancelPendingElicitations();\n if (transport) {\n pendingTransportRef.current = null;\n await closeQueuedTransports([transport]);\n }\n setLastError({\n message: err instanceof Error ? err.message : String(err),\n });\n setConnectionState(\"error\");\n }\n }\n });\n\n const doDisconnect = useEffectEvent(async () => {\n connectionGenerationRef.current += 1;\n setTools([]);\n setAuthorizationUrl(null);\n setConnectionState(\"disconnected\");\n await closeTransports();\n });\n\n const doCompleteAuth = useEffectEvent(async (callbackUrl: string) => {\n const validationGeneration = connectionGenerationRef.current;\n const url = new URL(callbackUrl);\n const state = url.searchParams.get(\"state\");\n if (!state) throw new Error('missing \"state\" parameter');\n let pendingAuthValidation = pendingAuthValidationRef.current;\n if (!pendingAuthValidation) {\n let resolve!: () => void;\n const promise = new Promise<void>((resolvePromise) => {\n resolve = resolvePromise;\n });\n pendingAuthValidation = { count: 0, promise, resolve };\n pendingAuthValidationRef.current = pendingAuthValidation;\n }\n pendingAuthValidation.count += 1;\n try {\n const persisted = await props.storage.loadAuthState(props.id);\n if (!isCurrentConnection(validationGeneration)) {\n throw createInterruptedAuthError();\n }\n if (!persisted?.state) {\n throw new Error(\n \"no pending OAuth authorization request for this server\",\n );\n }\n if (persisted.state !== state) {\n throw new Error(\"OAuth state does not match the authorization request\");\n }\n if (!url.searchParams.get(\"code\") && !url.searchParams.get(\"error\")) {\n throw new Error(\"missing authorization code in callback URL\");\n }\n } finally {\n pendingAuthValidation.count -= 1;\n if (pendingAuthValidation.count === 0) {\n pendingAuthValidationRef.current = null;\n pendingAuthValidation.resolve();\n }\n }\n\n // Claim the generation before a waiting auto-connect can resume.\n const generation = ++connectionGenerationRef.current;\n cancelPendingElicitations();\n await closePendingTransport();\n if (!isCurrentConnection(generation)) throw createInterruptedAuthError();\n\n setConnectionState(\"authPending\");\n setLastError(null);\n try {\n let transport = transportRef.current;\n if (!transport) {\n transport = await buildTransport();\n if (!isCurrentConnection(generation)) {\n await closeQueuedTransports([transport]);\n throw createInterruptedAuthError();\n }\n }\n transportRef.current = null;\n clientRef.current = null;\n pendingTransportRef.current = transport;\n await transport.finishAuth(url.searchParams);\n if (!isCurrentConnection(generation)) throw createInterruptedAuthError();\n setAuthorizationUrl(null);\n const connected = await finalizeConnect(transport, generation);\n if (!connected) throw createInterruptedAuthError();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n if (!isCurrentConnection(generation))\n throw createInterruptedAuthError(error);\n\n await closeTransports();\n setLastError({\n message: error.message,\n });\n setConnectionState(\"error\");\n throw error;\n }\n });\n\n const tryAutoConnect = useEffectEvent(\n async (signal: { cancelled: boolean }) => {\n if (!props.autoConnect) return;\n if (props.auth.type === \"none\") {\n void doConnect();\n return;\n }\n // Static connector token short-circuits the storage read.\n if (props.auth.type === \"bearer\" && props.auth.token) {\n void doConnect();\n return;\n }\n const generation = connectionGenerationRef.current;\n let persisted: Awaited<ReturnType<MCPStorage[\"loadAuthState\"]>>;\n try {\n persisted = await props.storage.loadAuthState(props.id);\n } catch (error) {\n if (signal.cancelled || !isCurrentConnection(generation)) return;\n const message = error instanceof Error ? error.message : String(error);\n setLastError({\n message: `MCP server \"${props.id}\" failed to load saved authentication: ${message}`,\n });\n setConnectionState(\"error\");\n return;\n }\n if (signal.cancelled || !isCurrentConnection(generation)) return;\n if (props.auth.type === \"oauth\") {\n if (!persisted?.tokens) return;\n } else if (!persisted?.token) {\n return;\n }\n const pendingAuthValidation = pendingAuthValidationRef.current;\n if (pendingAuthValidation) {\n await pendingAuthValidation.promise;\n if (signal.cancelled || !isCurrentConnection(generation)) return;\n }\n void doConnect();\n },\n );\n\n // Auto-connect on mount when usable auth exists.\n useEffect(() => {\n const transportCloseQueueRef = props.transportCloseQueueRef;\n const previousDisposal = pendingDisposalRef.current;\n if (previousDisposal) previousDisposal.cancelled = true;\n const pendingDisposal = { cancelled: false };\n pendingDisposalRef.current = pendingDisposal;\n mountedRef.current = true;\n const signal = { cancelled: false };\n void tryAutoConnect(signal);\n return () => {\n mountedRef.current = false;\n signal.cancelled = true;\n const task = transportCloseQueueRef.current.then(async () => {\n await new Promise<void>((resolve) => queueMicrotask(resolve));\n if (pendingDisposal.cancelled) return;\n connectionGenerationRef.current += 1;\n await Promise.all(detachTransports().map(closeTransportSafely));\n });\n transportCloseQueueRef.current = task;\n };\n }, [props.transportCloseQueueRef]);\n\n const state = useMemo<MCPServerState>(\n () => ({\n id: props.id,\n kind: props.kind,\n name: props.name,\n url: props.url,\n icon: props.icon,\n connectionState,\n lastError,\n tools,\n authorizationUrl,\n pendingElicitations,\n }),\n [\n props.id,\n props.kind,\n props.name,\n props.url,\n props.icon,\n connectionState,\n lastError,\n tools,\n authorizationUrl,\n pendingElicitations,\n ],\n );\n\n return {\n getState: () => state,\n connect: doConnect,\n disconnect: doDisconnect,\n remove: async () => {\n await doDisconnect();\n try {\n await clearOAuthProviderAuthState(props.storage, props.id);\n await props.onRemove();\n } catch (err) {\n setLastError({\n message: err instanceof Error ? err.message : String(err),\n });\n throw err;\n }\n },\n callTool: async (name, args) => {\n const client = clientRef.current;\n if (!client) {\n throw new Error(`MCP server \"${props.id}\" is not connected`);\n }\n return await client.callTool({\n name,\n arguments: args as Record<string, unknown> | undefined,\n });\n },\n listResources: async (params) => {\n const client = clientRef.current;\n if (!client) {\n throw new Error(`MCP server \"${props.id}\" is not connected`);\n }\n return await client.listResources(params);\n },\n readResource: async (uri) => {\n const client = clientRef.current;\n if (!client) {\n throw new Error(`MCP server \"${props.id}\" is not connected`);\n }\n return await client.readResource({ uri });\n },\n completeAuth: doCompleteAuth,\n answerElicitation: (\n id: string,\n response: MCPElicitationResponse,\n ): readonly { property: string; message: string }[] | undefined => {\n if (response.action === \"accept\") {\n const entry = elicitationResolversRef.current.get(id);\n if (!entry) return;\n\n if (\n typeof response.content !== \"object\" ||\n response.content === null ||\n Array.isArray(response.content)\n ) {\n const errors = [\n {\n property: \"content\",\n message: \"Response content must be an object.\",\n },\n ];\n setPendingElicitationError(id, {\n message: \"Invalid elicitation content: content.\",\n properties: [\"content\"],\n });\n return errors;\n }\n\n const errors = validateElicitationContent(\n entry.requestedSchema,\n response.content,\n );\n if (errors.length > 0) {\n const properties = [\n ...new Set(errors.map((error) => error.property)),\n ];\n setPendingElicitationError(id, {\n message: `Invalid elicitation content: ${properties.join(\", \")}.`,\n properties,\n });\n return errors;\n }\n\n const result: ElicitResult = {\n action: \"accept\",\n content: response.content as ElicitResult[\"content\"],\n };\n resolvePendingElicitation(id, result);\n return;\n }\n\n resolvePendingElicitation(id, { action: response.action });\n },\n };\n};\n\nconst McpServerResourceInstance = resource(useMcpServerResourceInstance);\n\nexport const McpServerResource = resource(function useMcpServerResource(\n props: McpServerResourceProps,\n): ClientOutput<\"mcpServer\"> {\n const transportCloseQueueRef = useRef(Promise.resolve());\n const dependencies = getConnectionDependencies(props);\n const [connection, setConnection] = useState({ dependencies, generation: 0 });\n let currentConnection = connection;\n if (!areConnectionDependenciesEqual(connection.dependencies, dependencies)) {\n currentConnection = {\n dependencies,\n generation: connection.generation + 1,\n };\n setConnection(currentConnection);\n }\n\n // Storage keys remounts through its optional scopeId rather than object\n // identity, because a defaulted storage element is rebuilt on ordinary\n // renders.\n return useResource(\n withKey(\n currentConnection.generation,\n McpServerResourceInstance({ ...props, transportCloseQueueRef }),\n ),\n );\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"McpServerResource.js","names":["useState","useRef","useEffect","useMemo","useEffectEvent","resource","useResource","withKey","ClientOutput","shallowEqual","Client","StreamableHTTPClientTransport","UnauthorizedError","ClientOptions","ElicitRequest","ElicitResult","StreamableHTTPClientTransportOptions","clearOAuthProviderAuthState","createOAuthProvider","hasUsableOAuthTokens","buildHeaders","assertValidServerId","hasPersistedCredentials","isAuthStateForServerUrl","validateElicitationContent","MCPStorage","MCPAuthConfig","MCPConnectionState","MCPElicitation","MCPElicitationResponse","MCPServerKind","MCPServerState","MCPToolInfo","createMcpId","beginMcpServerRemovalFence","McpServerResourceProps","id","kind","name","url","icon","auth","storage","redirectUri","autoConnect","connectionTimeout","cache","defaultTtlMs","elicitation","onRemove","Promise","McpServerResourceInstanceProps","transportCloseQueueRef","current","getConnectionDependencies","props","authDependencies","type","token","scopeId","scopes","length","authorizationEndpoint","tokenEndpoint","registrationEndpoint","clientId","clientSecret","useMcpServerResourceInstance","connectionState","setConnectionState","tools","setTools","lastError","setLastError","message","authorizationUrl","setAuthorizationUrl","pendingElicitations","setPendingElicitations","clientRef","transportRef","pendingTransportRef","connectionGenerationRef","pendingAuthValidationRef","count","promise","resolve","elicitationResolversRef","Map","result","signal","AbortSignal","onAbort","requestedSchema","pendingDisposalRef","cancelled","mountedRef","closeTransportSafely","transport","close","closeQueuedTransports","transports","task","then","all","map","closePendingTransport","resolvePendingElicitation","entry","get","delete","removeEventListener","filter","setPendingElicitationError","error","NonNullable","has","cancelPendingElicitations","action","detachTransports","pendingTransport","activeTransport","Set","closeTransports","isCurrentConnection","generation","createInterruptedAuthError","cause","Error","undefined","withConnectionTimeout","T","phase","startedAt","timeoutMs","remainingMs","Date","now","timeoutError","timeout","ReturnType","setTimeout","race","_","reject","clearTimeout","unboundAuthMessage","loadAuthState","state","unbound","buildTransport","authProvider","serverId","serverUrl","config","onAuthorizationUrl","toString","URL","headers","transportOpts","requestInit","applyToolsList","list","Array","description","inputSchema","t","info","syncTools","client","options","listPromise","listTools","finalizeConnect","clientOptions","capabilities","listChanged","autoRefresh","debounceMs","onChanged","items","defaultCacheTtlMs","version","setRequestHandler","request","context","params","set","aborted","addEventListener","once","connect","synced","doConnect","err","String","doDisconnect","doCompleteAuth","callbackUrl","validationGeneration","searchParams","pendingAuthValidation","resolvePromise","persisted","finishAuth","connected","tryAutoConnect","loaded","Awaited","previousDisposal","pendingDisposal","queueMicrotask","getState","disconnect","remove","releaseRemovalFence","callTool","args","arguments","Record","listResources","readResource","uri","completeAuth","answerElicitation","response","property","content","isArray","errors","properties","join","McpServerResourceInstance","McpServerResource","useMcpServerResource","dependencies","connection","setConnection","currentConnection"],"sources":["../../src/resources/McpServerResource.ts"],"sourcesContent":["import { useState, useRef, useEffect, useMemo, useEffectEvent } from \"react\";\nimport { resource, useResource, withKey } from \"@assistant-ui/tap\";\nimport type { ClientOutput } from \"@assistant-ui/store\";\nimport { shallowEqual } from \"@assistant-ui/store/internal\";\nimport {\n Client,\n StreamableHTTPClientTransport,\n UnauthorizedError,\n type ClientOptions,\n type ElicitRequest,\n type ElicitResult,\n type StreamableHTTPClientTransportOptions,\n} from \"@modelcontextprotocol/client\";\nimport {\n clearOAuthProviderAuthState,\n createOAuthProvider,\n hasUsableOAuthTokens,\n} from \"../auth/createOAuthProvider\";\nimport { buildHeaders } from \"../auth/buildHeaders\";\nimport { assertValidServerId } from \"../utils/serverId\";\nimport {\n hasPersistedCredentials,\n isAuthStateForServerUrl,\n} from \"../utils/serverUrl\";\nimport { validateElicitationContent } from \"./validateElicitationContent\";\nimport type { MCPStorage } from \"./storage/types\";\nimport type {\n MCPAuthConfig,\n MCPConnectionState,\n MCPElicitation,\n MCPElicitationResponse,\n MCPServerKind,\n MCPServerState,\n MCPToolInfo,\n} from \"../mcp-scope\";\nimport { createMcpId } from \"../utils/createMcpId\";\nimport { beginMcpServerRemovalFence } from \"./McpServerRemovalFence\";\n\nexport type McpServerResourceProps = {\n id: string;\n kind: MCPServerKind;\n name: string;\n url: string;\n icon?: string | undefined;\n auth: MCPAuthConfig;\n storage: MCPStorage;\n redirectUri: string;\n autoConnect: boolean;\n connectionTimeout?: number | undefined;\n cache?: { readonly defaultTtlMs?: number } | undefined;\n readonly elicitation?: boolean;\n onRemove: () => Promise<void>;\n};\n\ntype McpServerResourceInstanceProps = McpServerResourceProps & {\n transportCloseQueueRef: { current: Promise<void> };\n};\n\nexport const getConnectionDependencies = (\n props: McpServerResourceProps,\n): readonly unknown[] => {\n const auth = props.auth;\n const authDependencies =\n auth.type === \"bearer\"\n ? [auth.type, auth.token, props.storage.scopeId]\n : auth.type === \"oauth\"\n ? [\n auth.type,\n auth.scopes?.length,\n ...(auth.scopes ?? []),\n auth.authorizationEndpoint,\n auth.tokenEndpoint,\n auth.registrationEndpoint,\n auth.clientId,\n auth.clientSecret,\n props.storage.scopeId,\n ]\n : [auth.type];\n\n return [\n props.id,\n props.url,\n ...authDependencies,\n props.redirectUri,\n props.cache?.defaultTtlMs,\n props.elicitation !== false,\n ];\n};\n\nconst useMcpServerResourceInstance = (\n props: McpServerResourceInstanceProps,\n): ClientOutput<\"mcpServer\"> => {\n assertValidServerId(props.id);\n const [connectionState, setConnectionState] =\n useState<MCPConnectionState>(\"disconnected\");\n const [tools, setTools] = useState<MCPToolInfo[]>([]);\n const [lastError, setLastError] = useState<{ message: string } | null>(null);\n const [authorizationUrl, setAuthorizationUrl] = useState<string | null>(null);\n const [pendingElicitations, setPendingElicitations] = useState<\n MCPElicitation[]\n >([]);\n\n const clientRef = useRef<Client | null>(null);\n const transportRef = useRef<StreamableHTTPClientTransport | null>(null);\n const pendingTransportRef = useRef<StreamableHTTPClientTransport | null>(\n null,\n );\n const connectionGenerationRef = useRef(0);\n const pendingAuthValidationRef = useRef<{\n count: number;\n promise: Promise<void>;\n resolve: () => void;\n } | null>(null);\n const elicitationResolversRef = useRef(\n new Map<\n string,\n {\n resolve: (result: ElicitResult) => void;\n signal: AbortSignal;\n onAbort: () => void;\n requestedSchema: unknown;\n }\n >(),\n );\n const pendingDisposalRef = useRef<{ cancelled: boolean } | null>(null);\n const mountedRef = useRef(true);\n\n const closeTransportSafely = async (\n transport: StreamableHTTPClientTransport,\n ): Promise<void> => {\n try {\n await transport.close();\n } catch {\n // ignore close errors\n }\n };\n\n const closeQueuedTransports = (\n transports: StreamableHTTPClientTransport[],\n ): Promise<void> => {\n const task = props.transportCloseQueueRef.current.then(async () => {\n await Promise.all(transports.map(closeTransportSafely));\n });\n props.transportCloseQueueRef.current = task;\n return task;\n };\n\n const closePendingTransport = async () => {\n const transport = pendingTransportRef.current;\n pendingTransportRef.current = null;\n await closeQueuedTransports(transport ? [transport] : []);\n };\n\n const resolvePendingElicitation = (id: string, result: ElicitResult) => {\n const entry = elicitationResolversRef.current.get(id);\n if (!entry) return false;\n elicitationResolversRef.current.delete(id);\n entry.signal.removeEventListener(\"abort\", entry.onAbort);\n setPendingElicitations((current) =>\n current.filter((elicitation) => elicitation.id !== id),\n );\n entry.resolve(result);\n return true;\n };\n\n const setPendingElicitationError = (\n id: string,\n error: NonNullable<MCPElicitation[\"error\"]>,\n ) => {\n if (!elicitationResolversRef.current.has(id)) return false;\n setPendingElicitations((current) =>\n current.map((elicitation) =>\n elicitation.id === id ? { ...elicitation, error } : elicitation,\n ),\n );\n return true;\n };\n\n const cancelPendingElicitations = () => {\n for (const [id] of elicitationResolversRef.current) {\n resolvePendingElicitation(id, { action: \"cancel\" });\n }\n };\n\n const detachTransports = () => {\n cancelPendingElicitations();\n const pendingTransport = pendingTransportRef.current;\n const activeTransport = transportRef.current;\n pendingTransportRef.current = null;\n transportRef.current = null;\n clientRef.current = null;\n\n return [\n ...new Set(\n [pendingTransport, activeTransport].filter(\n (transport): transport is StreamableHTTPClientTransport =>\n transport !== null,\n ),\n ),\n ];\n };\n\n const closeTransports = async () => {\n await closeQueuedTransports(detachTransports());\n };\n\n const isCurrentConnection = (generation: number) =>\n mountedRef.current && generation === connectionGenerationRef.current;\n\n const createInterruptedAuthError = (cause?: unknown) =>\n new Error(\n `MCP server \"${props.id}\" authorization was interrupted before completion.`,\n cause === undefined ? undefined : { cause },\n );\n\n const withConnectionTimeout = useEffectEvent(\n async <T>(\n promise: Promise<T>,\n phase: \"connecting\" | \"listing tools\",\n startedAt: number,\n ): Promise<T> => {\n const timeoutMs = props.connectionTimeout;\n if (timeoutMs === undefined) return await promise;\n const remainingMs = timeoutMs - (Date.now() - startedAt);\n const timeoutError = () =>\n new Error(\n `MCP server \"${props.id}\" timed out while ${phase} after ${timeoutMs}ms.`,\n );\n if (remainingMs <= 0) throw timeoutError();\n\n let timeout: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timeout = setTimeout(() => reject(timeoutError()), remainingMs);\n }),\n ]);\n } finally {\n if (timeout !== undefined) clearTimeout(timeout);\n }\n },\n );\n\n const unboundAuthMessage = () =>\n `MCP server \"${props.id}\" has saved authentication for a different URL. Authenticate again to connect to ${props.url}.`;\n\n const loadAuthState = useEffectEvent(async () => {\n const state = await props.storage.loadAuthState(props.id);\n if (isAuthStateForServerUrl(state, props.url)) {\n return { state, unbound: false };\n }\n return { state: null, unbound: hasPersistedCredentials(state) };\n });\n\n const buildTransport = useEffectEvent(\n async (): Promise<StreamableHTTPClientTransport> => {\n if (props.auth.type === \"oauth\") {\n const authProvider = createOAuthProvider({\n serverId: props.id,\n serverUrl: props.url,\n config: props.auth,\n storage: props.storage,\n redirectUri: props.redirectUri,\n onAuthorizationUrl: (url) => setAuthorizationUrl(url.toString()),\n });\n return new StreamableHTTPClientTransport(new URL(props.url), {\n authProvider,\n });\n }\n if (props.auth.type === \"bearer\") {\n const { state, unbound } = await loadAuthState();\n const headers = buildHeaders(props.auth, state);\n if (!headers && unbound) throw new Error(unboundAuthMessage());\n const transportOpts: StreamableHTTPClientTransportOptions = {};\n if (headers) transportOpts.requestInit = { headers };\n return new StreamableHTTPClientTransport(\n new URL(props.url),\n transportOpts,\n );\n }\n return new StreamableHTTPClientTransport(new URL(props.url));\n },\n );\n\n const applyToolsList = useEffectEvent(\n (list: {\n tools: Array<{\n name: string;\n description?: string | undefined;\n inputSchema: unknown;\n }>;\n }) => {\n setTools(\n list.tools.map((t) => {\n const info: MCPToolInfo = {\n name: t.name,\n inputSchema: t.inputSchema,\n };\n if (t.description !== undefined) info.description = t.description;\n return info;\n }),\n );\n },\n );\n\n const syncTools = useEffectEvent(\n async (\n client: Client,\n generation: number,\n options?: { startedAt?: number | undefined },\n ): Promise<boolean> => {\n const listPromise = client.listTools();\n const list =\n options?.startedAt === undefined\n ? await listPromise\n : await withConnectionTimeout(\n listPromise,\n \"listing tools\",\n options.startedAt,\n );\n if (!isCurrentConnection(generation)) return false;\n applyToolsList(list);\n return true;\n },\n );\n\n const finalizeConnect = useEffectEvent(\n async (\n transport: StreamableHTTPClientTransport,\n generation: number,\n ): Promise<boolean> => {\n const clientOptions: ClientOptions = {\n ...(props.elicitation === false\n ? {}\n : { capabilities: { elicitation: {} } }),\n listChanged: {\n tools: {\n autoRefresh: true,\n debounceMs: 300,\n onChanged: (error, items) => {\n if (!isCurrentConnection(generation)) return;\n if (error !== null) {\n setLastError({ message: error.message });\n return;\n }\n if (items === null) return;\n setLastError(null);\n applyToolsList({ tools: items });\n },\n },\n },\n };\n if (props.cache?.defaultTtlMs !== undefined) {\n clientOptions.defaultCacheTtlMs = props.cache.defaultTtlMs;\n }\n const client = new Client(\n {\n name: \"assistant-ui-mcp\",\n version: \"0.0.0\",\n },\n clientOptions,\n );\n if (props.elicitation !== false) {\n client.setRequestHandler(\n \"elicitation/create\",\n (request: ElicitRequest, context): Promise<ElicitResult> => {\n if (!isCurrentConnection(generation)) {\n return Promise.resolve({ action: \"cancel\" });\n }\n if (!(\"requestedSchema\" in request.params)) {\n return Promise.resolve({ action: \"cancel\" });\n }\n const { message, requestedSchema } = request.params;\n\n const id = createMcpId();\n const promise = new Promise<ElicitResult>((resolve) => {\n const onAbort = () => {\n resolvePendingElicitation(id, { action: \"cancel\" });\n };\n elicitationResolversRef.current.set(id, {\n resolve,\n signal: context.signal,\n onAbort,\n requestedSchema,\n });\n });\n setPendingElicitations((current) => [\n ...current,\n {\n id,\n message,\n requestedSchema,\n },\n ]);\n const entry = elicitationResolversRef.current.get(id);\n if (entry) {\n if (context.signal.aborted) {\n entry.onAbort();\n } else {\n context.signal.addEventListener(\"abort\", entry.onAbort, {\n once: true,\n });\n }\n }\n return promise;\n },\n );\n }\n const startedAt = Date.now();\n await withConnectionTimeout(\n client.connect(transport),\n \"connecting\",\n startedAt,\n );\n if (!isCurrentConnection(generation)) return false;\n // Defer ref assignment until listTools() also succeeds — otherwise a\n // post-connect failure leaves stale refs that `callTool()` would\n // happily walk into, producing confusing SDK errors instead of\n // \"not connected\".\n const synced = await syncTools(client, generation, { startedAt });\n if (!synced) return false;\n\n pendingTransportRef.current = null;\n clientRef.current = client;\n transportRef.current = transport;\n setConnectionState(\"connected\");\n return true;\n },\n );\n\n const doConnect = useEffectEvent(async () => {\n const generation = ++connectionGenerationRef.current;\n // Close any prior transport/client so a re-connect doesn't leak.\n await closeTransports();\n if (!isCurrentConnection(generation)) return;\n\n setConnectionState(\"connecting\");\n setLastError(null);\n setAuthorizationUrl(null);\n // Clear tools so a reconnect (error → connect, or authRequired\n // → connect) doesn't expose the previous attempt's tool list.\n setTools([]);\n let transport: StreamableHTTPClientTransport | null = null;\n try {\n transport = await buildTransport();\n if (!isCurrentConnection(generation)) {\n await closeQueuedTransports([transport]);\n return;\n }\n pendingTransportRef.current = transport;\n // Don't assign to transportRef until connect succeeds — otherwise a\n // failed `listTools()` leaves an orphaned transport that future\n // doConnect / doDisconnect calls treat as live.\n await finalizeConnect(transport, generation);\n } catch (err) {\n if (!isCurrentConnection(generation)) return;\n\n if (err instanceof UnauthorizedError) {\n // OAuth: keep the transport alive so completeAuth can call\n // finishAuth on it. Closing it before storing would leave a\n // closed transport on transportRef.\n pendingTransportRef.current = null;\n transportRef.current = transport;\n setConnectionState(\"authRequired\");\n } else {\n cancelPendingElicitations();\n if (transport) {\n pendingTransportRef.current = null;\n await closeQueuedTransports([transport]);\n }\n setLastError({\n message: err instanceof Error ? err.message : String(err),\n });\n setConnectionState(\"error\");\n }\n }\n });\n\n const doDisconnect = useEffectEvent(async () => {\n connectionGenerationRef.current += 1;\n setTools([]);\n setAuthorizationUrl(null);\n setConnectionState(\"disconnected\");\n await closeTransports();\n });\n\n const doCompleteAuth = useEffectEvent(async (callbackUrl: string) => {\n const validationGeneration = connectionGenerationRef.current;\n const url = new URL(callbackUrl);\n const state = url.searchParams.get(\"state\");\n if (!state) throw new Error('missing \"state\" parameter');\n let pendingAuthValidation = pendingAuthValidationRef.current;\n if (!pendingAuthValidation) {\n let resolve!: () => void;\n const promise = new Promise<void>((resolvePromise) => {\n resolve = resolvePromise;\n });\n pendingAuthValidation = { count: 0, promise, resolve };\n pendingAuthValidationRef.current = pendingAuthValidation;\n }\n pendingAuthValidation.count += 1;\n try {\n const { state: persisted, unbound } = await loadAuthState();\n if (!isCurrentConnection(validationGeneration)) {\n throw createInterruptedAuthError();\n }\n if (unbound) throw new Error(unboundAuthMessage());\n if (!persisted?.state) {\n throw new Error(\n \"no pending OAuth authorization request for this server\",\n );\n }\n if (persisted.state !== state) {\n throw new Error(\"OAuth state does not match the authorization request\");\n }\n if (!url.searchParams.get(\"code\") && !url.searchParams.get(\"error\")) {\n throw new Error(\"missing authorization code in callback URL\");\n }\n } finally {\n pendingAuthValidation.count -= 1;\n if (pendingAuthValidation.count === 0) {\n pendingAuthValidationRef.current = null;\n pendingAuthValidation.resolve();\n }\n }\n\n // Claim the generation before a waiting auto-connect can resume.\n const generation = ++connectionGenerationRef.current;\n cancelPendingElicitations();\n await closePendingTransport();\n if (!isCurrentConnection(generation)) throw createInterruptedAuthError();\n\n setConnectionState(\"authPending\");\n setLastError(null);\n try {\n let transport = transportRef.current;\n if (!transport) {\n transport = await buildTransport();\n if (!isCurrentConnection(generation)) {\n await closeQueuedTransports([transport]);\n throw createInterruptedAuthError();\n }\n }\n transportRef.current = null;\n clientRef.current = null;\n pendingTransportRef.current = transport;\n await transport.finishAuth(url.searchParams);\n if (!isCurrentConnection(generation)) throw createInterruptedAuthError();\n setAuthorizationUrl(null);\n const connected = await finalizeConnect(transport, generation);\n if (!connected) throw createInterruptedAuthError();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n if (!isCurrentConnection(generation))\n throw createInterruptedAuthError(error);\n\n await closeTransports();\n setLastError({\n message: error.message,\n });\n setConnectionState(\"error\");\n throw error;\n }\n });\n\n const tryAutoConnect = useEffectEvent(\n async (signal: { cancelled: boolean }) => {\n if (!props.autoConnect) return;\n if (props.auth.type === \"none\") {\n void doConnect();\n return;\n }\n // Static connector token short-circuits the storage read.\n if (props.auth.type === \"bearer\" && props.auth.token) {\n void doConnect();\n return;\n }\n const generation = connectionGenerationRef.current;\n let loaded: Awaited<ReturnType<typeof loadAuthState>>;\n try {\n loaded = await loadAuthState();\n } catch (error) {\n if (signal.cancelled || !isCurrentConnection(generation)) return;\n const message = error instanceof Error ? error.message : String(error);\n setLastError({\n message: `MCP server \"${props.id}\" failed to load saved authentication: ${message}`,\n });\n setConnectionState(\"error\");\n return;\n }\n if (signal.cancelled || !isCurrentConnection(generation)) return;\n if (loaded.unbound) {\n setLastError({ message: unboundAuthMessage() });\n return;\n }\n const persisted = loaded.state;\n if (props.auth.type === \"oauth\") {\n if (!hasUsableOAuthTokens(persisted, props.auth)) return;\n } else if (!persisted?.token) {\n return;\n }\n const pendingAuthValidation = pendingAuthValidationRef.current;\n if (pendingAuthValidation) {\n await pendingAuthValidation.promise;\n if (signal.cancelled || !isCurrentConnection(generation)) return;\n }\n void doConnect();\n },\n );\n\n // Auto-connect on mount when usable auth exists.\n useEffect(() => {\n const transportCloseQueueRef = props.transportCloseQueueRef;\n const previousDisposal = pendingDisposalRef.current;\n if (previousDisposal) previousDisposal.cancelled = true;\n const pendingDisposal = { cancelled: false };\n pendingDisposalRef.current = pendingDisposal;\n mountedRef.current = true;\n const signal = { cancelled: false };\n // Auto-connect opens a transport, so it belongs to the same effect as the\n // disposal that closes it.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n void tryAutoConnect(signal);\n return () => {\n mountedRef.current = false;\n signal.cancelled = true;\n const task = transportCloseQueueRef.current.then(async () => {\n await new Promise<void>((resolve) => queueMicrotask(resolve));\n if (pendingDisposal.cancelled) return;\n connectionGenerationRef.current += 1;\n await Promise.all(detachTransports().map(closeTransportSafely));\n });\n transportCloseQueueRef.current = task;\n };\n }, [props.transportCloseQueueRef]);\n\n const state = useMemo<MCPServerState>(\n () => ({\n id: props.id,\n kind: props.kind,\n name: props.name,\n url: props.url,\n icon: props.icon,\n connectionState,\n lastError,\n tools,\n authorizationUrl,\n pendingElicitations,\n }),\n [\n props.id,\n props.kind,\n props.name,\n props.url,\n props.icon,\n connectionState,\n lastError,\n tools,\n authorizationUrl,\n pendingElicitations,\n ],\n );\n\n return {\n getState: () => state,\n connect: doConnect,\n disconnect: doDisconnect,\n remove: async () => {\n const releaseRemovalFence = beginMcpServerRemovalFence(props);\n try {\n await doDisconnect();\n await clearOAuthProviderAuthState(props.storage, props.id);\n await props.onRemove();\n } catch (err) {\n releaseRemovalFence?.();\n setLastError({\n message: err instanceof Error ? err.message : String(err),\n });\n throw err;\n }\n releaseRemovalFence?.();\n },\n callTool: async (name, args) => {\n const client = clientRef.current;\n if (!client) {\n throw new Error(`MCP server \"${props.id}\" is not connected`);\n }\n return await client.callTool({\n name,\n arguments: args as Record<string, unknown> | undefined,\n });\n },\n listResources: async (params) => {\n const client = clientRef.current;\n if (!client) {\n throw new Error(`MCP server \"${props.id}\" is not connected`);\n }\n return await client.listResources(params);\n },\n readResource: async (uri) => {\n const client = clientRef.current;\n if (!client) {\n throw new Error(`MCP server \"${props.id}\" is not connected`);\n }\n return await client.readResource({ uri });\n },\n completeAuth: doCompleteAuth,\n answerElicitation: (\n id: string,\n response: MCPElicitationResponse,\n ): readonly { property: string; message: string }[] | undefined => {\n if (response.action === \"accept\") {\n const entry = elicitationResolversRef.current.get(id);\n if (!entry) return;\n\n if (\n typeof response.content !== \"object\" ||\n response.content === null ||\n Array.isArray(response.content)\n ) {\n const errors = [\n {\n property: \"content\",\n message: \"Response content must be an object.\",\n },\n ];\n setPendingElicitationError(id, {\n message: \"Invalid elicitation content: content.\",\n properties: [\"content\"],\n });\n return errors;\n }\n\n const errors = validateElicitationContent(\n entry.requestedSchema,\n response.content,\n );\n if (errors.length > 0) {\n const properties = [\n ...new Set(errors.map((error) => error.property)),\n ];\n setPendingElicitationError(id, {\n message: `Invalid elicitation content: ${properties.join(\", \")}.`,\n properties,\n });\n return errors;\n }\n\n const result: ElicitResult = {\n action: \"accept\",\n content: response.content as ElicitResult[\"content\"],\n };\n resolvePendingElicitation(id, result);\n return;\n }\n\n resolvePendingElicitation(id, { action: response.action });\n },\n };\n};\n\nconst McpServerResourceInstance = resource(useMcpServerResourceInstance);\n\nexport const McpServerResource = resource(function useMcpServerResource(\n props: McpServerResourceProps,\n): ClientOutput<\"mcpServer\"> {\n const transportCloseQueueRef = useRef(Promise.resolve());\n const dependencies = getConnectionDependencies(props);\n const [connection, setConnection] = useState({ dependencies, generation: 0 });\n let currentConnection = connection;\n if (!shallowEqual(connection.dependencies, dependencies)) {\n currentConnection = {\n dependencies,\n generation: connection.generation + 1,\n };\n setConnection(currentConnection);\n }\n\n // Storage keys remounts through its optional scopeId rather than object\n // identity, because a defaulted storage element is rebuilt on ordinary\n // renders.\n return useResource(\n withKey(\n currentConnection.generation,\n McpServerResourceInstance({ ...props, transportCloseQueueRef }),\n ),\n );\n});\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpLocalStorage.d.ts","names":[],"sources":["../../../src/resources/storage/McpLocalStorage.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"McpLocalStorage.d.ts","names":[],"sources":["../../../src/resources/storage/McpLocalStorage.ts"],"mappings":";;;;KAcY;;EAEV;;EAEA,UAAU;;;;;;;EAOV;;cAiGW,+BACX,mBACC;cAiFU,8BACX,mBACC;cAiGU,6CAAe,SAAA,aAAA,OAAA"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assertValidServerId } from "../../utils/serverId.js";
|
|
2
|
+
import { normalizeMcpServerUrl } from "../../utils/serverUrl.js";
|
|
2
3
|
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
3
4
|
import "@assistant-ui/tap/react-shim";
|
|
4
5
|
import { resource } from "@assistant-ui/tap";
|
|
@@ -67,7 +68,17 @@ const isSecureNetworkUrl = (value) => {
|
|
|
67
68
|
if (!isNonEmptyString(value)) return false;
|
|
68
69
|
try {
|
|
69
70
|
const url = new URL(value);
|
|
70
|
-
|
|
71
|
+
const isIpv4Loopback = /^127(?:\.\d{1,3}){3}$/.test(url.hostname);
|
|
72
|
+
return url.protocol === "https:" || url.protocol === "http:" && (url.hostname === "localhost" || url.hostname.endsWith(".localhost") || isIpv4Loopback || url.hostname === "[::1]");
|
|
73
|
+
} catch {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const isMcpServerUrl = (value) => {
|
|
78
|
+
if (typeof value !== "string") return false;
|
|
79
|
+
try {
|
|
80
|
+
const url = new URL(value);
|
|
81
|
+
return url.protocol === "https:" || url.protocol === "http:";
|
|
71
82
|
} catch {
|
|
72
83
|
return false;
|
|
73
84
|
}
|
|
@@ -84,14 +95,18 @@ const normalizeDiscoveryState = (value) => {
|
|
|
84
95
|
};
|
|
85
96
|
const normalizePersistedAuthState = (value) => {
|
|
86
97
|
if (!isRecord(value)) return null;
|
|
98
|
+
if ("serverUrl" in value && !isMcpServerUrl(value.serverUrl)) return null;
|
|
87
99
|
const state = {};
|
|
100
|
+
if (isMcpServerUrl(value.serverUrl)) state.serverUrl = normalizeMcpServerUrl(value.serverUrl);
|
|
88
101
|
if (isNonEmptyString(value.token)) state.token = value.token;
|
|
102
|
+
if (isNonEmptyString(value.tokensClientId)) state.tokensClientId = value.tokensClientId;
|
|
89
103
|
if (isNonEmptyString(value.codeVerifier)) state.codeVerifier = value.codeVerifier;
|
|
90
104
|
if (isNonEmptyString(value.state)) state.state = value.state;
|
|
91
105
|
const tokens = normalizeOAuthTokens(value.tokens);
|
|
92
106
|
if (tokens) state.tokens = tokens;
|
|
93
107
|
const clientInformation = normalizeClientInformation(value.clientInformation);
|
|
94
108
|
if (clientInformation) state.clientInformation = clientInformation;
|
|
109
|
+
if (value.clientInformationSource === "registered") state.clientInformationSource = value.clientInformationSource;
|
|
95
110
|
const discoveryState = normalizeDiscoveryState(value.discoveryState);
|
|
96
111
|
if (discoveryState) state.discoveryState = discoveryState;
|
|
97
112
|
return Object.keys(state).length > 0 ? state : null;
|