@assistant-ui/react-mcp 0.1.15 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/auth/createOAuthProvider.d.ts +19 -2
  2. package/dist/auth/createOAuthProvider.d.ts.map +1 -1
  3. package/dist/auth/createOAuthProvider.js +197 -35
  4. package/dist/auth/createOAuthProvider.js.map +1 -1
  5. package/dist/auth/types.d.ts +6 -1
  6. package/dist/auth/types.d.ts.map +1 -1
  7. package/dist/hooks/useMcpOAuthCallback.d.ts.map +1 -1
  8. package/dist/hooks/useMcpOAuthCallback.js +5 -6
  9. package/dist/hooks/useMcpOAuthCallback.js.map +1 -1
  10. package/dist/resources/McpManagerResource.d.ts.map +1 -1
  11. package/dist/resources/McpManagerResource.js +2 -1
  12. package/dist/resources/McpManagerResource.js.map +1 -1
  13. package/dist/resources/McpServerResource.d.ts +2 -1
  14. package/dist/resources/McpServerResource.d.ts.map +1 -1
  15. package/dist/resources/McpServerResource.js +79 -19
  16. package/dist/resources/McpServerResource.js.map +1 -1
  17. package/dist/resources/storage/McpLocalStorage.d.ts +7 -0
  18. package/dist/resources/storage/McpLocalStorage.d.ts.map +1 -1
  19. package/dist/resources/storage/McpLocalStorage.js +146 -37
  20. package/dist/resources/storage/McpLocalStorage.js.map +1 -1
  21. package/dist/resources/storage/McpMemoryStorage.d.ts.map +1 -1
  22. package/dist/resources/storage/McpMemoryStorage.js +9 -6
  23. package/dist/resources/storage/McpMemoryStorage.js.map +1 -1
  24. package/dist/resources/storage/types.d.ts +12 -0
  25. package/dist/resources/storage/types.d.ts.map +1 -1
  26. package/dist/utils/serverUrl.d.ts +8 -0
  27. package/dist/utils/serverUrl.d.ts.map +1 -0
  28. package/dist/utils/serverUrl.js +15 -0
  29. package/dist/utils/serverUrl.js.map +1 -0
  30. package/package.json +7 -7
  31. package/src/auth/createOAuthProvider.test.ts +919 -5
  32. package/src/auth/createOAuthProvider.ts +328 -42
  33. package/src/auth/types.ts +6 -1
  34. package/src/hooks/useMcpOAuthCallback.test.ts +74 -1
  35. package/src/hooks/useMcpOAuthCallback.tsx +11 -8
  36. package/src/resources/McpManagerResource.ts +5 -1
  37. package/src/resources/McpServerResource.test.ts +612 -16
  38. package/src/resources/McpServerResource.ts +95 -23
  39. package/src/resources/storage/McpLocalStorage.test.ts +97 -1
  40. package/src/resources/storage/McpLocalStorage.ts +90 -47
  41. package/src/resources/storage/McpMemoryStorage.test.ts +99 -0
  42. package/src/resources/storage/McpMemoryStorage.ts +23 -17
  43. package/src/resources/storage/types.ts +12 -0
  44. package/src/utils/serverUrl.test.ts +66 -0
  45. 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","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","clearAuthState","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 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 storage.clearAuthState(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":";;;;;;;;;;;AAkCA,SAAS+B,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,uBAAuB,OAC3Bb,SACAc,YACG;CACH,IAAI;EACF,MAAMd,QAAQe,kBAAkBD,OAAO;CACzC,SAASH,OAAO;EACdF,2BAA2B,QAAQE,KAAK;CAC1C;AACF;AAEA,MAAMK,yBAAwBC,UAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAG5B,MAAApB,aAAmBkB,MAAKlB,cAALS;CACnB,MAAAN,cAAoBe,MAAKf,eAAL;CAA0B,IAAAkB;CAAA,IAAAF,EAAA,OAAAD,MAAAhB,kBAAA;EAC1BmB,KAAAH,MAAKhB,oBAAqBG,mBAAmB;EAACc,EAAA,KAAAD,MAAAhB;EAAAiB,EAAA,KAAAE;CAAA,OAAAA,KAAAF,EAAA;CAAlE,MAAAG,cAAoBD;CACpB,MAAAjB,oBAA0Bc,MAAKd;CAAmB,IAAAmB;CAAA,IAAAJ,EAAA,OAAAD,MAAAjB,SAAA;EAE3BsB,KAAAL,MAAKjB,WAAYV,gBAAgB;EAAC4B,EAAA,KAAAD,MAAAjB;EAAAkB,EAAA,KAAAI;CAAA,OAAAA,KAAAJ,EAAA;CACzD,MAAAlB,UAAgBtB,YAAY6C,EAAc;CAAE,IAAAC;CAAA,IAAAN,EAAA,OAAAO,OAAAC,IAAA,2BAAA,GAAA;EAG1CF,KAAA,CAAA;EAAEN,EAAA,KAAAM;CAAA,OAAAA,KAAAN,EAAA;CADJ,MAAA,CAAAS,eAAAC,oBAA0CvD,SACxCmD,EACF;CACA,MAAA,CAAAK,YAAAC,iBAAoCzD,SAAS,KAAK;CAElD,MAAA0D,cAAoBtD,OAAO,KAAK;CAChC,MAAAuD,aAAmBvD,OAAOuB,OAAO;CAAE,IAAAiC;CAAA,IAAAf,EAAA,OAAAO,OAAAC,IAAA,2BAAA,GAAA;EACAO,KAAAC,QAAOC,QAAS;EAACjB,EAAA,KAAAe;CAAA,OAAAA,KAAAf,EAAA;CAApD,MAAAkB,sBAA4B3D,OAAOwD,EAAiB;CAAE,IAAAI;CAAA,IAAAC;CAAA,IAAApB,EAAA,OAAAlB,SAAA;EAE5CqC,WAAA;GACRL,WAAUO,UAAWvC;EAAH;EACjBsC,KAAA,CAACtC,OAAO;EAACkB,EAAA,KAAAlB;EAAAkB,EAAA,KAAAmB;EAAAnB,EAAA,KAAAoB;CAAA,OAAA;EAAAD,KAAAnB,EAAA;EAAAoB,KAAApB,EAAA;CAAA;CAFZ5C,UAAU+D,IAEPC,EAAS;CAAC,IAAAE;CAAA,IAAAtB,EAAA,OAAAlB,SAAA;EAEkBwC,KAAA,OAAAC,WAAA;GAC7B,MAAAC,qBAAqB;IACnB,IAAI,CAACD,OAAME,WAAU;KACnBZ,YAAWQ,UAAW;KACtBT,cAAc,IAAI;IAAC;GACpB;GAGChB,IAAAA;GACJ,IAAA;IACEA,UAAUA,MAAMd,QAAO4C,kBAAmB;GAAnC,SAAAC,IAAA;IACAlC,MAAAA,QAAAA;IACP,IAAI,CAAC8B,OAAME,WACTlC,2BAA2B,QAAQE,KAAK;IAE1C+B,aAAa;IAAC;GAAA;GAIhB,IAAID,OAAME,WAAU;GAIpBf,kBAAiBkB,SAAA;IACf,IAAIA,KAAIC,WAAY,GAAC,OAASjC;IAC9B,MAAAkC,eAAqB,IAAIC,IAAInC,QAAOoC,IAAKC,KAAW,CAAC;IAAE,OAChD,CAAA,GAAIrC,SAAO,GAAKgC,KAAIM,QAAQC,QAAO,CAACL,aAAYM,IAAKC,IAACC,EAAG,CAAC,CAAC;GAAC,CACpE;GACDd,aAAa;EAAC;EACfxB,EAAA,KAAAlB;EAAAkB,EAAA,MAAAsB;CAAA,OAAAA,KAAAtB,EAAA;CA7BD,MAAAuC,UAAgBjF,eAAegE,EA6B9B;CAAE,IAAAK;CAAA,IAAA3B,EAAA,QAAAuC,SAAA;EAEOZ,WAAA;GACR,MAAAa,WAAe,EAAAf,WAAa,MAAM;GAC7Bc,QAAQhB,QAAM;GAAC,aACb;IACLA,SAAME,YAAa;GAAH;EACjB;EACFzB,EAAA,MAAAuC;EAAAvC,EAAA,MAAA2B;CAAA,OAAAA,KAAA3B,EAAA;CAAA,IAAAyC;CAAA,IAAAzC,EAAA,QAAAO,OAAAC,IAAA,2BAAA,GAAA;EAAEiC,KAAA,CAAA;EAAEzC,EAAA,MAAAyC;CAAA,OAAAA,KAAAzC,EAAA;CANL5C,UAAUuE,IAMPc,EAAE;CAAC,IAAAC;CAAA,IAAAC;CAAA,IAAA3C,EAAA,QAAAS,eAAA;EAEIkC,WAAA;GACR,IAAI,CAAC9B,YAAWQ,SAAQ;GACxB,MAAAuB,gBAAsB9B,WAAUO;GAChCH,oBAAmBG,UAAWH,oBAAmBG,QAAQwB,WACvDlD,qBAAqBiD,eAAenC,aAAa,CACnD;EAF2B;EAG1BiC,MAAA,CAACjC,aAAa;EAACT,EAAA,MAAAS;EAAAT,EAAA,MAAA0C;EAAA1C,EAAA,MAAA2C;CAAA,OAAA;EAAAD,MAAA1C,EAAA;EAAA2C,KAAA3C,EAAA;CAAA;CANlB5C,UAAUuF,IAMPD,GAAe;CAGhBnE,sBAAsB,CAAA,GACjBM,WAAUmD,IAAKc,MAAW,GAAC,GAC3BrC,cAAauB,IAAKe,MAAW,CAAC,CAClC;CAAC,IAAAC;CAAA,IAAAhD,EAAA,QAAAhB,eAAAgB,EAAA,QAAAf,qBAAAe,EAAA,QAAAnB,cAAAmB,EAAA,QAAAS,iBAAAT,EAAA,QAAAG,eAAAH,EAAA,QAAAlB,SAAA;EAAA,IAAAmE;EAAA,IAAAjD,EAAA,QAAAhB,eAAAgB,EAAA,QAAAf,qBAAAe,EAAA,QAAAG,eAAAH,EAAA,QAAAlB,SAAA;GAEuCmE,OAAAC,QACvCxF,QACEyF,IAACb,IACDnE,kBAAkB;IAAAmE,IACZa,IAACb;IAAGc,MACF;IAAWC,MACXF,IAACE;IAAKC,KACPH,IAACG;IAAIC,MACJJ,IAACI;IAAKC,MACNL,IAACK;IAAK1E;IAAAqB;IAAAnB;IAAAC,mBAIOkE,IAAClE,qBAADA;IAAwC,GACvDkE,IAACM,UAAWC,KAAAA,IAAZ,EAAAD,OAAiCN,IAACM,MAAY,IAA9C,CAA8C;IAAC,GAC/CN,IAACQ,gBAAiBD,KAAAA,IAAlB,EAAAC,aACeR,IAACQ,YACf,IAFD,CAEC;IAACC,UACIC;GAGZ,CAAC,CACH;GAAC7D,EAAA,MAAAhB;GAAAgB,EAAA,MAAAf;GAAAe,EAAA,MAAAG;GAAAH,EAAA,MAAAlB;GAAAkB,EAAA,MAAAiD;EAAA,OAAAA,MAAAjD,EAAA;EAtBH,MAAA8D,oBAA0BjF,WAAUmD,IAAKiB,GAuBzC;EAAE,IAAAc;EAAA,IAAA/D,EAAA,QAAAhB,eAAAgB,EAAA,QAAAf,qBAAAe,EAAA,QAAAG,eAAAH,EAAA,QAAAlB,SAAA;GACuCiF,OAAAC,QACvCtG,QACEuG,IAAC3B,IACDnE,kBAAkB;IAAAmE,IACZ2B,IAAC3B;IAAGc,MACF;IAAQC,MACRY,IAACZ;IAAKC,KACPW,IAACX;IAAIE,MACJS,IAACT;IAAK1E;IAAAqB;IAAAnB;IAAAC,mBAIOgF,IAAChF,qBAADA;IAAwC,GACvDgF,IAACR,UAAWC,KAAAA,IAAZ,EAAAD,OAAiCQ,IAACR,MAAY,IAA9C,CAA8C;IAAC,GAC/CQ,IAACN,gBAAiBD,KAAAA,IAAlB,EAAAC,aACeM,IAACN,YACf,IAFD,CAEC;IAACC,UACI,YAAA;KACRlD,kBAAiBwD,WAAUtC,OAAIM,QAAQiC,MAAOA,EAAC7B,OAAQ2B,IAAC3B,EAAG,CAAC;IAAC;GAEjE,CAAC,CACH;GAACtC,EAAA,MAAAhB;GAAAgB,EAAA,MAAAf;GAAAe,EAAA,MAAAG;GAAAH,EAAA,MAAAlB;GAAAkB,EAAA,MAAA+D;EAAA,OAAAA,MAAA/D,EAAA;EArBH,MAAAoE,iBAAuB3D,cAAauB,IAAK+B,GAsBzC;EACOf,MAAA,CAAA,GAAIc,mBAAiB,GAAKM,cAAc;EAACpE,EAAA,MAAAhB;EAAAgB,EAAA,MAAAf;EAAAe,EAAA,MAAAnB;EAAAmB,EAAA,MAAAS;EAAAT,EAAA,MAAAG;EAAAH,EAAA,MAAAlB;EAAAkB,EAAA,MAAAgD;CAAA,OAAAA,MAAAhD,EAAA;CAUlD,MAAAsE,SAAe3G,gBAAgB0G,GAAc;CAG3C,MAAAE,MAAYD,OAAME;CAAO,IAAAvB;CAAA,IAAAjD,EAAA,QAAAuE,KAAA;EAGXtB,MAAAsB,IAAGrC,OAAQuC,MAA6B;EAACzE,EAAA,MAAAuE;EAAAvE,EAAA,MAAAiD;CAAA,OAAAA,MAAAjD,EAAA;CAAA,IAAA+D;CAAA,IAAA/D,EAAA,QAAAuE,KAAA;EACtCR,MAAAQ,IAAGrC,OAAQwC,MAA0B;EAAC1E,EAAA,MAAAuE;EAAAvE,EAAA,MAAA+D;CAAA,OAAAA,MAAA/D,EAAA;CAAA,IAAA2E;CAAA,IAAA3E,EAAA,QAAAuE,OAAAvE,EAAA,QAAAW,cAAAX,EAAA,QAAAiD,OAAAjD,EAAA,QAAA+D,KAAA;EAHhDY,MAAA;GAAAC,SACIL;GAAG1F,YACAoE;GAAyCxC,eACtCsD;GAAsCpD;EAEvD;EAACX,EAAA,MAAAuE;EAAAvE,EAAA,MAAAW;EAAAX,EAAA,MAAAiD;EAAAjD,EAAA,MAAA+D;EAAA/D,EAAA,MAAA2E;CAAA,OAAAA,MAAA3E,EAAA;CAPH,MAAAwE,QAEEG;CAM6B,IAAAE;CAAA,IAAA7E,EAAA,QAAAsE,UAAAtE,EAAA,QAAAwE,MAAAI,SAAA;EAS7BC,MAA4C,CAAC;EAC7C,KAAK,MAAAC,UAAgBN,MAAKI,SAAQ;GAChC,IAAIE,OAAMC,oBAAqB,aAAa;GAC5C,KAAK,MAAAC,QAAcF,OAAMG,OAAM;IAC7B,MAAAC,WAAiB,GAAGJ,OAAMxC,GAAG,IAAK0C,KAAI3B;IACtCwB,IAAIK,YAAY;KAAAC,MACR;KAAU,GACZH,KAAII,gBAAiB1B,KAAAA,IAArB,EAAA0B,aACeJ,KAAII,YAClB,IAFD,CAEC;KAACC,YACML,KAAIM;KAAqBC,UAC5BC,SACPlB,OAAMmB,IAAK,EAAAC,KAAOZ,OAAMxC,GAAI,CAAC,CAAC,CAAAqD,SAAUX,KAAI3B,MAAOmC,IAAe;IACtE;GARa;EASd;EACFxF,EAAA,MAAAsE;EAAAtE,EAAA,MAAAwE,MAAAI;EAAA5E,EAAA,MAAA6E;CAAA,OAAAA,MAAA7E,EAAA;CAhBH,MAAA4F,UAiBEf;CAGF,MAAAgB,YAAkBjI,sBAAsB;CAAE,IAAAkI;CAAA,IAAA9F,EAAA,QAAA6F,aAAA7F,EAAA,QAAA4F,SAAA;EAIxCE,YAAA;GACE,MAAAC,SAAeF,UAASxE;GACxB,IAAI,CAAC0E,QAAM;GAAS,OACbA,OAAMC,aAAaC,SAAU,EAAAC,wBACV,EAAAjB,OAASW,QAAQ,GAC3C,CAAC;EAAC;EACH5F,EAAA,MAAA6F;EAAA7F,EAAA,MAAA4F;EAAA5F,EAAA,MAAA8F;CAAA,OAAAA,MAAA9F,EAAA;CAAA,IAAAmG;CAAA,IAAAnG,EAAA,QAAA4F,SAAA;EACDO,MAAA,CAACP,OAAO;EAAC5F,EAAA,MAAA4F;EAAA5F,EAAA,MAAAmG;CAAA,OAAAA,MAAAnG,EAAA;CATXjC,wBACE,gBACA+H,KAOAK,GACF;CAAC,IAAAC;CAAA,IAAApG,EAAA,QAAAsE,UAAAtE,EAAA,QAAAwE,OAAA;EAEoB4B,OAAAhD,MAAAiD,UAAA;GACnB,MAAAC,OAAalD,SAAS,cAAcoB,MAAK3F,aAAc2F,MAAK/D;GAC5D,MAAA8F,QAAcD,KAAKD;GACnB,IAAI,CAACE,OACH,MAAM,IAAIC,MACR,0BAA0BpD,KAAI,YAAaiD,MAAK,WAAYC,KAAIzE,OAAO,EACzE;GACD,OACMyC,OAAMmB,IAAK,EAAAC,KAAOa,MAAKjE,GAAI,CAAC;EAAC;EACrCtC,EAAA,MAAAsE;EAAAtE,EAAA,MAAAwE;EAAAxE,EAAA,MAAAoG;CAAA,OAAAA,MAAApG,EAAA;CATD,MAAAyG,eAAqBL;CASnB,IAAAM;CAAA,IAAA1G,EAAA,QAAAwE,OAAA;EAGUkC,YAAMlC;EAAKxE,EAAA,MAAAwE;EAAAxE,EAAA,MAAA0G;CAAA,OAAAA,MAAA1G,EAAA;CAAA,IAAA2G;CAAA,IAAA3G,EAAA,QAAAsE,UAAAtE,EAAA,QAAAyG,cAAA;EACbE,OAAAC,UAAA;GACN,IAAI,QAAQA,OAAK,OAAStC,OAAMmB,IAAK,EAAAC,KAAOkB,MAAKtE,GAAI,CAAC;GAAE,OACjDmE,aAAaG,MAAKxD,MAAOwD,MAAKP,KAAM;EAAC;EAC7CrG,EAAA,MAAAsE;EAAAtE,EAAA,MAAAyG;EAAAzG,EAAA,MAAA2G;CAAA,OAAAA,MAAA3G,EAAA;CAAA,IAAA6G;CAAA,IAAAC;CAAA,IAAA9G,EAAA,QAAAyG,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;EAAArG,EAAA,MAAAyG;EAAAzG,EAAA,MAAA6G;EAAA7G,EAAA,MAAA8G;CAAA,OAAA;EAAAD,MAAA7G,EAAA;EAAA8G,MAAA9G,EAAA;CAAA;CAAA,IAAA+G;CAAA,IAAA/G,EAAA,QAAAO,OAAAC,IAAA,2BAAA,GAAA;EACzCuG,MAAA,OAAAE,QAAA;GAAO,MAAA,EAAA5D,MAAAC,KAAAE,MAAAvE,mBAAAkI,qBAAA1D,OAAAE,gBAAAsD;GAQtB,MAAAG,SAAsC;IAAA9E,IAChCrE,YAAY;IAACoF;IAAAC;IAGXE;IAAqBvE,mBAC3BA;IAAiB,GACbwE,UAAUC,KAAAA,IAAV,EAAAD,MAAmC,IAAnC,CAAmC;IAAC,GACpCE,gBAAgBD,KAAAA,IAAhB,EAAAC,YAA+C,IAA/C,CAA+C;IAAC0D,WACzCC,KAAIC,IAAK;GACtB;GACA7G,kBAAiB8G,WAAU,CAAA,GAAI5F,QAAMwF,MAAM,CAAC;GAAC,OACtCA,OAAM9E;EAAG;EACjBtC,EAAA,MAAA+G;CAAA,OAAAA,MAAA/G,EAAA;CAAA,IAAAiH;CAAA,IAAAjH,EAAA,QAAAsE,UAAAtE,EAAA,QAAAwE,SAAAxE,EAAA,QAAAlB,SAAA;EACamI,MAAA,OAAA3E,OAAA;GAGZ,IAAIkC,MAAK3F,WAAW4I,MAAMC,QAAOvE,IAACb,OAAQA,EAAE,GAC1C,MAAM,IAAIkE,MACR,4BAA4BlE,GAAE,kFAChC;GAMF,IAAA;IACE,MAAMgC,OAAMmB,IAAK,EAAAC,KAAOpD,GAAG,CAAC,CAAC,CAAAqF,OAAQ;GAAC,QAAA;IAEtC,MAAM7I,QAAO8I,eAAgBtF,EAAE;IAC/B5B,kBAAiBmH,WAAUjG,OAAIM,QAAQ4F,QAAO7D,IAAC3B,OAAQA,EAAE,CAAC;GAAC;EAC5D;EACFtC,EAAA,MAAAsE;EAAAtE,EAAA,MAAAwE;EAAAxE,EAAA,MAAAlB;EAAAkB,EAAA,MAAAiH;CAAA,OAAAA,MAAAjH,EAAA;CAAA,IAAA+H;CAAA,IAAA/H,EAAA,QAAA0G,OAAA1G,EAAA,QAAA2G,OAAA3G,EAAA,QAAA6G,OAAA7G,EAAA,QAAA8G,OAAA9G,EAAA,QAAAiH,KAAA;EA/CIc,MAAA;GAAAC,UACKtB;GAAW5B,QACb6B;GAGPsB,WACUpB;GAA+CqB,cAC5CpB;GAA4CqB,iBACzCpB;GAoBhBqB,cACanB;EAmBhB;EAACjH,EAAA,MAAA0G;EAAA1G,EAAA,MAAA2G;EAAA3G,EAAA,MAAA6G;EAAA7G,EAAA,MAAA8G;EAAA9G,EAAA,MAAAiH;EAAAjH,EAAA,MAAA+H;CAAA,OAAAA,MAAA/H,EAAA;CAAA,OAhDM+H;AAgDN;AAGH,MAAaM,qBAAqB5K,SAASqC,qBAAqB;AAKhEjC,sBAAsBiC,wBAAwBwI,QAAQC,WAAW;CAC/D,IAAI,CAACD,OAAOtC,gBAAgBuC,OAAOvC,aAAawC,WAAW,MACzDF,OAAOtC,eAAehI,aAAa;AAEvC,CAAC;AAjQ6B,SAAAiE,MAAAI,GAAA;CAAA,OAiDwBA,EAACC;AAAG;AAjD5B,SAAAQ,OAAAK,GAAA;CAAA,OAyECA,EAACb;AAAG;AAzEL,SAAAS,OAAAkB,GAAA;CAAA,OA0EIA,EAAC3B;AAAG;AA1ER,eAAAuB,SAAA,CAAA;AAAA,SAAAY,OAAAgE,KAAA;CAAA,OA4IMxE,IAACb,SAAU;AAAW;AA5I5B,SAAAsB,OAAAgE,KAAA;CAAA,OA6ISzE,IAACb,SAAU;AAAQ"}
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 // 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 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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"}
@@ -19,7 +19,8 @@ type McpServerResourceProps = {
19
19
  readonly elicitation?: boolean;
20
20
  onRemove: () => Promise<void>;
21
21
  };
22
+ declare const getConnectionDependencies: (props: McpServerResourceProps) => readonly unknown[];
22
23
  declare const McpServerResource: import("@assistant-ui/tap").Resource<ClientOutput<"mcpServer">, [props: McpServerResourceProps]>;
23
24
  //#endregion
24
- export { McpServerResource, McpServerResourceProps };
25
+ export { McpServerResource, McpServerResourceProps, getConnectionDependencies };
25
26
  //# sourceMappingURL=McpServerResource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"McpServerResource.d.ts","names":[],"sources":["../../src/resources/McpServerResource.ts"],"mappings":";;;;KA4BY;EACV;EACA,MAAM;EACN;EACA;EACA;EACA,MAAM;EACN,SAAS;EACT;EACA;EACA;EACA;aAAmB;;WACV;EACT,gBAAgB;;cAwoBL,+CAAiB,SAAA,4BAAA,OAAA"}
1
+ {"version":3,"file":"McpServerResource.d.ts","names":[],"sources":["../../src/resources/McpServerResource.ts"],"mappings":";;;;KAqCY;EACV;EACA,MAAM;EACN;EACA;EACA;EACA,MAAM;EACN,SAAS;EACT;EACA;EACA;EACA;aAAmB;;WACV;EACT,gBAAgB;;cAOL,4BACX,OAAO;cA8rBI,+CAAiB,SAAA,4BAAA,OAAA"}
@@ -1,15 +1,21 @@
1
1
  import { assertValidServerId } from "../utils/serverId.js";
2
2
  import { createMcpId } from "../utils/createMcpId.js";
3
- import { createOAuthProvider } from "../auth/createOAuthProvider.js";
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";
6
7
  import { useEffect, useEffectEvent, useMemo, useRef, useState } from "@assistant-ui/tap/react-shim";
7
8
  import { resource, useResource, withKey } from "@assistant-ui/tap";
9
+ import { shallowEqual } from "@assistant-ui/store/internal";
8
10
  import { Client, StreamableHTTPClientTransport, UnauthorizedError } from "@modelcontextprotocol/client";
9
11
  //#region src/resources/McpServerResource.ts
10
12
  const getConnectionDependencies = (props) => {
11
13
  const auth = props.auth;
12
- const authDependencies = auth.type === "bearer" ? [auth.type, auth.token] : auth.type === "oauth" ? [
14
+ const authDependencies = auth.type === "bearer" ? [
15
+ auth.type,
16
+ auth.token,
17
+ props.storage.scopeId
18
+ ] : auth.type === "oauth" ? [
13
19
  auth.type,
14
20
  auth.scopes?.length,
15
21
  ...auth.scopes ?? [],
@@ -17,7 +23,8 @@ const getConnectionDependencies = (props) => {
17
23
  auth.tokenEndpoint,
18
24
  auth.registrationEndpoint,
19
25
  auth.clientId,
20
- auth.clientSecret
26
+ auth.clientSecret,
27
+ props.storage.scopeId
21
28
  ] : [auth.type];
22
29
  return [
23
30
  props.id,
@@ -28,7 +35,6 @@ const getConnectionDependencies = (props) => {
28
35
  props.elicitation !== false
29
36
  ];
30
37
  };
31
- const areConnectionDependenciesEqual = (left, right) => left.length === right.length && left.every((value, index) => Object.is(value, right[index]));
32
38
  const useMcpServerResourceInstance = (props) => {
33
39
  assertValidServerId(props.id);
34
40
  const [connectionState, setConnectionState] = useState("disconnected");
@@ -40,6 +46,7 @@ const useMcpServerResourceInstance = (props) => {
40
46
  const transportRef = useRef(null);
41
47
  const pendingTransportRef = useRef(null);
42
48
  const connectionGenerationRef = useRef(0);
49
+ const pendingAuthValidationRef = useRef(null);
43
50
  const elicitationResolversRef = useRef(/* @__PURE__ */ new Map());
44
51
  const pendingDisposalRef = useRef(null);
45
52
  const mountedRef = useRef(true);
@@ -109,10 +116,23 @@ const useMcpServerResourceInstance = (props) => {
109
116
  if (timeout !== void 0) clearTimeout(timeout);
110
117
  }
111
118
  });
119
+ const unboundAuthMessage = () => `MCP server "${props.id}" has saved authentication for a different URL. Authenticate again to connect to ${props.url}.`;
120
+ const loadAuthState = useEffectEvent(async () => {
121
+ const state = await props.storage.loadAuthState(props.id);
122
+ if (isAuthStateForServerUrl(state, props.url)) return {
123
+ state,
124
+ unbound: false
125
+ };
126
+ return {
127
+ state: null,
128
+ unbound: hasPersistedCredentials(state)
129
+ };
130
+ });
112
131
  const buildTransport = useEffectEvent(async () => {
113
132
  if (props.auth.type === "oauth") {
114
133
  const authProvider = createOAuthProvider({
115
134
  serverId: props.id,
135
+ serverUrl: props.url,
116
136
  config: props.auth,
117
137
  storage: props.storage,
118
138
  redirectUri: props.redirectUri,
@@ -121,8 +141,9 @@ const useMcpServerResourceInstance = (props) => {
121
141
  return new StreamableHTTPClientTransport(new URL(props.url), { authProvider });
122
142
  }
123
143
  if (props.auth.type === "bearer") {
124
- const persisted = await props.storage.loadAuthState(props.id);
125
- const headers = buildHeaders(props.auth, persisted);
144
+ const { state: state_0, unbound } = await loadAuthState();
145
+ const headers = buildHeaders(props.auth, state_0);
146
+ if (!headers && unbound) throw new Error(unboundAuthMessage());
126
147
  const transportOpts = {};
127
148
  if (headers) transportOpts.requestInit = { headers };
128
149
  return new StreamableHTTPClientTransport(new URL(props.url), transportOpts);
@@ -249,6 +270,37 @@ const useMcpServerResourceInstance = (props) => {
249
270
  await closeTransports();
250
271
  });
251
272
  const doCompleteAuth = useEffectEvent(async (callbackUrl) => {
273
+ const validationGeneration = connectionGenerationRef.current;
274
+ const url_0 = new URL(callbackUrl);
275
+ const state_1 = url_0.searchParams.get("state");
276
+ if (!state_1) throw new Error("missing \"state\" parameter");
277
+ let pendingAuthValidation = pendingAuthValidationRef.current;
278
+ if (!pendingAuthValidation) {
279
+ let resolve_0;
280
+ pendingAuthValidation = {
281
+ count: 0,
282
+ promise: new Promise((resolvePromise) => {
283
+ resolve_0 = resolvePromise;
284
+ }),
285
+ resolve: resolve_0
286
+ };
287
+ pendingAuthValidationRef.current = pendingAuthValidation;
288
+ }
289
+ pendingAuthValidation.count += 1;
290
+ try {
291
+ const { state: persisted, unbound } = await loadAuthState();
292
+ if (!isCurrentConnection(validationGeneration)) throw createInterruptedAuthError();
293
+ if (unbound) throw new Error(unboundAuthMessage());
294
+ if (!persisted?.state) throw new Error("no pending OAuth authorization request for this server");
295
+ if (persisted.state !== state_1) throw new Error("OAuth state does not match the authorization request");
296
+ if (!url_0.searchParams.get("code") && !url_0.searchParams.get("error")) throw new Error("missing authorization code in callback URL");
297
+ } finally {
298
+ pendingAuthValidation.count -= 1;
299
+ if (pendingAuthValidation.count === 0) {
300
+ pendingAuthValidationRef.current = null;
301
+ pendingAuthValidation.resolve();
302
+ }
303
+ }
252
304
  const generation_3 = ++connectionGenerationRef.current;
253
305
  cancelPendingElicitations();
254
306
  await closePendingTransport();
@@ -256,8 +308,6 @@ const useMcpServerResourceInstance = (props) => {
256
308
  setConnectionState("authPending");
257
309
  setLastError(null);
258
310
  try {
259
- const code = new URL(callbackUrl).searchParams.get("code");
260
- if (!code) throw new Error("missing authorization code in callback URL");
261
311
  let transport_4 = transportRef.current;
262
312
  if (!transport_4) {
263
313
  transport_4 = await buildTransport();
@@ -269,7 +319,7 @@ const useMcpServerResourceInstance = (props) => {
269
319
  transportRef.current = null;
270
320
  clientRef.current = null;
271
321
  pendingTransportRef.current = transport_4;
272
- await transport_4.finishAuth(code);
322
+ await transport_4.finishAuth(url_0.searchParams);
273
323
  if (!isCurrentConnection(generation_3)) throw createInterruptedAuthError();
274
324
  setAuthorizationUrl(null);
275
325
  if (!await finalizeConnect(transport_4, generation_3)) throw createInterruptedAuthError();
@@ -293,9 +343,9 @@ const useMcpServerResourceInstance = (props) => {
293
343
  return;
294
344
  }
295
345
  const generation_4 = connectionGenerationRef.current;
296
- let persisted_0;
346
+ let loaded;
297
347
  try {
298
- persisted_0 = await props.storage.loadAuthState(props.id);
348
+ loaded = await loadAuthState();
299
349
  } catch (error_2) {
300
350
  if (signal.cancelled || !isCurrentConnection(generation_4)) return;
301
351
  const message_0 = error_2 instanceof Error ? error_2.message : String(error_2);
@@ -304,9 +354,19 @@ const useMcpServerResourceInstance = (props) => {
304
354
  return;
305
355
  }
306
356
  if (signal.cancelled || !isCurrentConnection(generation_4)) return;
357
+ if (loaded.unbound) {
358
+ setLastError({ message: unboundAuthMessage() });
359
+ return;
360
+ }
361
+ const persisted = loaded.state;
307
362
  if (props.auth.type === "oauth") {
308
- if (!persisted_0?.tokens) return;
309
- } else if (!persisted_0?.token) return;
363
+ if (!hasUsableOAuthTokens(persisted, props.auth)) return;
364
+ } else if (!persisted?.token) return;
365
+ const pendingAuthValidation_0 = pendingAuthValidationRef.current;
366
+ if (pendingAuthValidation_0) {
367
+ await pendingAuthValidation_0.promise;
368
+ if (signal.cancelled || !isCurrentConnection(generation_4)) return;
369
+ }
310
370
  doConnect();
311
371
  });
312
372
  useEffect(() => {
@@ -322,7 +382,7 @@ const useMcpServerResourceInstance = (props) => {
322
382
  mountedRef.current = false;
323
383
  signal_0.cancelled = true;
324
384
  const task_0 = transportCloseQueueRef.current.then(async () => {
325
- await new Promise((resolve_0) => queueMicrotask(resolve_0));
385
+ await new Promise((resolve_1) => queueMicrotask(resolve_1));
326
386
  if (pendingDisposal.cancelled) return;
327
387
  connectionGenerationRef.current += 1;
328
388
  await Promise.all(detachTransports().map(closeTransportSafely));
@@ -330,7 +390,7 @@ const useMcpServerResourceInstance = (props) => {
330
390
  transportCloseQueueRef.current = task_0;
331
391
  };
332
392
  }, [props.transportCloseQueueRef]);
333
- const state = useMemo(() => ({
393
+ const state_2 = useMemo(() => ({
334
394
  id: props.id,
335
395
  kind: props.kind,
336
396
  name: props.name,
@@ -354,13 +414,13 @@ const useMcpServerResourceInstance = (props) => {
354
414
  pendingElicitations
355
415
  ]);
356
416
  return {
357
- getState: () => state,
417
+ getState: () => state_2,
358
418
  connect: doConnect,
359
419
  disconnect: doDisconnect,
360
420
  remove: async () => {
361
421
  await doDisconnect();
362
422
  try {
363
- await props.storage.clearAuthState(props.id);
423
+ await clearOAuthProviderAuthState(props.storage, props.id);
364
424
  await props.onRemove();
365
425
  } catch (err_1) {
366
426
  setLastError({ message: err_1 instanceof Error ? err_1.message : String(err_1) });
@@ -430,7 +490,7 @@ const McpServerResource = resource(function useMcpServerResource(props) {
430
490
  generation: 0
431
491
  });
432
492
  let currentConnection = connection;
433
- if (!areConnectionDependenciesEqual(connection.dependencies, dependencies)) {
493
+ if (!shallowEqual(connection.dependencies, dependencies)) {
434
494
  currentConnection = {
435
495
  dependencies,
436
496
  generation: connection.generation + 1
@@ -443,6 +503,6 @@ const McpServerResource = resource(function useMcpServerResource(props) {
443
503
  })));
444
504
  });
445
505
  //#endregion
446
- export { McpServerResource };
506
+ export { McpServerResource, getConnectionDependencies };
447
507
 
448
508
  //# sourceMappingURL=McpServerResource.js.map
@@ -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","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","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","elicitationResolversRef","Map","resolve","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","promise","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","code","searchParams","finishAuth","connected","tryAutoConnect","Awaited","tokens","previousDisposal","pendingDisposal","queueMicrotask","state","getState","disconnect","remove","clearAuthState","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 { createOAuthProvider } 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\nconst getConnectionDependencies = (\n props: McpServerResourceProps,\n): readonly unknown[] => {\n const auth = props.auth;\n const authDependencies =\n auth.type === \"bearer\"\n ? [auth.type, auth.token]\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 ]\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 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 generation = ++connectionGenerationRef.current;\n cancelPendingElicitations();\n await closePendingTransport();\n if (!isCurrentConnection(generation)) throw createInterruptedAuthError();\n\n setConnectionState(\"authPending\");\n setLastError(null);\n try {\n const url = new URL(callbackUrl);\n const code = url.searchParams.get(\"code\");\n if (!code) throw new Error(\"missing authorization code in callback URL\");\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(code);\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 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 props.storage.clearAuthState(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 resources do not expose a stable scope identity and may return a\n // fresh client on ordinary renders, so storage changes cannot key remounts.\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","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","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\";\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 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 (!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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"}
@@ -7,6 +7,13 @@ type McpLocalStorageOptions = {
7
7
  keyPrefix?: string;
8
8
  /** Override the underlying Storage. Defaults to globalThis.localStorage. */
9
9
  storage?: Storage;
10
+ /**
11
+ * Stable identity for the backing data, used to key server reconnects.
12
+ * Required to get reconnect-on-swap behavior when `storage` is overridden;
13
+ * without it a custom backing store declares no scope, since a prefix
14
+ * alone cannot distinguish two different stores.
15
+ */
16
+ scopeId?: string;
10
17
  };
11
18
  declare const normalizeCustomServerRecords: (value: unknown) => MCPCustomServerRecord[];
12
19
  declare const normalizePersistedAuthState: (value: unknown) => MCPPersistedAuthState | null;
@@ -1 +1 @@
1
- {"version":3,"file":"McpLocalStorage.d.ts","names":[],"sources":["../../../src/resources/storage/McpLocalStorage.ts"],"mappings":";;;;KAYY;;EAEV;;EAEA,UAAU;;cAiGC,+BACX,mBACC;cAsEU,8BACX,mBACC;cAyEU,6CAAe,SAAA,aAAA,OAAA"}
1
+ {"version":3,"file":"McpLocalStorage.d.ts","names":[],"sources":["../../../src/resources/storage/McpLocalStorage.ts"],"mappings":";;;;KAcY;;EAEV;;EAEA,UAAU;;;;;;;EAOV;;cAiGW,+BACX,mBACC;cAgFU,8BACX,mBACC;cAiGU,6CAAe,SAAA,aAAA,OAAA"}