@assistant-ui/react-mcp 0.1.16 → 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.
- package/dist/auth/createOAuthProvider.d.ts +13 -2
- package/dist/auth/createOAuthProvider.d.ts.map +1 -1
- package/dist/auth/createOAuthProvider.js +109 -31
- package/dist/auth/createOAuthProvider.js.map +1 -1
- package/dist/auth/types.d.ts +5 -1
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/resources/McpManagerResource.d.ts.map +1 -1
- package/dist/resources/McpManagerResource.js.map +1 -1
- package/dist/resources/McpServerResource.d.ts.map +1 -1
- package/dist/resources/McpServerResource.js +36 -15
- package/dist/resources/McpServerResource.js.map +1 -1
- package/dist/resources/storage/McpLocalStorage.d.ts.map +1 -1
- package/dist/resources/storage/McpLocalStorage.js +14 -0
- package/dist/resources/storage/McpLocalStorage.js.map +1 -1
- package/dist/utils/serverUrl.d.ts +8 -0
- package/dist/utils/serverUrl.d.ts.map +1 -0
- package/dist/utils/serverUrl.js +15 -0
- package/dist/utils/serverUrl.js.map +1 -0
- package/package.json +6 -6
- package/src/auth/createOAuthProvider.test.ts +544 -35
- package/src/auth/createOAuthProvider.ts +201 -46
- package/src/auth/types.ts +5 -1
- package/src/resources/McpManagerResource.ts +3 -0
- package/src/resources/McpServerResource.test.ts +252 -17
- package/src/resources/McpServerResource.ts +35 -14
- package/src/resources/storage/McpLocalStorage.test.ts +26 -0
- package/src/resources/storage/McpLocalStorage.ts +21 -0
- package/src/utils/serverUrl.test.ts +66 -0
- package/src/utils/serverUrl.ts +23 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { assertValidServerId } from "../utils/serverId.js";
|
|
2
2
|
import { createMcpId } from "../utils/createMcpId.js";
|
|
3
|
-
import {
|
|
3
|
+
import { hasPersistedCredentials, isAuthStateForServerUrl } from "../utils/serverUrl.js";
|
|
4
|
+
import { clearOAuthProviderAuthState, createOAuthProvider, hasUsableOAuthTokens } from "../auth/createOAuthProvider.js";
|
|
4
5
|
import { buildHeaders } from "../auth/buildHeaders.js";
|
|
5
6
|
import { validateElicitationContent } from "./validateElicitationContent.js";
|
|
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) => {
|
|
@@ -33,7 +35,6 @@ const getConnectionDependencies = (props) => {
|
|
|
33
35
|
props.elicitation !== false
|
|
34
36
|
];
|
|
35
37
|
};
|
|
36
|
-
const areConnectionDependenciesEqual = (left, right) => left.length === right.length && left.every((value, index) => Object.is(value, right[index]));
|
|
37
38
|
const useMcpServerResourceInstance = (props) => {
|
|
38
39
|
assertValidServerId(props.id);
|
|
39
40
|
const [connectionState, setConnectionState] = useState("disconnected");
|
|
@@ -115,10 +116,23 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
115
116
|
if (timeout !== void 0) clearTimeout(timeout);
|
|
116
117
|
}
|
|
117
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
|
+
});
|
|
118
131
|
const buildTransport = useEffectEvent(async () => {
|
|
119
132
|
if (props.auth.type === "oauth") {
|
|
120
133
|
const authProvider = createOAuthProvider({
|
|
121
134
|
serverId: props.id,
|
|
135
|
+
serverUrl: props.url,
|
|
122
136
|
config: props.auth,
|
|
123
137
|
storage: props.storage,
|
|
124
138
|
redirectUri: props.redirectUri,
|
|
@@ -127,8 +141,9 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
127
141
|
return new StreamableHTTPClientTransport(new URL(props.url), { authProvider });
|
|
128
142
|
}
|
|
129
143
|
if (props.auth.type === "bearer") {
|
|
130
|
-
const
|
|
131
|
-
const headers = buildHeaders(props.auth,
|
|
144
|
+
const { state: state_0, unbound } = await loadAuthState();
|
|
145
|
+
const headers = buildHeaders(props.auth, state_0);
|
|
146
|
+
if (!headers && unbound) throw new Error(unboundAuthMessage());
|
|
132
147
|
const transportOpts = {};
|
|
133
148
|
if (headers) transportOpts.requestInit = { headers };
|
|
134
149
|
return new StreamableHTTPClientTransport(new URL(props.url), transportOpts);
|
|
@@ -257,8 +272,8 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
257
272
|
const doCompleteAuth = useEffectEvent(async (callbackUrl) => {
|
|
258
273
|
const validationGeneration = connectionGenerationRef.current;
|
|
259
274
|
const url_0 = new URL(callbackUrl);
|
|
260
|
-
const
|
|
261
|
-
if (!
|
|
275
|
+
const state_1 = url_0.searchParams.get("state");
|
|
276
|
+
if (!state_1) throw new Error("missing \"state\" parameter");
|
|
262
277
|
let pendingAuthValidation = pendingAuthValidationRef.current;
|
|
263
278
|
if (!pendingAuthValidation) {
|
|
264
279
|
let resolve_0;
|
|
@@ -273,10 +288,11 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
273
288
|
}
|
|
274
289
|
pendingAuthValidation.count += 1;
|
|
275
290
|
try {
|
|
276
|
-
const persisted = await
|
|
291
|
+
const { state: persisted, unbound } = await loadAuthState();
|
|
277
292
|
if (!isCurrentConnection(validationGeneration)) throw createInterruptedAuthError();
|
|
293
|
+
if (unbound) throw new Error(unboundAuthMessage());
|
|
278
294
|
if (!persisted?.state) throw new Error("no pending OAuth authorization request for this server");
|
|
279
|
-
if (persisted.state !==
|
|
295
|
+
if (persisted.state !== state_1) throw new Error("OAuth state does not match the authorization request");
|
|
280
296
|
if (!url_0.searchParams.get("code") && !url_0.searchParams.get("error")) throw new Error("missing authorization code in callback URL");
|
|
281
297
|
} finally {
|
|
282
298
|
pendingAuthValidation.count -= 1;
|
|
@@ -327,9 +343,9 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
327
343
|
return;
|
|
328
344
|
}
|
|
329
345
|
const generation_4 = connectionGenerationRef.current;
|
|
330
|
-
let
|
|
346
|
+
let loaded;
|
|
331
347
|
try {
|
|
332
|
-
|
|
348
|
+
loaded = await loadAuthState();
|
|
333
349
|
} catch (error_2) {
|
|
334
350
|
if (signal.cancelled || !isCurrentConnection(generation_4)) return;
|
|
335
351
|
const message_0 = error_2 instanceof Error ? error_2.message : String(error_2);
|
|
@@ -338,9 +354,14 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
338
354
|
return;
|
|
339
355
|
}
|
|
340
356
|
if (signal.cancelled || !isCurrentConnection(generation_4)) return;
|
|
357
|
+
if (loaded.unbound) {
|
|
358
|
+
setLastError({ message: unboundAuthMessage() });
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
const persisted = loaded.state;
|
|
341
362
|
if (props.auth.type === "oauth") {
|
|
342
|
-
if (!
|
|
343
|
-
} else if (!
|
|
363
|
+
if (!hasUsableOAuthTokens(persisted, props.auth)) return;
|
|
364
|
+
} else if (!persisted?.token) return;
|
|
344
365
|
const pendingAuthValidation_0 = pendingAuthValidationRef.current;
|
|
345
366
|
if (pendingAuthValidation_0) {
|
|
346
367
|
await pendingAuthValidation_0.promise;
|
|
@@ -369,7 +390,7 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
369
390
|
transportCloseQueueRef.current = task_0;
|
|
370
391
|
};
|
|
371
392
|
}, [props.transportCloseQueueRef]);
|
|
372
|
-
const
|
|
393
|
+
const state_2 = useMemo(() => ({
|
|
373
394
|
id: props.id,
|
|
374
395
|
kind: props.kind,
|
|
375
396
|
name: props.name,
|
|
@@ -393,7 +414,7 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
393
414
|
pendingElicitations
|
|
394
415
|
]);
|
|
395
416
|
return {
|
|
396
|
-
getState: () =>
|
|
417
|
+
getState: () => state_2,
|
|
397
418
|
connect: doConnect,
|
|
398
419
|
disconnect: doDisconnect,
|
|
399
420
|
remove: async () => {
|
|
@@ -469,7 +490,7 @@ const McpServerResource = resource(function useMcpServerResource(props) {
|
|
|
469
490
|
generation: 0
|
|
470
491
|
});
|
|
471
492
|
let currentConnection = connection;
|
|
472
|
-
if (!
|
|
493
|
+
if (!shallowEqual(connection.dependencies, dependencies)) {
|
|
473
494
|
currentConnection = {
|
|
474
495
|
dependencies,
|
|
475
496
|
generation: connection.generation + 1
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpServerResource.js","names":["useState","useRef","useEffect","useMemo","useEffectEvent","resource","useResource","withKey","ClientOutput","Client","StreamableHTTPClientTransport","UnauthorizedError","ClientOptions","ElicitRequest","ElicitResult","StreamableHTTPClientTransportOptions","clearOAuthProviderAuthState","createOAuthProvider","buildHeaders","assertValidServerId","validateElicitationContent","MCPStorage","MCPAuthConfig","MCPConnectionState","MCPElicitation","MCPElicitationResponse","MCPServerKind","MCPServerState","MCPToolInfo","createMcpId","McpServerResourceProps","id","kind","name","url","icon","auth","storage","redirectUri","autoConnect","connectionTimeout","cache","defaultTtlMs","elicitation","onRemove","Promise","McpServerResourceInstanceProps","transportCloseQueueRef","current","getConnectionDependencies","props","authDependencies","type","token","scopeId","scopes","length","authorizationEndpoint","tokenEndpoint","registrationEndpoint","clientId","clientSecret","areConnectionDependenciesEqual","left","right","every","value","index","Object","is","useMcpServerResourceInstance","connectionState","setConnectionState","tools","setTools","lastError","setLastError","message","authorizationUrl","setAuthorizationUrl","pendingElicitations","setPendingElicitations","clientRef","transportRef","pendingTransportRef","connectionGenerationRef","pendingAuthValidationRef","count","promise","resolve","elicitationResolversRef","Map","result","signal","AbortSignal","onAbort","requestedSchema","pendingDisposalRef","cancelled","mountedRef","closeTransportSafely","transport","close","closeQueuedTransports","transports","task","then","all","map","closePendingTransport","resolvePendingElicitation","entry","get","delete","removeEventListener","filter","setPendingElicitationError","error","NonNullable","has","cancelPendingElicitations","action","detachTransports","pendingTransport","activeTransport","Set","closeTransports","isCurrentConnection","generation","createInterruptedAuthError","cause","Error","undefined","withConnectionTimeout","T","phase","startedAt","timeoutMs","remainingMs","Date","now","timeoutError","timeout","ReturnType","setTimeout","race","_","reject","clearTimeout","buildTransport","authProvider","serverId","config","onAuthorizationUrl","toString","URL","persisted","loadAuthState","headers","transportOpts","requestInit","applyToolsList","list","Array","description","inputSchema","t","info","syncTools","client","options","listPromise","listTools","finalizeConnect","clientOptions","capabilities","listChanged","autoRefresh","debounceMs","onChanged","items","defaultCacheTtlMs","version","setRequestHandler","request","context","params","set","aborted","addEventListener","once","connect","synced","doConnect","err","String","doDisconnect","doCompleteAuth","callbackUrl","validationGeneration","state","searchParams","pendingAuthValidation","resolvePromise","finishAuth","connected","tryAutoConnect","Awaited","tokens","previousDisposal","pendingDisposal","queueMicrotask","getState","disconnect","remove","callTool","args","arguments","Record","listResources","readResource","uri","completeAuth","answerElicitation","response","property","content","isArray","errors","properties","join","McpServerResourceInstance","McpServerResource","useMcpServerResource","dependencies","connection","setConnection","currentConnection"],"sources":["../../src/resources/McpServerResource.ts"],"sourcesContent":["import { useState, useRef, useEffect, useMemo, useEffectEvent } from \"react\";\nimport { resource, useResource, withKey } from \"@assistant-ui/tap\";\nimport type { ClientOutput } from \"@assistant-ui/store\";\nimport {\n Client,\n StreamableHTTPClientTransport,\n UnauthorizedError,\n type ClientOptions,\n type ElicitRequest,\n type ElicitResult,\n type StreamableHTTPClientTransportOptions,\n} from \"@modelcontextprotocol/client\";\nimport {\n clearOAuthProviderAuthState,\n createOAuthProvider,\n} from \"../auth/createOAuthProvider\";\nimport { buildHeaders } from \"../auth/buildHeaders\";\nimport { assertValidServerId } from \"../utils/serverId\";\nimport { validateElicitationContent } from \"./validateElicitationContent\";\nimport type { MCPStorage } from \"./storage/types\";\nimport type {\n MCPAuthConfig,\n MCPConnectionState,\n MCPElicitation,\n MCPElicitationResponse,\n MCPServerKind,\n MCPServerState,\n MCPToolInfo,\n} from \"../mcp-scope\";\nimport { createMcpId } from \"../utils/createMcpId\";\n\nexport type McpServerResourceProps = {\n id: string;\n kind: MCPServerKind;\n name: string;\n url: string;\n icon?: string | undefined;\n auth: MCPAuthConfig;\n storage: MCPStorage;\n redirectUri: string;\n autoConnect: boolean;\n connectionTimeout?: number | undefined;\n cache?: { readonly defaultTtlMs?: number } | undefined;\n readonly elicitation?: boolean;\n onRemove: () => Promise<void>;\n};\n\ntype McpServerResourceInstanceProps = McpServerResourceProps & {\n transportCloseQueueRef: { current: Promise<void> };\n};\n\nexport const getConnectionDependencies = (\n props: McpServerResourceProps,\n): readonly unknown[] => {\n const auth = props.auth;\n const authDependencies =\n auth.type === \"bearer\"\n ? [auth.type, auth.token, props.storage.scopeId]\n : auth.type === \"oauth\"\n ? [\n auth.type,\n auth.scopes?.length,\n ...(auth.scopes ?? []),\n auth.authorizationEndpoint,\n auth.tokenEndpoint,\n auth.registrationEndpoint,\n auth.clientId,\n auth.clientSecret,\n props.storage.scopeId,\n ]\n : [auth.type];\n\n return [\n props.id,\n props.url,\n ...authDependencies,\n props.redirectUri,\n props.cache?.defaultTtlMs,\n props.elicitation !== false,\n ];\n};\n\nconst areConnectionDependenciesEqual = (\n left: readonly unknown[],\n right: readonly unknown[],\n) =>\n left.length === right.length &&\n left.every((value, index) => Object.is(value, right[index]));\n\nconst useMcpServerResourceInstance = (\n props: McpServerResourceInstanceProps,\n): ClientOutput<\"mcpServer\"> => {\n assertValidServerId(props.id);\n const [connectionState, setConnectionState] =\n useState<MCPConnectionState>(\"disconnected\");\n const [tools, setTools] = useState<MCPToolInfo[]>([]);\n const [lastError, setLastError] = useState<{ message: string } | null>(null);\n const [authorizationUrl, setAuthorizationUrl] = useState<string | null>(null);\n const [pendingElicitations, setPendingElicitations] = useState<\n MCPElicitation[]\n >([]);\n\n const clientRef = useRef<Client | null>(null);\n const transportRef = useRef<StreamableHTTPClientTransport | null>(null);\n const pendingTransportRef = useRef<StreamableHTTPClientTransport | null>(\n null,\n );\n const connectionGenerationRef = useRef(0);\n const pendingAuthValidationRef = useRef<{\n count: number;\n promise: Promise<void>;\n resolve: () => void;\n } | null>(null);\n const elicitationResolversRef = useRef(\n new Map<\n string,\n {\n resolve: (result: ElicitResult) => void;\n signal: AbortSignal;\n onAbort: () => void;\n requestedSchema: unknown;\n }\n >(),\n );\n const pendingDisposalRef = useRef<{ cancelled: boolean } | null>(null);\n const mountedRef = useRef(true);\n\n const closeTransportSafely = async (\n transport: StreamableHTTPClientTransport,\n ): Promise<void> => {\n try {\n await transport.close();\n } catch {\n // ignore close errors\n }\n };\n\n const closeQueuedTransports = (\n transports: StreamableHTTPClientTransport[],\n ): Promise<void> => {\n const task = props.transportCloseQueueRef.current.then(async () => {\n await Promise.all(transports.map(closeTransportSafely));\n });\n props.transportCloseQueueRef.current = task;\n return task;\n };\n\n const closePendingTransport = async () => {\n const transport = pendingTransportRef.current;\n pendingTransportRef.current = null;\n await closeQueuedTransports(transport ? [transport] : []);\n };\n\n const resolvePendingElicitation = (id: string, result: ElicitResult) => {\n const entry = elicitationResolversRef.current.get(id);\n if (!entry) return false;\n elicitationResolversRef.current.delete(id);\n entry.signal.removeEventListener(\"abort\", entry.onAbort);\n setPendingElicitations((current) =>\n current.filter((elicitation) => elicitation.id !== id),\n );\n entry.resolve(result);\n return true;\n };\n\n const setPendingElicitationError = (\n id: string,\n error: NonNullable<MCPElicitation[\"error\"]>,\n ) => {\n if (!elicitationResolversRef.current.has(id)) return false;\n setPendingElicitations((current) =>\n current.map((elicitation) =>\n elicitation.id === id ? { ...elicitation, error } : elicitation,\n ),\n );\n return true;\n };\n\n const cancelPendingElicitations = () => {\n for (const [id] of elicitationResolversRef.current) {\n resolvePendingElicitation(id, { action: \"cancel\" });\n }\n };\n\n const detachTransports = () => {\n cancelPendingElicitations();\n const pendingTransport = pendingTransportRef.current;\n const activeTransport = transportRef.current;\n pendingTransportRef.current = null;\n transportRef.current = null;\n clientRef.current = null;\n\n return [\n ...new Set(\n [pendingTransport, activeTransport].filter(\n (transport): transport is StreamableHTTPClientTransport =>\n transport !== null,\n ),\n ),\n ];\n };\n\n const closeTransports = async () => {\n await closeQueuedTransports(detachTransports());\n };\n\n const isCurrentConnection = (generation: number) =>\n mountedRef.current && generation === connectionGenerationRef.current;\n\n const createInterruptedAuthError = (cause?: unknown) =>\n new Error(\n `MCP server \"${props.id}\" authorization was interrupted before completion.`,\n cause === undefined ? undefined : { cause },\n );\n\n const withConnectionTimeout = useEffectEvent(\n async <T>(\n promise: Promise<T>,\n phase: \"connecting\" | \"listing tools\",\n startedAt: number,\n ): Promise<T> => {\n const timeoutMs = props.connectionTimeout;\n if (timeoutMs === undefined) return await promise;\n const remainingMs = timeoutMs - (Date.now() - startedAt);\n const timeoutError = () =>\n new Error(\n `MCP server \"${props.id}\" timed out while ${phase} after ${timeoutMs}ms.`,\n );\n if (remainingMs <= 0) throw timeoutError();\n\n let timeout: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timeout = setTimeout(() => reject(timeoutError()), remainingMs);\n }),\n ]);\n } finally {\n if (timeout !== undefined) clearTimeout(timeout);\n }\n },\n );\n\n const buildTransport = useEffectEvent(\n async (): Promise<StreamableHTTPClientTransport> => {\n if (props.auth.type === \"oauth\") {\n const authProvider = createOAuthProvider({\n serverId: props.id,\n config: props.auth,\n storage: props.storage,\n redirectUri: props.redirectUri,\n onAuthorizationUrl: (url) => setAuthorizationUrl(url.toString()),\n });\n return new StreamableHTTPClientTransport(new URL(props.url), {\n authProvider,\n });\n }\n if (props.auth.type === \"bearer\") {\n const persisted = await props.storage.loadAuthState(props.id);\n const headers = buildHeaders(props.auth, persisted);\n const transportOpts: StreamableHTTPClientTransportOptions = {};\n if (headers) transportOpts.requestInit = { headers };\n return new StreamableHTTPClientTransport(\n new URL(props.url),\n transportOpts,\n );\n }\n return new StreamableHTTPClientTransport(new URL(props.url));\n },\n );\n\n const applyToolsList = useEffectEvent(\n (list: {\n tools: Array<{\n name: string;\n description?: string | undefined;\n inputSchema: unknown;\n }>;\n }) => {\n setTools(\n list.tools.map((t) => {\n const info: MCPToolInfo = {\n name: t.name,\n inputSchema: t.inputSchema,\n };\n if (t.description !== undefined) info.description = t.description;\n return info;\n }),\n );\n },\n );\n\n const syncTools = useEffectEvent(\n async (\n client: Client,\n generation: number,\n options?: { startedAt?: number | undefined },\n ): Promise<boolean> => {\n const listPromise = client.listTools();\n const list =\n options?.startedAt === undefined\n ? await listPromise\n : await withConnectionTimeout(\n listPromise,\n \"listing tools\",\n options.startedAt,\n );\n if (!isCurrentConnection(generation)) return false;\n applyToolsList(list);\n return true;\n },\n );\n\n const finalizeConnect = useEffectEvent(\n async (\n transport: StreamableHTTPClientTransport,\n generation: number,\n ): Promise<boolean> => {\n const clientOptions: ClientOptions = {\n ...(props.elicitation === false\n ? {}\n : { capabilities: { elicitation: {} } }),\n listChanged: {\n tools: {\n autoRefresh: true,\n debounceMs: 300,\n onChanged: (error, items) => {\n if (!isCurrentConnection(generation)) return;\n if (error !== null) {\n setLastError({ message: error.message });\n return;\n }\n if (items === null) return;\n setLastError(null);\n applyToolsList({ tools: items });\n },\n },\n },\n };\n if (props.cache?.defaultTtlMs !== undefined) {\n clientOptions.defaultCacheTtlMs = props.cache.defaultTtlMs;\n }\n const client = new Client(\n {\n name: \"assistant-ui-mcp\",\n version: \"0.0.0\",\n },\n clientOptions,\n );\n if (props.elicitation !== false) {\n client.setRequestHandler(\n \"elicitation/create\",\n (request: ElicitRequest, context): Promise<ElicitResult> => {\n if (!isCurrentConnection(generation)) {\n return Promise.resolve({ action: \"cancel\" });\n }\n if (!(\"requestedSchema\" in request.params)) {\n return Promise.resolve({ action: \"cancel\" });\n }\n const { message, requestedSchema } = request.params;\n\n const id = createMcpId();\n const promise = new Promise<ElicitResult>((resolve) => {\n const onAbort = () => {\n resolvePendingElicitation(id, { action: \"cancel\" });\n };\n elicitationResolversRef.current.set(id, {\n resolve,\n signal: context.signal,\n onAbort,\n requestedSchema,\n });\n });\n setPendingElicitations((current) => [\n ...current,\n {\n id,\n message,\n requestedSchema,\n },\n ]);\n const entry = elicitationResolversRef.current.get(id);\n if (entry) {\n if (context.signal.aborted) {\n entry.onAbort();\n } else {\n context.signal.addEventListener(\"abort\", entry.onAbort, {\n once: true,\n });\n }\n }\n return promise;\n },\n );\n }\n const startedAt = Date.now();\n await withConnectionTimeout(\n client.connect(transport),\n \"connecting\",\n startedAt,\n );\n if (!isCurrentConnection(generation)) return false;\n // Defer ref assignment until listTools() also succeeds — otherwise a\n // post-connect failure leaves stale refs that `callTool()` would\n // happily walk into, producing confusing SDK errors instead of\n // \"not connected\".\n const synced = await syncTools(client, generation, { startedAt });\n if (!synced) return false;\n\n pendingTransportRef.current = null;\n clientRef.current = client;\n transportRef.current = transport;\n setConnectionState(\"connected\");\n return true;\n },\n );\n\n const doConnect = useEffectEvent(async () => {\n const generation = ++connectionGenerationRef.current;\n // Close any prior transport/client so a re-connect doesn't leak.\n await closeTransports();\n if (!isCurrentConnection(generation)) return;\n\n setConnectionState(\"connecting\");\n setLastError(null);\n setAuthorizationUrl(null);\n // Clear tools so a reconnect (error → connect, or authRequired\n // → connect) doesn't expose the previous attempt's tool list.\n setTools([]);\n let transport: StreamableHTTPClientTransport | null = null;\n try {\n transport = await buildTransport();\n if (!isCurrentConnection(generation)) {\n await closeQueuedTransports([transport]);\n return;\n }\n pendingTransportRef.current = transport;\n // Don't assign to transportRef until connect succeeds — otherwise a\n // failed `listTools()` leaves an orphaned transport that future\n // doConnect / doDisconnect calls treat as live.\n await finalizeConnect(transport, generation);\n } catch (err) {\n if (!isCurrentConnection(generation)) return;\n\n if (err instanceof UnauthorizedError) {\n // OAuth: keep the transport alive so completeAuth can call\n // finishAuth on it. Closing it before storing would leave a\n // closed transport on transportRef.\n pendingTransportRef.current = null;\n transportRef.current = transport;\n setConnectionState(\"authRequired\");\n } else {\n cancelPendingElicitations();\n if (transport) {\n pendingTransportRef.current = null;\n await closeQueuedTransports([transport]);\n }\n setLastError({\n message: err instanceof Error ? err.message : String(err),\n });\n setConnectionState(\"error\");\n }\n }\n });\n\n const doDisconnect = useEffectEvent(async () => {\n connectionGenerationRef.current += 1;\n setTools([]);\n setAuthorizationUrl(null);\n setConnectionState(\"disconnected\");\n await closeTransports();\n });\n\n const doCompleteAuth = useEffectEvent(async (callbackUrl: string) => {\n const validationGeneration = connectionGenerationRef.current;\n const url = new URL(callbackUrl);\n const state = url.searchParams.get(\"state\");\n if (!state) throw new Error('missing \"state\" parameter');\n let pendingAuthValidation = pendingAuthValidationRef.current;\n if (!pendingAuthValidation) {\n let resolve!: () => void;\n const promise = new Promise<void>((resolvePromise) => {\n resolve = resolvePromise;\n });\n pendingAuthValidation = { count: 0, promise, resolve };\n pendingAuthValidationRef.current = pendingAuthValidation;\n }\n pendingAuthValidation.count += 1;\n try {\n const persisted = await props.storage.loadAuthState(props.id);\n if (!isCurrentConnection(validationGeneration)) {\n throw createInterruptedAuthError();\n }\n if (!persisted?.state) {\n throw new Error(\n \"no pending OAuth authorization request for this server\",\n );\n }\n if (persisted.state !== state) {\n throw new Error(\"OAuth state does not match the authorization request\");\n }\n if (!url.searchParams.get(\"code\") && !url.searchParams.get(\"error\")) {\n throw new Error(\"missing authorization code in callback URL\");\n }\n } finally {\n pendingAuthValidation.count -= 1;\n if (pendingAuthValidation.count === 0) {\n pendingAuthValidationRef.current = null;\n pendingAuthValidation.resolve();\n }\n }\n\n // Claim the generation before a waiting auto-connect can resume.\n const generation = ++connectionGenerationRef.current;\n cancelPendingElicitations();\n await closePendingTransport();\n if (!isCurrentConnection(generation)) throw createInterruptedAuthError();\n\n setConnectionState(\"authPending\");\n setLastError(null);\n try {\n let transport = transportRef.current;\n if (!transport) {\n transport = await buildTransport();\n if (!isCurrentConnection(generation)) {\n await closeQueuedTransports([transport]);\n throw createInterruptedAuthError();\n }\n }\n transportRef.current = null;\n clientRef.current = null;\n pendingTransportRef.current = transport;\n await transport.finishAuth(url.searchParams);\n if (!isCurrentConnection(generation)) throw createInterruptedAuthError();\n setAuthorizationUrl(null);\n const connected = await finalizeConnect(transport, generation);\n if (!connected) throw createInterruptedAuthError();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n if (!isCurrentConnection(generation))\n throw createInterruptedAuthError(error);\n\n await closeTransports();\n setLastError({\n message: error.message,\n });\n setConnectionState(\"error\");\n throw error;\n }\n });\n\n const tryAutoConnect = useEffectEvent(\n async (signal: { cancelled: boolean }) => {\n if (!props.autoConnect) return;\n if (props.auth.type === \"none\") {\n void doConnect();\n return;\n }\n // Static connector token short-circuits the storage read.\n if (props.auth.type === \"bearer\" && props.auth.token) {\n void doConnect();\n return;\n }\n const generation = connectionGenerationRef.current;\n let persisted: Awaited<ReturnType<MCPStorage[\"loadAuthState\"]>>;\n try {\n persisted = await props.storage.loadAuthState(props.id);\n } catch (error) {\n if (signal.cancelled || !isCurrentConnection(generation)) return;\n const message = error instanceof Error ? error.message : String(error);\n setLastError({\n message: `MCP server \"${props.id}\" failed to load saved authentication: ${message}`,\n });\n setConnectionState(\"error\");\n return;\n }\n if (signal.cancelled || !isCurrentConnection(generation)) return;\n if (props.auth.type === \"oauth\") {\n if (!persisted?.tokens) return;\n } else if (!persisted?.token) {\n return;\n }\n const pendingAuthValidation = pendingAuthValidationRef.current;\n if (pendingAuthValidation) {\n await pendingAuthValidation.promise;\n if (signal.cancelled || !isCurrentConnection(generation)) return;\n }\n void doConnect();\n },\n );\n\n // Auto-connect on mount when usable auth exists.\n useEffect(() => {\n const transportCloseQueueRef = props.transportCloseQueueRef;\n const previousDisposal = pendingDisposalRef.current;\n if (previousDisposal) previousDisposal.cancelled = true;\n const pendingDisposal = { cancelled: false };\n pendingDisposalRef.current = pendingDisposal;\n mountedRef.current = true;\n const signal = { cancelled: false };\n void tryAutoConnect(signal);\n return () => {\n mountedRef.current = false;\n signal.cancelled = true;\n const task = transportCloseQueueRef.current.then(async () => {\n await new Promise<void>((resolve) => queueMicrotask(resolve));\n if (pendingDisposal.cancelled) return;\n connectionGenerationRef.current += 1;\n await Promise.all(detachTransports().map(closeTransportSafely));\n });\n transportCloseQueueRef.current = task;\n };\n }, [props.transportCloseQueueRef]);\n\n const state = useMemo<MCPServerState>(\n () => ({\n id: props.id,\n kind: props.kind,\n name: props.name,\n url: props.url,\n icon: props.icon,\n connectionState,\n lastError,\n tools,\n authorizationUrl,\n pendingElicitations,\n }),\n [\n props.id,\n props.kind,\n props.name,\n props.url,\n props.icon,\n connectionState,\n lastError,\n tools,\n authorizationUrl,\n pendingElicitations,\n ],\n );\n\n return {\n getState: () => state,\n connect: doConnect,\n disconnect: doDisconnect,\n remove: async () => {\n await doDisconnect();\n try {\n await clearOAuthProviderAuthState(props.storage, props.id);\n await props.onRemove();\n } catch (err) {\n setLastError({\n message: err instanceof Error ? err.message : String(err),\n });\n throw err;\n }\n },\n callTool: async (name, args) => {\n const client = clientRef.current;\n if (!client) {\n throw new Error(`MCP server \"${props.id}\" is not connected`);\n }\n return await client.callTool({\n name,\n arguments: args as Record<string, unknown> | undefined,\n });\n },\n listResources: async (params) => {\n const client = clientRef.current;\n if (!client) {\n throw new Error(`MCP server \"${props.id}\" is not connected`);\n }\n return await client.listResources(params);\n },\n readResource: async (uri) => {\n const client = clientRef.current;\n if (!client) {\n throw new Error(`MCP server \"${props.id}\" is not connected`);\n }\n return await client.readResource({ uri });\n },\n completeAuth: doCompleteAuth,\n answerElicitation: (\n id: string,\n response: MCPElicitationResponse,\n ): readonly { property: string; message: string }[] | undefined => {\n if (response.action === \"accept\") {\n const entry = elicitationResolversRef.current.get(id);\n if (!entry) return;\n\n if (\n typeof response.content !== \"object\" ||\n response.content === null ||\n Array.isArray(response.content)\n ) {\n const errors = [\n {\n property: \"content\",\n message: \"Response content must be an object.\",\n },\n ];\n setPendingElicitationError(id, {\n message: \"Invalid elicitation content: content.\",\n properties: [\"content\"],\n });\n return errors;\n }\n\n const errors = validateElicitationContent(\n entry.requestedSchema,\n response.content,\n );\n if (errors.length > 0) {\n const properties = [\n ...new Set(errors.map((error) => error.property)),\n ];\n setPendingElicitationError(id, {\n message: `Invalid elicitation content: ${properties.join(\", \")}.`,\n properties,\n });\n return errors;\n }\n\n const result: ElicitResult = {\n action: \"accept\",\n content: response.content as ElicitResult[\"content\"],\n };\n resolvePendingElicitation(id, result);\n return;\n }\n\n resolvePendingElicitation(id, { action: response.action });\n },\n };\n};\n\nconst McpServerResourceInstance = resource(useMcpServerResourceInstance);\n\nexport const McpServerResource = resource(function useMcpServerResource(\n props: McpServerResourceProps,\n): ClientOutput<\"mcpServer\"> {\n const transportCloseQueueRef = useRef(Promise.resolve());\n const dependencies = getConnectionDependencies(props);\n const [connection, setConnection] = useState({ dependencies, generation: 0 });\n let currentConnection = connection;\n if (!areConnectionDependenciesEqual(connection.dependencies, dependencies)) {\n currentConnection = {\n dependencies,\n generation: connection.generation + 1,\n };\n setConnection(currentConnection);\n }\n\n // Storage keys remounts through its optional scopeId rather than object\n // identity, because a defaulted storage element is rebuilt on ordinary\n // renders.\n return useResource(\n withKey(\n currentConnection.generation,\n McpServerResourceInstance({ ...props, transportCloseQueueRef }),\n ),\n );\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"McpServerResource.js","names":["useState","useRef","useEffect","useMemo","useEffectEvent","resource","useResource","withKey","ClientOutput","shallowEqual","Client","StreamableHTTPClientTransport","UnauthorizedError","ClientOptions","ElicitRequest","ElicitResult","StreamableHTTPClientTransportOptions","clearOAuthProviderAuthState","createOAuthProvider","hasUsableOAuthTokens","buildHeaders","assertValidServerId","hasPersistedCredentials","isAuthStateForServerUrl","validateElicitationContent","MCPStorage","MCPAuthConfig","MCPConnectionState","MCPElicitation","MCPElicitationResponse","MCPServerKind","MCPServerState","MCPToolInfo","createMcpId","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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpLocalStorage.d.ts","names":[],"sources":["../../../src/resources/storage/McpLocalStorage.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"McpLocalStorage.d.ts","names":[],"sources":["../../../src/resources/storage/McpLocalStorage.ts"],"mappings":";;;;KAcY;;EAEV;;EAEA,UAAU;;;;;;;EAOV;;cAiGW,+BACX,mBACC;cAgFU,8BACX,mBACC;cAiGU,6CAAe,SAAA,aAAA,OAAA"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assertValidServerId } from "../../utils/serverId.js";
|
|
2
|
+
import { normalizeMcpServerUrl } from "../../utils/serverUrl.js";
|
|
2
3
|
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
3
4
|
import "@assistant-ui/tap/react-shim";
|
|
4
5
|
import { resource } from "@assistant-ui/tap";
|
|
@@ -72,6 +73,15 @@ const isSecureNetworkUrl = (value) => {
|
|
|
72
73
|
return false;
|
|
73
74
|
}
|
|
74
75
|
};
|
|
76
|
+
const isMcpServerUrl = (value) => {
|
|
77
|
+
if (typeof value !== "string") return false;
|
|
78
|
+
try {
|
|
79
|
+
const url = new URL(value);
|
|
80
|
+
return url.protocol === "https:" || url.protocol === "http:";
|
|
81
|
+
} catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
75
85
|
const normalizeDiscoveryState = (value) => {
|
|
76
86
|
if (!isRecord(value) || !isSecureNetworkUrl(value.authorizationServerUrl)) return;
|
|
77
87
|
const state = { authorizationServerUrl: value.authorizationServerUrl };
|
|
@@ -84,14 +94,18 @@ const normalizeDiscoveryState = (value) => {
|
|
|
84
94
|
};
|
|
85
95
|
const normalizePersistedAuthState = (value) => {
|
|
86
96
|
if (!isRecord(value)) return null;
|
|
97
|
+
if ("serverUrl" in value && !isMcpServerUrl(value.serverUrl)) return null;
|
|
87
98
|
const state = {};
|
|
99
|
+
if (isMcpServerUrl(value.serverUrl)) state.serverUrl = normalizeMcpServerUrl(value.serverUrl);
|
|
88
100
|
if (isNonEmptyString(value.token)) state.token = value.token;
|
|
101
|
+
if (isNonEmptyString(value.tokensClientId)) state.tokensClientId = value.tokensClientId;
|
|
89
102
|
if (isNonEmptyString(value.codeVerifier)) state.codeVerifier = value.codeVerifier;
|
|
90
103
|
if (isNonEmptyString(value.state)) state.state = value.state;
|
|
91
104
|
const tokens = normalizeOAuthTokens(value.tokens);
|
|
92
105
|
if (tokens) state.tokens = tokens;
|
|
93
106
|
const clientInformation = normalizeClientInformation(value.clientInformation);
|
|
94
107
|
if (clientInformation) state.clientInformation = clientInformation;
|
|
108
|
+
if (value.clientInformationSource === "registered") state.clientInformationSource = value.clientInformationSource;
|
|
95
109
|
const discoveryState = normalizeDiscoveryState(value.discoveryState);
|
|
96
110
|
if (discoveryState) state.discoveryState = discoveryState;
|
|
97
111
|
return Object.keys(state).length > 0 ? state : null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpLocalStorage.js","names":["resource","useMemo","OAuthMetadataSchema","OAuthClientInformationFullSchema","OAuthProtectedResourceMetadataSchema","OAuthTokensSchema","MCPAuthConfig","MCPCustomServerRecord","MCPPersistedAuthState","assertValidServerId","MCPStorage","McpLocalStorageOptions","keyPrefix","storage","Storage","scopeId","resolveStorage","opts","globalThis","localStorage","isRecord","value","Record","Array","isArray","isOptionalString","undefined","isNonEmptyString","trim","length","isOptionalNonEmptyString","isOptionalStringArray","every","item","isOptionalConnectionTimeout","Number","isFinite","isValidServerId","id","isMCPAuthConfig","auth","type","token","scopes","authorizationEndpoint","tokenEndpoint","registrationEndpoint","clientId","clientSecret","isCustomServerRecord","name","url","createdAt","connectionTimeout","normalizeCustomServerRecord","record","normalizeCustomServerRecords","flatMap","normalizeOAuthTokens","result","safeParse","success","data","normalizeClientInformation","isSecureNetworkUrl","URL","protocol","hostname","endsWith","startsWith","normalizeDiscoveryState","authorizationServerUrl","state","NonNullable","resourceMetadataUrl","metadata","authorizationServerMetadata","resourceMetadata","normalizePersistedAuthState","codeVerifier","tokens","clientInformation","discoveryState","Object","keys","useMcpLocalStorage","t0","$","_c","t1","prefix","t2","customServersKey","t3","authKey","t4","key","fallback","raw","getItem","JSON","parse","T","read","t5","key_0","setItem","stringify","write","t6","key_1","removeItem","remove","t7","t8","t9","records","t10","id_0","t11","id_1","t12","id_2","t13","loadCustomServers","saveCustomServers","loadAuthState","saveAuthState","clearAuthState","McpLocalStorage"],"sources":["../../../src/resources/storage/McpLocalStorage.ts"],"sourcesContent":["import { resource } from \"@assistant-ui/tap\";\nimport { useMemo } from \"react\";\nimport {\n OAuthMetadataSchema,\n OAuthClientInformationFullSchema,\n OAuthProtectedResourceMetadataSchema,\n OAuthTokensSchema,\n} from \"@modelcontextprotocol/core\";\nimport type { MCPAuthConfig, MCPCustomServerRecord } from \"../../mcp-scope\";\nimport type { MCPPersistedAuthState } from \"../../auth/types\";\nimport { assertValidServerId } from \"../../utils/serverId\";\nimport type { MCPStorage } from \"./types\";\n\nexport type McpLocalStorageOptions = {\n /** Namespace prefix for keys. Default \"aui-mcp\". */\n keyPrefix?: string;\n /** Override the underlying Storage. Defaults to globalThis.localStorage. */\n storage?: Storage;\n /**\n * Stable identity for the backing data, used to key server reconnects.\n * Required to get reconnect-on-swap behavior when `storage` is overridden;\n * without it a custom backing store declares no scope, since a prefix\n * alone cannot distinguish two different stores.\n */\n scopeId?: string;\n};\n\nfunction resolveStorage(opts: McpLocalStorageOptions): Storage | null {\n if (opts.storage) return opts.storage;\n if (typeof globalThis !== \"undefined\" && \"localStorage\" in globalThis) {\n try {\n return (globalThis as { localStorage: Storage }).localStorage;\n } catch {\n return null;\n }\n }\n return null;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isOptionalString = (value: unknown): value is string | undefined =>\n value === undefined || typeof value === \"string\";\n\nconst isNonEmptyString = (value: unknown): value is string =>\n typeof value === \"string\" && value.trim().length > 0;\n\nconst isOptionalNonEmptyString = (\n value: unknown,\n): value is string | undefined =>\n value === undefined || isNonEmptyString(value);\n\nconst isOptionalStringArray = (value: unknown): value is string[] | undefined =>\n value === undefined ||\n (Array.isArray(value) && value.every((item) => typeof item === \"string\"));\n\nconst isOptionalConnectionTimeout = (\n value: unknown,\n): value is number | undefined =>\n value === undefined ||\n (typeof value === \"number\" && Number.isFinite(value) && value >= 0);\n\nconst isValidServerId = (id: string): boolean => {\n try {\n assertValidServerId(id);\n return true;\n } catch {\n return false;\n }\n};\n\nconst isMCPAuthConfig = (auth: unknown): auth is MCPAuthConfig => {\n if (!isRecord(auth)) return false;\n\n switch (auth.type) {\n case \"none\":\n return true;\n case \"bearer\":\n return isOptionalNonEmptyString(auth.token);\n case \"oauth\":\n return (\n isOptionalStringArray(auth.scopes) &&\n isOptionalString(auth.authorizationEndpoint) &&\n isOptionalString(auth.tokenEndpoint) &&\n isOptionalString(auth.registrationEndpoint) &&\n isOptionalString(auth.clientId) &&\n isOptionalString(auth.clientSecret)\n );\n default:\n return false;\n }\n};\n\nconst isCustomServerRecord = (\n value: unknown,\n): value is MCPCustomServerRecord => {\n if (!isRecord(value)) return false;\n if (typeof value.id !== \"string\" || !isValidServerId(value.id)) {\n return false;\n }\n return (\n isNonEmptyString(value.name) &&\n isNonEmptyString(value.url) &&\n Number.isFinite(value.createdAt) &&\n isMCPAuthConfig(value.auth) &&\n isOptionalConnectionTimeout(value.connectionTimeout)\n );\n};\n\nconst normalizeCustomServerRecord = (\n value: unknown,\n): MCPCustomServerRecord | null => {\n if (isCustomServerRecord(value)) return value;\n if (!isRecord(value)) return null;\n\n const record = { ...value };\n delete record.connectionTimeout;\n return isCustomServerRecord(record) ? record : null;\n};\n\nexport const normalizeCustomServerRecords = (\n value: unknown,\n): MCPCustomServerRecord[] => {\n if (!Array.isArray(value)) return [];\n return value.flatMap((item) => {\n const record = normalizeCustomServerRecord(item);\n return record === null ? [] : [record];\n });\n};\n\nconst normalizeOAuthTokens = (\n value: unknown,\n): MCPPersistedAuthState[\"tokens\"] | undefined => {\n const result = OAuthTokensSchema.safeParse(value);\n return result.success ? result.data : undefined;\n};\n\nconst normalizeClientInformation = (\n value: unknown,\n): MCPPersistedAuthState[\"clientInformation\"] | undefined => {\n const result = OAuthClientInformationFullSchema.safeParse(value);\n return result.success ? result.data : undefined;\n};\n\nconst isSecureNetworkUrl = (value: unknown): value is string => {\n if (!isNonEmptyString(value)) return false;\n try {\n const url = new URL(value);\n return (\n url.protocol === \"https:\" ||\n (url.protocol === \"http:\" &&\n (url.hostname === \"localhost\" ||\n url.hostname.endsWith(\".localhost\") ||\n url.hostname.startsWith(\"127.\") ||\n url.hostname === \"[::1]\"))\n );\n } catch {\n return false;\n }\n};\n\nconst normalizeDiscoveryState = (\n value: unknown,\n): MCPPersistedAuthState[\"discoveryState\"] | undefined => {\n if (!isRecord(value) || !isSecureNetworkUrl(value.authorizationServerUrl)) {\n return undefined;\n }\n\n // A malformed optional field is dropped alone: keeping the validated\n // authorization server URL preserves the redirect-time binding, and the SDK\n // re-discovers whatever metadata is missing.\n const state: NonNullable<MCPPersistedAuthState[\"discoveryState\"]> = {\n authorizationServerUrl: value.authorizationServerUrl,\n };\n\n if (isSecureNetworkUrl(value.resourceMetadataUrl)) {\n state.resourceMetadataUrl = value.resourceMetadataUrl;\n }\n\n const metadata = OAuthMetadataSchema.safeParse(\n value.authorizationServerMetadata,\n );\n if (metadata.success) state.authorizationServerMetadata = metadata.data;\n\n const resourceMetadata = OAuthProtectedResourceMetadataSchema.safeParse(\n value.resourceMetadata,\n );\n if (resourceMetadata.success) state.resourceMetadata = resourceMetadata.data;\n\n return state;\n};\n\nexport const normalizePersistedAuthState = (\n value: unknown,\n): MCPPersistedAuthState | null => {\n if (!isRecord(value)) return null;\n\n const state: MCPPersistedAuthState = {};\n if (isNonEmptyString(value.token)) state.token = value.token;\n if (isNonEmptyString(value.codeVerifier)) {\n state.codeVerifier = value.codeVerifier;\n }\n if (isNonEmptyString(value.state)) state.state = value.state;\n\n const tokens = normalizeOAuthTokens(value.tokens);\n if (tokens) state.tokens = tokens;\n\n const clientInformation = normalizeClientInformation(value.clientInformation);\n if (clientInformation) state.clientInformation = clientInformation;\n\n const discoveryState = normalizeDiscoveryState(value.discoveryState);\n if (discoveryState) state.discoveryState = discoveryState;\n\n return Object.keys(state).length > 0 ? state : null;\n};\n\nconst useMcpLocalStorage = (opts: McpLocalStorageOptions = {}): MCPStorage => {\n const prefix = opts.keyPrefix ?? \"aui-mcp\";\n const storage = resolveStorage(opts);\n // Deriving a scope from the prefix is only honest for the shared\n // globalThis.localStorage; two custom backing stores under one prefix hold\n // different data, so an overridden backing declares no scope unless the\n // caller names one.\n const scopeId =\n opts.scopeId ??\n (opts.storage === undefined ? `local-storage:${prefix}` : undefined);\n\n // Callers key per-server coordination state on this instance, so it has to\n // stay referentially stable for as long as the underlying store does.\n return useMemo(() => {\n const customServersKey = `${prefix}:custom-servers`;\n const authKey = (id: string) => `${prefix}:auth:${id}`;\n\n const read = <T>(key: string, fallback: T): T => {\n if (!storage) return fallback;\n try {\n const raw = storage.getItem(key);\n if (raw == null) return fallback;\n return JSON.parse(raw) as T;\n } catch {\n return fallback;\n }\n };\n\n const write = (key: string, value: unknown): void => {\n if (!storage) return;\n try {\n storage.setItem(key, JSON.stringify(value));\n } catch {\n // quota or serialization failure — silently drop\n }\n };\n\n const remove = (key: string): void => {\n if (!storage) return;\n try {\n storage.removeItem(key);\n } catch {\n // ignore\n }\n };\n\n return {\n ...(scopeId !== undefined ? { scopeId } : {}),\n loadCustomServers: async () =>\n normalizeCustomServerRecords(read<unknown>(customServersKey, [])),\n saveCustomServers: async (records) => {\n write(customServersKey, records);\n },\n loadAuthState: async (id) =>\n normalizePersistedAuthState(read<unknown>(authKey(id), null)),\n saveAuthState: async (id, state) => {\n write(authKey(id), state);\n },\n clearAuthState: async (id) => {\n remove(authKey(id));\n },\n };\n }, [prefix, storage, scopeId]);\n};\n\nexport const McpLocalStorage = resource(useMcpLocalStorage);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"McpLocalStorage.js","names":["resource","useMemo","OAuthMetadataSchema","OAuthClientInformationFullSchema","OAuthProtectedResourceMetadataSchema","OAuthTokensSchema","normalizeMcpServerUrl","MCPAuthConfig","MCPCustomServerRecord","MCPPersistedAuthState","assertValidServerId","MCPStorage","McpLocalStorageOptions","keyPrefix","storage","Storage","scopeId","resolveStorage","opts","globalThis","localStorage","isRecord","value","Record","Array","isArray","isOptionalString","undefined","isNonEmptyString","trim","length","isOptionalNonEmptyString","isOptionalStringArray","every","item","isOptionalConnectionTimeout","Number","isFinite","isValidServerId","id","isMCPAuthConfig","auth","type","token","scopes","authorizationEndpoint","tokenEndpoint","registrationEndpoint","clientId","clientSecret","isCustomServerRecord","name","url","createdAt","connectionTimeout","normalizeCustomServerRecord","record","normalizeCustomServerRecords","flatMap","normalizeOAuthTokens","result","safeParse","success","data","normalizeClientInformation","isSecureNetworkUrl","URL","protocol","hostname","endsWith","startsWith","isMcpServerUrl","normalizeDiscoveryState","authorizationServerUrl","state","NonNullable","resourceMetadataUrl","metadata","authorizationServerMetadata","resourceMetadata","normalizePersistedAuthState","serverUrl","tokensClientId","codeVerifier","tokens","clientInformation","clientInformationSource","discoveryState","Object","keys","useMcpLocalStorage","t0","$","_c","t1","prefix","t2","customServersKey","t3","authKey","t4","key","fallback","raw","getItem","JSON","parse","T","read","t5","key_0","setItem","stringify","write","t6","key_1","removeItem","remove","t7","t8","t9","records","t10","id_0","t11","id_1","t12","id_2","t13","loadCustomServers","saveCustomServers","loadAuthState","saveAuthState","clearAuthState","McpLocalStorage"],"sources":["../../../src/resources/storage/McpLocalStorage.ts"],"sourcesContent":["import { resource } from \"@assistant-ui/tap\";\nimport { useMemo } from \"react\";\nimport {\n OAuthMetadataSchema,\n OAuthClientInformationFullSchema,\n OAuthProtectedResourceMetadataSchema,\n OAuthTokensSchema,\n} from \"@modelcontextprotocol/core\";\nimport { normalizeMcpServerUrl } from \"../../utils/serverUrl\";\nimport type { MCPAuthConfig, MCPCustomServerRecord } from \"../../mcp-scope\";\nimport type { MCPPersistedAuthState } from \"../../auth/types\";\nimport { assertValidServerId } from \"../../utils/serverId\";\nimport type { MCPStorage } from \"./types\";\n\nexport type McpLocalStorageOptions = {\n /** Namespace prefix for keys. Default \"aui-mcp\". */\n keyPrefix?: string;\n /** Override the underlying Storage. Defaults to globalThis.localStorage. */\n storage?: Storage;\n /**\n * Stable identity for the backing data, used to key server reconnects.\n * Required to get reconnect-on-swap behavior when `storage` is overridden;\n * without it a custom backing store declares no scope, since a prefix\n * alone cannot distinguish two different stores.\n */\n scopeId?: string;\n};\n\nfunction resolveStorage(opts: McpLocalStorageOptions): Storage | null {\n if (opts.storage) return opts.storage;\n if (typeof globalThis !== \"undefined\" && \"localStorage\" in globalThis) {\n try {\n return (globalThis as { localStorage: Storage }).localStorage;\n } catch {\n return null;\n }\n }\n return null;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isOptionalString = (value: unknown): value is string | undefined =>\n value === undefined || typeof value === \"string\";\n\nconst isNonEmptyString = (value: unknown): value is string =>\n typeof value === \"string\" && value.trim().length > 0;\n\nconst isOptionalNonEmptyString = (\n value: unknown,\n): value is string | undefined =>\n value === undefined || isNonEmptyString(value);\n\nconst isOptionalStringArray = (value: unknown): value is string[] | undefined =>\n value === undefined ||\n (Array.isArray(value) && value.every((item) => typeof item === \"string\"));\n\nconst isOptionalConnectionTimeout = (\n value: unknown,\n): value is number | undefined =>\n value === undefined ||\n (typeof value === \"number\" && Number.isFinite(value) && value >= 0);\n\nconst isValidServerId = (id: string): boolean => {\n try {\n assertValidServerId(id);\n return true;\n } catch {\n return false;\n }\n};\n\nconst isMCPAuthConfig = (auth: unknown): auth is MCPAuthConfig => {\n if (!isRecord(auth)) return false;\n\n switch (auth.type) {\n case \"none\":\n return true;\n case \"bearer\":\n return isOptionalNonEmptyString(auth.token);\n case \"oauth\":\n return (\n isOptionalStringArray(auth.scopes) &&\n isOptionalString(auth.authorizationEndpoint) &&\n isOptionalString(auth.tokenEndpoint) &&\n isOptionalString(auth.registrationEndpoint) &&\n isOptionalString(auth.clientId) &&\n isOptionalString(auth.clientSecret)\n );\n default:\n return false;\n }\n};\n\nconst isCustomServerRecord = (\n value: unknown,\n): value is MCPCustomServerRecord => {\n if (!isRecord(value)) return false;\n if (typeof value.id !== \"string\" || !isValidServerId(value.id)) {\n return false;\n }\n return (\n isNonEmptyString(value.name) &&\n isNonEmptyString(value.url) &&\n Number.isFinite(value.createdAt) &&\n isMCPAuthConfig(value.auth) &&\n isOptionalConnectionTimeout(value.connectionTimeout)\n );\n};\n\nconst normalizeCustomServerRecord = (\n value: unknown,\n): MCPCustomServerRecord | null => {\n if (isCustomServerRecord(value)) return value;\n if (!isRecord(value)) return null;\n\n const record = { ...value };\n delete record.connectionTimeout;\n return isCustomServerRecord(record) ? record : null;\n};\n\nexport const normalizeCustomServerRecords = (\n value: unknown,\n): MCPCustomServerRecord[] => {\n if (!Array.isArray(value)) return [];\n return value.flatMap((item) => {\n const record = normalizeCustomServerRecord(item);\n return record === null ? [] : [record];\n });\n};\n\nconst normalizeOAuthTokens = (\n value: unknown,\n): MCPPersistedAuthState[\"tokens\"] | undefined => {\n const result = OAuthTokensSchema.safeParse(value);\n return result.success ? result.data : undefined;\n};\n\nconst normalizeClientInformation = (\n value: unknown,\n): MCPPersistedAuthState[\"clientInformation\"] | undefined => {\n const result = OAuthClientInformationFullSchema.safeParse(value);\n return result.success ? result.data : undefined;\n};\n\nconst isSecureNetworkUrl = (value: unknown): value is string => {\n if (!isNonEmptyString(value)) return false;\n try {\n const url = new URL(value);\n return (\n url.protocol === \"https:\" ||\n (url.protocol === \"http:\" &&\n (url.hostname === \"localhost\" ||\n url.hostname.endsWith(\".localhost\") ||\n url.hostname.startsWith(\"127.\") ||\n url.hostname === \"[::1]\"))\n );\n } catch {\n return false;\n }\n};\n\nconst isMcpServerUrl = (value: unknown): value is string => {\n if (typeof value !== \"string\") return false;\n try {\n const url = new URL(value);\n return url.protocol === \"https:\" || url.protocol === \"http:\";\n } catch {\n return false;\n }\n};\n\nconst normalizeDiscoveryState = (\n value: unknown,\n): MCPPersistedAuthState[\"discoveryState\"] | undefined => {\n if (!isRecord(value) || !isSecureNetworkUrl(value.authorizationServerUrl)) {\n return undefined;\n }\n\n // A malformed optional field is dropped alone: keeping the validated\n // authorization server URL preserves the redirect-time binding, and the SDK\n // re-discovers whatever metadata is missing.\n const state: NonNullable<MCPPersistedAuthState[\"discoveryState\"]> = {\n authorizationServerUrl: value.authorizationServerUrl,\n };\n\n if (isSecureNetworkUrl(value.resourceMetadataUrl)) {\n state.resourceMetadataUrl = value.resourceMetadataUrl;\n }\n\n const metadata = OAuthMetadataSchema.safeParse(\n value.authorizationServerMetadata,\n );\n if (metadata.success) state.authorizationServerMetadata = metadata.data;\n\n const resourceMetadata = OAuthProtectedResourceMetadataSchema.safeParse(\n value.resourceMetadata,\n );\n if (resourceMetadata.success) state.resourceMetadata = resourceMetadata.data;\n\n return state;\n};\n\nexport const normalizePersistedAuthState = (\n value: unknown,\n): MCPPersistedAuthState | null => {\n if (!isRecord(value)) return null;\n if (\"serverUrl\" in value && !isMcpServerUrl(value.serverUrl)) return null;\n\n const state: MCPPersistedAuthState = {};\n if (isMcpServerUrl(value.serverUrl)) {\n state.serverUrl = normalizeMcpServerUrl(value.serverUrl);\n }\n if (isNonEmptyString(value.token)) state.token = value.token;\n if (isNonEmptyString(value.tokensClientId)) {\n state.tokensClientId = value.tokensClientId;\n }\n if (isNonEmptyString(value.codeVerifier)) {\n state.codeVerifier = value.codeVerifier;\n }\n if (isNonEmptyString(value.state)) state.state = value.state;\n\n const tokens = normalizeOAuthTokens(value.tokens);\n if (tokens) state.tokens = tokens;\n\n const clientInformation = normalizeClientInformation(value.clientInformation);\n if (clientInformation) state.clientInformation = clientInformation;\n if (value.clientInformationSource === \"registered\") {\n state.clientInformationSource = value.clientInformationSource;\n }\n\n const discoveryState = normalizeDiscoveryState(value.discoveryState);\n if (discoveryState) state.discoveryState = discoveryState;\n\n return Object.keys(state).length > 0 ? state : null;\n};\n\nconst useMcpLocalStorage = (opts: McpLocalStorageOptions = {}): MCPStorage => {\n const prefix = opts.keyPrefix ?? \"aui-mcp\";\n const storage = resolveStorage(opts);\n // Deriving a scope from the prefix is only honest for the shared\n // globalThis.localStorage; two custom backing stores under one prefix hold\n // different data, so an overridden backing declares no scope unless the\n // caller names one.\n const scopeId =\n opts.scopeId ??\n (opts.storage === undefined ? `local-storage:${prefix}` : undefined);\n\n // Callers key per-server coordination state on this instance, so it has to\n // stay referentially stable for as long as the underlying store does.\n return useMemo(() => {\n const customServersKey = `${prefix}:custom-servers`;\n const authKey = (id: string) => `${prefix}:auth:${id}`;\n\n const read = <T>(key: string, fallback: T): T => {\n if (!storage) return fallback;\n try {\n const raw = storage.getItem(key);\n if (raw == null) return fallback;\n return JSON.parse(raw) as T;\n } catch {\n return fallback;\n }\n };\n\n const write = (key: string, value: unknown): void => {\n if (!storage) return;\n try {\n storage.setItem(key, JSON.stringify(value));\n } catch {\n // quota or serialization failure — silently drop\n }\n };\n\n const remove = (key: string): void => {\n if (!storage) return;\n try {\n storage.removeItem(key);\n } catch {\n // ignore\n }\n };\n\n return {\n ...(scopeId !== undefined ? { scopeId } : {}),\n loadCustomServers: async () =>\n normalizeCustomServerRecords(read<unknown>(customServersKey, [])),\n saveCustomServers: async (records) => {\n write(customServersKey, records);\n },\n loadAuthState: async (id) =>\n normalizePersistedAuthState(read<unknown>(authKey(id), null)),\n saveAuthState: async (id, state) => {\n write(authKey(id), state);\n },\n clearAuthState: async (id) => {\n remove(authKey(id));\n },\n };\n }, [prefix, storage, scopeId]);\n};\n\nexport const McpLocalStorage = resource(useMcpLocalStorage);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { MCPPersistedAuthState } from "../auth/types.js";
|
|
2
|
+
//#region src/utils/serverUrl.d.ts
|
|
3
|
+
declare const normalizeMcpServerUrl: (serverUrl: string) => string;
|
|
4
|
+
declare const isAuthStateForServerUrl: (state: MCPPersistedAuthState | null, serverUrl: string) => boolean;
|
|
5
|
+
declare const hasPersistedCredentials: (state: MCPPersistedAuthState | null) => boolean;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { hasPersistedCredentials, isAuthStateForServerUrl, normalizeMcpServerUrl };
|
|
8
|
+
//# sourceMappingURL=serverUrl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serverUrl.d.ts","names":[],"sources":["../../src/utils/serverUrl.ts"],"mappings":";;cAEa,wBAAyB;cAGzB,0BACX,OAAO,8BACP;cAaW,0BACX,OAAO"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/utils/serverUrl.ts
|
|
2
|
+
const normalizeMcpServerUrl = (serverUrl) => new URL(serverUrl).toString();
|
|
3
|
+
const isAuthStateForServerUrl = (state, serverUrl) => {
|
|
4
|
+
if (state?.serverUrl === void 0) return false;
|
|
5
|
+
try {
|
|
6
|
+
return normalizeMcpServerUrl(state.serverUrl) === normalizeMcpServerUrl(serverUrl);
|
|
7
|
+
} catch {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
const hasPersistedCredentials = (state) => Boolean(state?.tokens) || Boolean(state?.token);
|
|
12
|
+
//#endregion
|
|
13
|
+
export { hasPersistedCredentials, isAuthStateForServerUrl, normalizeMcpServerUrl };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=serverUrl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serverUrl.js","names":["MCPPersistedAuthState","normalizeMcpServerUrl","serverUrl","URL","toString","isAuthStateForServerUrl","state","undefined","hasPersistedCredentials","Boolean","tokens","token"],"sources":["../../src/utils/serverUrl.ts"],"sourcesContent":["import type { MCPPersistedAuthState } from \"../auth/types\";\n\nexport const normalizeMcpServerUrl = (serverUrl: string): string =>\n new URL(serverUrl).toString();\n\nexport const isAuthStateForServerUrl = (\n state: MCPPersistedAuthState | null,\n serverUrl: string,\n): boolean => {\n if (state?.serverUrl === undefined) return false;\n try {\n return (\n normalizeMcpServerUrl(state.serverUrl) ===\n normalizeMcpServerUrl(serverUrl)\n );\n } catch {\n return false;\n }\n};\n\nexport const hasPersistedCredentials = (\n state: MCPPersistedAuthState | null,\n): boolean => Boolean(state?.tokens) || Boolean(state?.token);\n"],"mappings":";AAEA,MAAaC,yBAAyBC,cACpC,IAAIC,IAAID,SAAS,CAAC,CAACE,SAAS;AAE9B,MAAaC,2BACXC,OACAJ,cACY;CACZ,IAAII,OAAOJ,cAAcK,KAAAA,GAAW,OAAO;CAC3C,IAAI;EACF,OACEN,sBAAsBK,MAAMJ,SAAS,MACrCD,sBAAsBC,SAAS;CAEnC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAaM,2BACXF,UACYG,QAAQH,OAAOI,MAAM,KAAKD,QAAQH,OAAOK,KAAK"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "MCP server configuration and connection primitives for @assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -27,13 +27,13 @@
|
|
|
27
27
|
],
|
|
28
28
|
"sideEffects": false,
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@assistant-ui/core": "^0.3.
|
|
31
|
-
"@assistant-ui/store": "^0.3.
|
|
32
|
-
"@assistant-ui/tap": "^0.9.
|
|
30
|
+
"@assistant-ui/core": "^0.3.18",
|
|
31
|
+
"@assistant-ui/store": "^0.3.13",
|
|
32
|
+
"@assistant-ui/tap": "^0.9.17",
|
|
33
33
|
"@modelcontextprotocol/client": "^2.0.0",
|
|
34
34
|
"@modelcontextprotocol/core": "^2.0.0",
|
|
35
35
|
"@radix-ui/react-primitive": "^2.1.10",
|
|
36
|
-
"assistant-stream": "^0.3.
|
|
36
|
+
"assistant-stream": "^0.3.42"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@types/react": "*",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
48
|
+
"@assistant-ui/x-buildutils": "0.0.26",
|
|
49
49
|
"@testing-library/dom": "^10.4.1",
|
|
50
50
|
"@testing-library/react": "^16.3.3",
|
|
51
51
|
"@types/react": "^19.2.18",
|