@assistant-ui/react-mcp 0.1.21 → 0.1.22
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.map +1 -1
- package/dist/auth/createOAuthProvider.js +7 -1
- package/dist/auth/createOAuthProvider.js.map +1 -1
- package/dist/primitives/addForm/McpAddFormRoot.d.ts.map +1 -1
- package/dist/primitives/addForm/McpAddFormRoot.js +14 -5
- package/dist/primitives/addForm/McpAddFormRoot.js.map +1 -1
- package/dist/resources/McpCustomServerPersistence.d.ts +17 -0
- package/dist/resources/McpCustomServerPersistence.d.ts.map +1 -0
- package/dist/resources/McpCustomServerPersistence.js +192 -0
- package/dist/resources/McpCustomServerPersistence.js.map +1 -0
- package/dist/resources/McpElicitationLifecycle.d.ts +12 -0
- package/dist/resources/McpElicitationLifecycle.d.ts.map +1 -0
- package/dist/resources/McpElicitationLifecycle.js +144 -0
- package/dist/resources/McpElicitationLifecycle.js.map +1 -0
- package/dist/resources/McpManagerResource.d.ts.map +1 -1
- package/dist/resources/McpManagerResource.js +28 -205
- package/dist/resources/McpManagerResource.js.map +1 -1
- package/dist/resources/McpServerResource.d.ts.map +1 -1
- package/dist/resources/McpServerResource.js +24 -101
- package/dist/resources/McpServerResource.js.map +1 -1
- package/dist/resources/storage/McpLocalStorage.d.ts +1 -4
- package/dist/resources/storage/McpLocalStorage.d.ts.map +1 -1
- package/dist/resources/storage/McpLocalStorage.js +1 -100
- package/dist/resources/storage/McpLocalStorage.js.map +1 -1
- package/dist/resources/storage/McpStoredDataNormalization.d.ts +5 -0
- package/dist/resources/storage/McpStoredDataNormalization.d.ts.map +1 -0
- package/dist/resources/storage/McpStoredDataNormalization.js +95 -0
- package/dist/resources/storage/McpStoredDataNormalization.js.map +1 -0
- package/dist/utils/serverUrl.d.ts +1 -0
- package/dist/utils/serverUrl.d.ts.map +1 -1
- package/dist/utils/serverUrl.js +15 -1
- package/dist/utils/serverUrl.js.map +1 -1
- package/package.json +10 -9
- package/src/auth/createOAuthProvider.test.ts +30 -1
- package/src/auth/createOAuthProvider.ts +12 -4
- package/src/primitives/addForm/McpAddFormRoot.test.tsx +66 -0
- package/src/primitives/addForm/McpAddFormRoot.tsx +11 -1
- package/src/resources/McpCustomServerPersistence.test.ts +56 -0
- package/src/resources/McpCustomServerPersistence.ts +233 -0
- package/src/resources/McpElicitationLifecycle.test.ts +83 -0
- package/src/resources/McpElicitationLifecycle.ts +156 -0
- package/src/resources/McpManagerResource.test.ts +36 -0
- package/src/resources/McpManagerResource.ts +25 -248
- package/src/resources/McpServerResource.test.ts +123 -10
- package/src/resources/McpServerResource.ts +29 -149
- package/src/resources/storage/McpLocalStorage.ts +9 -209
- package/src/resources/storage/McpStoredDataNormalization.test.ts +24 -0
- package/src/resources/storage/McpStoredDataNormalization.ts +201 -0
- package/src/utils/serverUrl.test.ts +30 -0
- package/src/utils/serverUrl.ts +26 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { buildHeaders } from "../auth/buildHeaders.js";
|
|
2
|
-
import { hasPersistedCredentials, isAuthStateForServerUrl } from "../utils/serverUrl.js";
|
|
2
|
+
import { hasPersistedCredentials, isAuthStateForServerUrl, isSecureNetworkUrl } from "../utils/serverUrl.js";
|
|
3
3
|
import { clearOAuthProviderAuthState, createOAuthProvider, hasUsableOAuthTokens } from "../auth/createOAuthProvider.js";
|
|
4
4
|
import { assertValidServerId } from "../utils/serverId.js";
|
|
5
|
-
import { createMcpId } from "../utils/createMcpId.js";
|
|
6
|
-
import { validateElicitationContent } from "./validateElicitationContent.js";
|
|
7
5
|
import { beginMcpServerRemovalFence } from "./McpServerRemovalFence.js";
|
|
6
|
+
import { useMcpElicitationLifecycle } from "./McpElicitationLifecycle.js";
|
|
8
7
|
import { useEffect, useEffectEvent, useMemo, useRef, useState } from "@assistant-ui/tap/react-shim";
|
|
9
8
|
import { resource, useResource, withKey } from "@assistant-ui/tap";
|
|
10
9
|
import { shallowEqual } from "@assistant-ui/store/internal";
|
|
@@ -42,14 +41,13 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
42
41
|
const [tools, setTools] = useState([]);
|
|
43
42
|
const [lastError, setLastError] = useState(null);
|
|
44
43
|
const [authorizationUrl, setAuthorizationUrl] = useState(null);
|
|
45
|
-
const
|
|
44
|
+
const { pendingElicitations, cancelPendingElicitations, requestElicitation, answerElicitation } = useMcpElicitationLifecycle();
|
|
46
45
|
const clientRef = useRef(null);
|
|
47
46
|
const transportRef = useRef(null);
|
|
48
47
|
const pendingTransportRef = useRef(null);
|
|
49
48
|
const transportGenerationRef = useRef(/* @__PURE__ */ new WeakMap());
|
|
50
49
|
const connectionGenerationRef = useRef(0);
|
|
51
50
|
const pendingAuthValidationRef = useRef(null);
|
|
52
|
-
const elicitationResolversRef = useRef(/* @__PURE__ */ new Map());
|
|
53
51
|
const pendingDisposalRef = useRef(null);
|
|
54
52
|
const mountedRef = useRef(true);
|
|
55
53
|
const closeTransportSafely = async (transport) => {
|
|
@@ -69,26 +67,6 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
69
67
|
pendingTransportRef.current = null;
|
|
70
68
|
await closeQueuedTransports(transport_0 ? [transport_0] : []);
|
|
71
69
|
};
|
|
72
|
-
const resolvePendingElicitation = (id, result) => {
|
|
73
|
-
const entry = elicitationResolversRef.current.get(id);
|
|
74
|
-
if (!entry) return false;
|
|
75
|
-
elicitationResolversRef.current.delete(id);
|
|
76
|
-
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
77
|
-
setPendingElicitations((current) => current.filter((elicitation) => elicitation.id !== id));
|
|
78
|
-
entry.resolve(result);
|
|
79
|
-
return true;
|
|
80
|
-
};
|
|
81
|
-
const setPendingElicitationError = (id_0, error) => {
|
|
82
|
-
if (!elicitationResolversRef.current.has(id_0)) return false;
|
|
83
|
-
setPendingElicitations((current_0) => current_0.map((elicitation_0) => elicitation_0.id === id_0 ? {
|
|
84
|
-
...elicitation_0,
|
|
85
|
-
error
|
|
86
|
-
} : elicitation_0));
|
|
87
|
-
return true;
|
|
88
|
-
};
|
|
89
|
-
const cancelPendingElicitations = () => {
|
|
90
|
-
for (const [id_1] of elicitationResolversRef.current) resolvePendingElicitation(id_1, { action: "cancel" });
|
|
91
|
-
};
|
|
92
70
|
const detachTransports = () => {
|
|
93
71
|
cancelPendingElicitations();
|
|
94
72
|
const pendingTransport = pendingTransportRef.current;
|
|
@@ -131,6 +109,7 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
131
109
|
};
|
|
132
110
|
});
|
|
133
111
|
const buildTransport = useEffectEvent(async (generation_0) => {
|
|
112
|
+
if (props.auth.type !== "none" && !isSecureNetworkUrl(props.url)) throw new Error(`Authenticated MCP server "${props.id}" must use HTTPS or loopback HTTP.`);
|
|
134
113
|
if (props.auth.type === "oauth") {
|
|
135
114
|
const generationOwner = { current: generation_0 };
|
|
136
115
|
const authProvider = createOAuthProvider({
|
|
@@ -140,7 +119,10 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
140
119
|
storage: props.storage,
|
|
141
120
|
redirectUri: props.redirectUri,
|
|
142
121
|
onAuthorizationUrl: (url) => {
|
|
143
|
-
if (isCurrentConnection(generationOwner.current))
|
|
122
|
+
if (!isCurrentConnection(generationOwner.current)) return;
|
|
123
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error(`Unsupported MCP OAuth authorization URL protocol: ${url.protocol}`);
|
|
124
|
+
if (!isSecureNetworkUrl(url.toString())) throw new Error("MCP OAuth authorization URL must use HTTPS or loopback HTTP.");
|
|
125
|
+
setAuthorizationUrl(url.toString());
|
|
144
126
|
}
|
|
145
127
|
});
|
|
146
128
|
const transport_2 = new StreamableHTTPClientTransport(new URL(props.url), { authProvider });
|
|
@@ -180,10 +162,10 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
180
162
|
listChanged: { tools: {
|
|
181
163
|
autoRefresh: true,
|
|
182
164
|
debounceMs: 300,
|
|
183
|
-
onChanged: (
|
|
165
|
+
onChanged: (error, items) => {
|
|
184
166
|
if (!isCurrentConnection(generation_2)) return;
|
|
185
|
-
if (
|
|
186
|
-
setLastError({ message:
|
|
167
|
+
if (error !== null) {
|
|
168
|
+
setLastError({ message: error.message });
|
|
187
169
|
return;
|
|
188
170
|
}
|
|
189
171
|
if (items === null) return;
|
|
@@ -197,34 +179,7 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
197
179
|
name: "assistant-ui-mcp",
|
|
198
180
|
version: "0.0.0"
|
|
199
181
|
}, clientOptions);
|
|
200
|
-
if (props.elicitation !== false) client_0.setRequestHandler("elicitation/create", (request, context) =>
|
|
201
|
-
if (!isCurrentConnection(generation_2)) return Promise.resolve({ action: "cancel" });
|
|
202
|
-
if (!("requestedSchema" in request.params)) return Promise.resolve({ action: "cancel" });
|
|
203
|
-
const { message, requestedSchema } = request.params;
|
|
204
|
-
const id_2 = createMcpId();
|
|
205
|
-
const promise_0 = new Promise((resolve) => {
|
|
206
|
-
const onAbort = () => {
|
|
207
|
-
resolvePendingElicitation(id_2, { action: "cancel" });
|
|
208
|
-
};
|
|
209
|
-
elicitationResolversRef.current.set(id_2, {
|
|
210
|
-
resolve,
|
|
211
|
-
signal: context.mcpReq.signal,
|
|
212
|
-
onAbort,
|
|
213
|
-
requestedSchema
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
setPendingElicitations((current_1) => [...current_1, {
|
|
217
|
-
id: id_2,
|
|
218
|
-
message,
|
|
219
|
-
requestedSchema
|
|
220
|
-
}]);
|
|
221
|
-
const entry_0 = elicitationResolversRef.current.get(id_2);
|
|
222
|
-
if (entry_0) {
|
|
223
|
-
if (context.mcpReq.signal.aborted) entry_0.onAbort();
|
|
224
|
-
else context.mcpReq.signal.addEventListener("abort", entry_0.onAbort, { once: true });
|
|
225
|
-
}
|
|
226
|
-
return promise_0;
|
|
227
|
-
});
|
|
182
|
+
if (props.elicitation !== false) client_0.setRequestHandler("elicitation/create", (request, context) => requestElicitation(request, context.mcpReq.signal, () => isCurrentConnection(generation_2)));
|
|
228
183
|
const startedAt_0 = Date.now();
|
|
229
184
|
await withConnectionTimeout(client_0.connect(transport_3), "connecting", startedAt_0);
|
|
230
185
|
if (!isCurrentConnection(generation_2)) return false;
|
|
@@ -283,13 +238,13 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
283
238
|
if (!state_1) throw new Error("missing \"state\" parameter");
|
|
284
239
|
let pendingAuthValidation = pendingAuthValidationRef.current;
|
|
285
240
|
if (!pendingAuthValidation) {
|
|
286
|
-
let
|
|
241
|
+
let resolve;
|
|
287
242
|
pendingAuthValidation = {
|
|
288
243
|
count: 0,
|
|
289
244
|
promise: new Promise((resolvePromise) => {
|
|
290
|
-
|
|
245
|
+
resolve = resolvePromise;
|
|
291
246
|
}),
|
|
292
|
-
resolve
|
|
247
|
+
resolve
|
|
293
248
|
};
|
|
294
249
|
pendingAuthValidationRef.current = pendingAuthValidation;
|
|
295
250
|
}
|
|
@@ -333,12 +288,12 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
333
288
|
setAuthorizationUrl(null);
|
|
334
289
|
if (!await finalizeConnect(transport_5, generation_4)) throw createInterruptedAuthError();
|
|
335
290
|
} catch (err_0) {
|
|
336
|
-
const
|
|
337
|
-
if (!isCurrentConnection(generation_4)) throw createInterruptedAuthError(
|
|
291
|
+
const error_0 = err_0 instanceof Error ? err_0 : new Error(String(err_0));
|
|
292
|
+
if (!isCurrentConnection(generation_4)) throw createInterruptedAuthError(error_0);
|
|
338
293
|
await closeTransports();
|
|
339
|
-
setLastError({ message:
|
|
294
|
+
setLastError({ message: error_0.message });
|
|
340
295
|
setConnectionState("error");
|
|
341
|
-
throw
|
|
296
|
+
throw error_0;
|
|
342
297
|
}
|
|
343
298
|
});
|
|
344
299
|
const tryAutoConnect = useEffectEvent(async (signal) => {
|
|
@@ -355,10 +310,10 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
355
310
|
let loaded;
|
|
356
311
|
try {
|
|
357
312
|
loaded = await loadAuthState();
|
|
358
|
-
} catch (
|
|
313
|
+
} catch (error_1) {
|
|
359
314
|
if (signal.cancelled || !isCurrentConnection(generation_5)) return;
|
|
360
|
-
const
|
|
361
|
-
setLastError({ message: `MCP server "${props.id}" failed to load saved authentication: ${
|
|
315
|
+
const message = error_1 instanceof Error ? error_1.message : String(error_1);
|
|
316
|
+
setLastError({ message: `MCP server "${props.id}" failed to load saved authentication: ${message}` });
|
|
362
317
|
setConnectionState("error");
|
|
363
318
|
return;
|
|
364
319
|
}
|
|
@@ -391,7 +346,7 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
391
346
|
mountedRef.current = false;
|
|
392
347
|
signal_0.cancelled = true;
|
|
393
348
|
const task_0 = transportCloseQueueRef.current.then(async () => {
|
|
394
|
-
await new Promise((
|
|
349
|
+
await new Promise((resolve_0) => queueMicrotask(resolve_0));
|
|
395
350
|
if (pendingDisposal.cancelled) return;
|
|
396
351
|
connectionGenerationRef.current += 1;
|
|
397
352
|
await Promise.all(detachTransports().map(closeTransportSafely));
|
|
@@ -458,39 +413,7 @@ const useMcpServerResourceInstance = (props) => {
|
|
|
458
413
|
return await client_3.readResource({ uri });
|
|
459
414
|
},
|
|
460
415
|
completeAuth: doCompleteAuth,
|
|
461
|
-
answerElicitation
|
|
462
|
-
if (response.action === "accept") {
|
|
463
|
-
const entry_1 = elicitationResolversRef.current.get(id_3);
|
|
464
|
-
if (!entry_1) return void 0;
|
|
465
|
-
if (typeof response.content !== "object" || response.content === null || Array.isArray(response.content)) {
|
|
466
|
-
const errors = [{
|
|
467
|
-
property: "content",
|
|
468
|
-
message: "Response content must be an object."
|
|
469
|
-
}];
|
|
470
|
-
setPendingElicitationError(id_3, {
|
|
471
|
-
message: "Invalid elicitation content: content.",
|
|
472
|
-
properties: ["content"]
|
|
473
|
-
});
|
|
474
|
-
return errors;
|
|
475
|
-
}
|
|
476
|
-
const errors_0 = validateElicitationContent(entry_1.requestedSchema, response.content);
|
|
477
|
-
if (errors_0.length > 0) {
|
|
478
|
-
const properties = [...new Set(errors_0.map((error_3) => error_3.property))];
|
|
479
|
-
setPendingElicitationError(id_3, {
|
|
480
|
-
message: `Invalid elicitation content: ${properties.join(", ")}.`,
|
|
481
|
-
properties
|
|
482
|
-
});
|
|
483
|
-
return errors_0;
|
|
484
|
-
}
|
|
485
|
-
const result_0 = {
|
|
486
|
-
action: "accept",
|
|
487
|
-
content: response.content
|
|
488
|
-
};
|
|
489
|
-
resolvePendingElicitation(id_3, result_0);
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
resolvePendingElicitation(id_3, { action: response.action });
|
|
493
|
-
}
|
|
416
|
+
answerElicitation
|
|
494
417
|
};
|
|
495
418
|
};
|
|
496
419
|
const McpServerResourceInstance = resource(useMcpServerResourceInstance);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpServerResource.js","names":["useState","useRef","useEffect","useMemo","useEffectEvent","resource","useResource","withKey","ClientOutput","shallowEqual","Client","StreamableHTTPClientTransport","UnauthorizedError","ClientOptions","ElicitRequest","ElicitResult","StreamableHTTPClientTransportOptions","clearOAuthProviderAuthState","createOAuthProvider","hasUsableOAuthTokens","buildHeaders","assertValidServerId","hasPersistedCredentials","isAuthStateForServerUrl","validateElicitationContent","MCPStorage","MCPAuthConfig","MCPConnectionState","MCPElicitation","MCPElicitationResponse","MCPServerKind","MCPServerState","MCPToolInfo","createMcpId","beginMcpServerRemovalFence","McpServerResourceProps","id","kind","name","url","icon","auth","storage","redirectUri","autoConnect","connectionTimeout","cache","defaultTtlMs","elicitation","onRemove","Promise","McpServerResourceInstanceProps","transportCloseQueueRef","current","getConnectionDependencies","props","authDependencies","type","token","scopeId","scopes","length","authorizationEndpoint","tokenEndpoint","registrationEndpoint","clientId","clientSecret","useMcpServerResourceInstance","connectionState","setConnectionState","tools","setTools","lastError","setLastError","message","authorizationUrl","setAuthorizationUrl","pendingElicitations","setPendingElicitations","clientRef","transportRef","pendingTransportRef","transportGenerationRef","WeakMap","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","generationOwner","authProvider","serverId","serverUrl","config","onAuthorizationUrl","toString","URL","set","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","mcpReq","aborted","addEventListener","once","connect","synced","doConnect","err","String","doDisconnect","doCompleteAuth","callbackUrl","validationGeneration","searchParams","pendingAuthValidation","resolvePromise","persisted","finishAuth","connected","tryAutoConnect","loaded","Awaited","previousDisposal","pendingDisposal","queueMicrotask","getState","disconnect","remove","releaseRemovalFence","callTool","args","arguments","Record","listResources","readResource","uri","completeAuth","answerElicitation","response","property","content","isArray","errors","properties","join","McpServerResourceInstance","McpServerResource","useMcpServerResource","dependencies","connection","setConnection","currentConnection"],"sources":["../../src/resources/McpServerResource.ts"],"sourcesContent":["import { useState, useRef, useEffect, useMemo, useEffectEvent } from \"react\";\nimport { resource, useResource, withKey } from \"@assistant-ui/tap\";\nimport type { ClientOutput } from \"@assistant-ui/store\";\nimport { shallowEqual } from \"@assistant-ui/store/internal\";\nimport {\n Client,\n StreamableHTTPClientTransport,\n UnauthorizedError,\n type ClientOptions,\n type ElicitRequest,\n type ElicitResult,\n type StreamableHTTPClientTransportOptions,\n} from \"@modelcontextprotocol/client\";\nimport {\n clearOAuthProviderAuthState,\n createOAuthProvider,\n hasUsableOAuthTokens,\n} from \"../auth/createOAuthProvider\";\nimport { buildHeaders } from \"../auth/buildHeaders\";\nimport { assertValidServerId } from \"../utils/serverId\";\nimport {\n hasPersistedCredentials,\n isAuthStateForServerUrl,\n} from \"../utils/serverUrl\";\nimport { validateElicitationContent } from \"./validateElicitationContent\";\nimport type { MCPStorage } from \"./storage/types\";\nimport type {\n MCPAuthConfig,\n MCPConnectionState,\n MCPElicitation,\n MCPElicitationResponse,\n MCPServerKind,\n MCPServerState,\n MCPToolInfo,\n} from \"../mcp-scope\";\nimport { createMcpId } from \"../utils/createMcpId\";\nimport { beginMcpServerRemovalFence } from \"./McpServerRemovalFence\";\n\nexport type McpServerResourceProps = {\n id: string;\n kind: MCPServerKind;\n name: string;\n url: string;\n icon?: string | undefined;\n auth: MCPAuthConfig;\n storage: MCPStorage;\n redirectUri: string;\n autoConnect: boolean;\n connectionTimeout?: number | undefined;\n cache?: { readonly defaultTtlMs?: number } | undefined;\n readonly elicitation?: boolean;\n onRemove: () => Promise<void>;\n};\n\ntype McpServerResourceInstanceProps = McpServerResourceProps & {\n transportCloseQueueRef: { current: Promise<void> };\n};\n\nexport const getConnectionDependencies = (\n props: McpServerResourceProps,\n): readonly unknown[] => {\n const auth = props.auth;\n const authDependencies =\n auth.type === \"bearer\"\n ? [auth.type, auth.token, props.storage.scopeId]\n : auth.type === \"oauth\"\n ? [\n auth.type,\n auth.scopes?.length,\n ...(auth.scopes ?? []),\n auth.authorizationEndpoint,\n auth.tokenEndpoint,\n auth.registrationEndpoint,\n auth.clientId,\n auth.clientSecret,\n props.storage.scopeId,\n ]\n : [auth.type];\n\n return [\n props.id,\n props.url,\n ...authDependencies,\n props.redirectUri,\n props.cache?.defaultTtlMs,\n props.elicitation !== false,\n ];\n};\n\nconst useMcpServerResourceInstance = (\n props: McpServerResourceInstanceProps,\n): ClientOutput<\"mcpServer\"> => {\n assertValidServerId(props.id);\n const [connectionState, setConnectionState] =\n useState<MCPConnectionState>(\"disconnected\");\n const [tools, setTools] = useState<MCPToolInfo[]>([]);\n const [lastError, setLastError] = useState<{ message: string } | null>(null);\n const [authorizationUrl, setAuthorizationUrl] = useState<string | null>(null);\n const [pendingElicitations, setPendingElicitations] = useState<\n MCPElicitation[]\n >([]);\n\n const clientRef = useRef<Client | null>(null);\n const transportRef = useRef<StreamableHTTPClientTransport | null>(null);\n const pendingTransportRef = useRef<StreamableHTTPClientTransport | null>(\n null,\n );\n const transportGenerationRef = useRef(\n new WeakMap<StreamableHTTPClientTransport, { current: number }>(),\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 (generation: number): Promise<StreamableHTTPClientTransport> => {\n if (props.auth.type === \"oauth\") {\n const generationOwner = { current: generation };\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) => {\n if (isCurrentConnection(generationOwner.current)) {\n setAuthorizationUrl(url.toString());\n }\n },\n });\n const transport = new StreamableHTTPClientTransport(\n new URL(props.url),\n {\n authProvider,\n },\n );\n transportGenerationRef.current.set(transport, generationOwner);\n return transport;\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.mcpReq.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.mcpReq.signal.aborted) {\n entry.onAbort();\n } else {\n context.mcpReq.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(generation);\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(generation);\n if (!isCurrentConnection(generation)) {\n await closeQueuedTransports([transport]);\n throw createInterruptedAuthError();\n }\n }\n const generationOwner = transportGenerationRef.current.get(transport);\n if (generationOwner) generationOwner.current = generation;\n transportRef.current = null;\n clientRef.current = null;\n pendingTransportRef.current = transport;\n await transport.finishAuth(url.searchParams);\n if (!isCurrentConnection(generation)) throw createInterruptedAuthError();\n setAuthorizationUrl(null);\n const connected = await finalizeConnect(transport, generation);\n if (!connected) throw createInterruptedAuthError();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n if (!isCurrentConnection(generation))\n throw createInterruptedAuthError(error);\n\n await closeTransports();\n setLastError({\n message: error.message,\n });\n setConnectionState(\"error\");\n throw error;\n }\n });\n\n const tryAutoConnect = useEffectEvent(\n async (signal: { cancelled: boolean }) => {\n if (!props.autoConnect) return;\n if (props.auth.type === \"none\") {\n void doConnect();\n return;\n }\n // Static connector token short-circuits the storage read.\n if (props.auth.type === \"bearer\" && props.auth.token) {\n void doConnect();\n return;\n }\n const generation = connectionGenerationRef.current;\n let loaded: Awaited<ReturnType<typeof loadAuthState>>;\n try {\n loaded = await loadAuthState();\n } catch (error) {\n if (signal.cancelled || !isCurrentConnection(generation)) return;\n const message = error instanceof Error ? error.message : String(error);\n setLastError({\n message: `MCP server \"${props.id}\" failed to load saved authentication: ${message}`,\n });\n setConnectionState(\"error\");\n return;\n }\n if (signal.cancelled || !isCurrentConnection(generation)) return;\n if (loaded.unbound) {\n setLastError({ message: unboundAuthMessage() });\n return;\n }\n const persisted = loaded.state;\n if (props.auth.type === \"oauth\") {\n if (!hasUsableOAuthTokens(persisted, props.auth)) return;\n } else if (!persisted?.token) {\n return;\n }\n const pendingAuthValidation = pendingAuthValidationRef.current;\n if (pendingAuthValidation) {\n await pendingAuthValidation.promise;\n if (signal.cancelled || !isCurrentConnection(generation)) return;\n }\n void doConnect();\n },\n );\n\n // Auto-connect on mount when usable auth exists.\n useEffect(() => {\n const transportCloseQueueRef = props.transportCloseQueueRef;\n const previousDisposal = pendingDisposalRef.current;\n if (previousDisposal) previousDisposal.cancelled = true;\n const pendingDisposal = { cancelled: false };\n pendingDisposalRef.current = pendingDisposal;\n mountedRef.current = true;\n const signal = { cancelled: false };\n // Auto-connect opens a transport, so it belongs to the same effect as the\n // disposal that closes it.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n void tryAutoConnect(signal);\n return () => {\n mountedRef.current = false;\n signal.cancelled = true;\n const task = transportCloseQueueRef.current.then(async () => {\n await new Promise<void>((resolve) => queueMicrotask(resolve));\n if (pendingDisposal.cancelled) return;\n connectionGenerationRef.current += 1;\n await Promise.all(detachTransports().map(closeTransportSafely));\n });\n transportCloseQueueRef.current = task;\n };\n }, [props.transportCloseQueueRef]);\n\n const state = useMemo<MCPServerState>(\n () => ({\n id: props.id,\n kind: props.kind,\n name: props.name,\n url: props.url,\n icon: props.icon,\n connectionState,\n lastError,\n tools,\n authorizationUrl,\n pendingElicitations,\n }),\n [\n props.id,\n props.kind,\n props.name,\n props.url,\n props.icon,\n connectionState,\n lastError,\n tools,\n authorizationUrl,\n pendingElicitations,\n ],\n );\n\n return {\n getState: () => state,\n connect: doConnect,\n disconnect: doDisconnect,\n remove: async () => {\n const releaseRemovalFence = beginMcpServerRemovalFence(props);\n try {\n await doDisconnect();\n await clearOAuthProviderAuthState(props.storage, props.id);\n await props.onRemove();\n } catch (err) {\n releaseRemovalFence?.();\n setLastError({\n message: err instanceof Error ? err.message : String(err),\n });\n throw err;\n }\n releaseRemovalFence?.();\n },\n callTool: async (name, args) => {\n const client = clientRef.current;\n if (!client) {\n throw new Error(`MCP server \"${props.id}\" is not connected`);\n }\n return await client.callTool({\n name,\n arguments: args as Record<string, unknown> | undefined,\n });\n },\n listResources: async (params) => {\n const client = clientRef.current;\n if (!client) {\n throw new Error(`MCP server \"${props.id}\" is not connected`);\n }\n return await client.listResources(params);\n },\n readResource: async (uri) => {\n const client = clientRef.current;\n if (!client) {\n throw new Error(`MCP server \"${props.id}\" is not connected`);\n }\n return await client.readResource({ uri });\n },\n completeAuth: doCompleteAuth,\n answerElicitation: (\n id: string,\n response: MCPElicitationResponse,\n ): readonly { property: string; message: string }[] | undefined => {\n if (response.action === \"accept\") {\n const entry = elicitationResolversRef.current.get(id);\n if (!entry) return undefined;\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 undefined;\n }\n\n resolvePendingElicitation(id, { action: response.action });\n return undefined;\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
|
+
{"version":3,"file":"McpServerResource.js","names":["useState","useRef","useEffect","useMemo","useEffectEvent","resource","useResource","withKey","ClientOutput","shallowEqual","Client","StreamableHTTPClientTransport","UnauthorizedError","ClientOptions","StreamableHTTPClientTransportOptions","clearOAuthProviderAuthState","createOAuthProvider","hasUsableOAuthTokens","buildHeaders","assertValidServerId","hasPersistedCredentials","isAuthStateForServerUrl","isSecureNetworkUrl","MCPStorage","MCPAuthConfig","MCPConnectionState","MCPServerKind","MCPServerState","MCPToolInfo","beginMcpServerRemovalFence","useMcpElicitationLifecycle","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","cancelPendingElicitations","requestElicitation","answerElicitation","clientRef","transportRef","pendingTransportRef","transportGenerationRef","WeakMap","connectionGenerationRef","pendingAuthValidationRef","count","promise","resolve","pendingDisposalRef","cancelled","mountedRef","closeTransportSafely","transport","close","closeQueuedTransports","transports","task","then","all","map","closePendingTransport","detachTransports","pendingTransport","activeTransport","Set","filter","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","generationOwner","authProvider","serverId","serverUrl","config","onAuthorizationUrl","protocol","toString","URL","set","headers","transportOpts","requestInit","applyToolsList","list","Array","description","inputSchema","t","info","syncTools","client","options","listPromise","listTools","finalizeConnect","clientOptions","capabilities","listChanged","autoRefresh","debounceMs","onChanged","error","items","defaultCacheTtlMs","version","setRequestHandler","request","context","mcpReq","signal","connect","synced","doConnect","err","String","doDisconnect","doCompleteAuth","callbackUrl","validationGeneration","searchParams","get","pendingAuthValidation","resolvePromise","persisted","finishAuth","connected","tryAutoConnect","loaded","Awaited","previousDisposal","pendingDisposal","queueMicrotask","getState","disconnect","remove","releaseRemovalFence","callTool","args","arguments","Record","listResources","params","readResource","uri","completeAuth","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 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 isSecureNetworkUrl,\n} from \"../utils/serverUrl\";\nimport type { MCPStorage } from \"./storage/types\";\nimport type {\n MCPAuthConfig,\n MCPConnectionState,\n MCPServerKind,\n MCPServerState,\n MCPToolInfo,\n} from \"../mcp-scope\";\nimport { beginMcpServerRemovalFence } from \"./McpServerRemovalFence\";\nimport { useMcpElicitationLifecycle } from \"./McpElicitationLifecycle\";\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 {\n pendingElicitations,\n cancelPendingElicitations,\n requestElicitation,\n answerElicitation,\n } = useMcpElicitationLifecycle();\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 transportGenerationRef = useRef(\n new WeakMap<StreamableHTTPClientTransport, { current: number }>(),\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 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 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 (generation: number): Promise<StreamableHTTPClientTransport> => {\n if (props.auth.type !== \"none\" && !isSecureNetworkUrl(props.url)) {\n throw new Error(\n `Authenticated MCP server \"${props.id}\" must use HTTPS or loopback HTTP.`,\n );\n }\n if (props.auth.type === \"oauth\") {\n const generationOwner = { current: generation };\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) => {\n if (!isCurrentConnection(generationOwner.current)) return;\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(\n `Unsupported MCP OAuth authorization URL protocol: ${url.protocol}`,\n );\n }\n if (!isSecureNetworkUrl(url.toString())) {\n throw new Error(\n \"MCP OAuth authorization URL must use HTTPS or loopback HTTP.\",\n );\n }\n setAuthorizationUrl(url.toString());\n },\n });\n const transport = new StreamableHTTPClientTransport(\n new URL(props.url),\n {\n authProvider,\n },\n );\n transportGenerationRef.current.set(transport, generationOwner);\n return transport;\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(\"elicitation/create\", (request, context) =>\n requestElicitation(request, context.mcpReq.signal, () =>\n isCurrentConnection(generation),\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(generation);\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(generation);\n if (!isCurrentConnection(generation)) {\n await closeQueuedTransports([transport]);\n throw createInterruptedAuthError();\n }\n }\n const generationOwner = transportGenerationRef.current.get(transport);\n if (generationOwner) generationOwner.current = generation;\n transportRef.current = null;\n clientRef.current = null;\n pendingTransportRef.current = transport;\n await transport.finishAuth(url.searchParams);\n if (!isCurrentConnection(generation)) throw createInterruptedAuthError();\n setAuthorizationUrl(null);\n const connected = await finalizeConnect(transport, generation);\n if (!connected) throw createInterruptedAuthError();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n if (!isCurrentConnection(generation))\n throw createInterruptedAuthError(error);\n\n await closeTransports();\n setLastError({\n message: error.message,\n });\n setConnectionState(\"error\");\n throw error;\n }\n });\n\n const tryAutoConnect = useEffectEvent(\n async (signal: { cancelled: boolean }) => {\n if (!props.autoConnect) return;\n if (props.auth.type === \"none\") {\n void doConnect();\n return;\n }\n // Static connector token short-circuits the storage read.\n if (props.auth.type === \"bearer\" && props.auth.token) {\n void doConnect();\n return;\n }\n const generation = connectionGenerationRef.current;\n let loaded: Awaited<ReturnType<typeof loadAuthState>>;\n try {\n loaded = await loadAuthState();\n } catch (error) {\n if (signal.cancelled || !isCurrentConnection(generation)) return;\n const message = error instanceof Error ? error.message : String(error);\n setLastError({\n message: `MCP server \"${props.id}\" failed to load saved authentication: ${message}`,\n });\n setConnectionState(\"error\");\n return;\n }\n if (signal.cancelled || !isCurrentConnection(generation)) return;\n if (loaded.unbound) {\n setLastError({ message: unboundAuthMessage() });\n return;\n }\n const persisted = loaded.state;\n if (props.auth.type === \"oauth\") {\n if (!hasUsableOAuthTokens(persisted, props.auth)) return;\n } else if (!persisted?.token) {\n return;\n }\n const pendingAuthValidation = pendingAuthValidationRef.current;\n if (pendingAuthValidation) {\n await pendingAuthValidation.promise;\n if (signal.cancelled || !isCurrentConnection(generation)) return;\n }\n void doConnect();\n },\n );\n\n // Auto-connect on mount when usable auth exists.\n useEffect(() => {\n const transportCloseQueueRef = props.transportCloseQueueRef;\n const previousDisposal = pendingDisposalRef.current;\n if (previousDisposal) previousDisposal.cancelled = true;\n const pendingDisposal = { cancelled: false };\n pendingDisposalRef.current = pendingDisposal;\n mountedRef.current = true;\n const signal = { cancelled: false };\n // Auto-connect opens a transport, so it belongs to the same effect as the\n // disposal that closes it.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n void tryAutoConnect(signal);\n return () => {\n mountedRef.current = false;\n signal.cancelled = true;\n const task = transportCloseQueueRef.current.then(async () => {\n await new Promise<void>((resolve) => queueMicrotask(resolve));\n if (pendingDisposal.cancelled) return;\n connectionGenerationRef.current += 1;\n await Promise.all(detachTransports().map(closeTransportSafely));\n });\n transportCloseQueueRef.current = task;\n };\n }, [props.transportCloseQueueRef]);\n\n const state = useMemo<MCPServerState>(\n () => ({\n id: props.id,\n kind: props.kind,\n name: props.name,\n url: props.url,\n icon: props.icon,\n connectionState,\n lastError,\n tools,\n authorizationUrl,\n pendingElicitations,\n }),\n [\n props.id,\n props.kind,\n props.name,\n props.url,\n props.icon,\n connectionState,\n lastError,\n tools,\n authorizationUrl,\n pendingElicitations,\n ],\n );\n\n return {\n getState: () => state,\n connect: doConnect,\n disconnect: doDisconnect,\n remove: async () => {\n const releaseRemovalFence = beginMcpServerRemovalFence(props);\n try {\n await doDisconnect();\n await clearOAuthProviderAuthState(props.storage, props.id);\n await props.onRemove();\n } catch (err) {\n releaseRemovalFence?.();\n setLastError({\n message: err instanceof Error ? err.message : String(err),\n });\n throw err;\n }\n releaseRemovalFence?.();\n },\n callTool: async (name, args) => {\n const client = clientRef.current;\n if (!client) {\n throw new Error(`MCP server \"${props.id}\" is not connected`);\n }\n return await client.callTool({\n name,\n arguments: args as Record<string, unknown> | undefined,\n });\n },\n listResources: async (params) => {\n const client = clientRef.current;\n if (!client) {\n throw new Error(`MCP server \"${props.id}\" is not connected`);\n }\n return await client.listResources(params);\n },\n readResource: async (uri) => {\n const client = clientRef.current;\n if (!client) {\n throw new Error(`MCP server \"${props.id}\" is not connected`);\n }\n return await client.readResource({ uri });\n },\n completeAuth: doCompleteAuth,\n answerElicitation,\n };\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,6 +1,5 @@
|
|
|
1
|
-
import type { MCPCustomServerRecord } from "../../mcp-scope.js";
|
|
2
|
-
import type { MCPPersistedAuthState } from "../../auth/types.js";
|
|
3
1
|
import type { MCPStorage } from "./types.js";
|
|
2
|
+
export { normalizeCustomServerRecords, normalizePersistedAuthState, } from "./McpStoredDataNormalization.js";
|
|
4
3
|
export type McpLocalStorageOptions = {
|
|
5
4
|
/** Namespace prefix for keys. Default "aui-mcp". */
|
|
6
5
|
keyPrefix?: string;
|
|
@@ -14,7 +13,5 @@ export type McpLocalStorageOptions = {
|
|
|
14
13
|
*/
|
|
15
14
|
scopeId?: string;
|
|
16
15
|
};
|
|
17
|
-
export declare const normalizeCustomServerRecords: (value: unknown) => MCPCustomServerRecord[];
|
|
18
|
-
export declare const normalizePersistedAuthState: (value: unknown) => MCPPersistedAuthState | null;
|
|
19
16
|
export declare const McpLocalStorage: import("@assistant-ui/tap").Resource<MCPStorage, [opts?: McpLocalStorageOptions | undefined]>;
|
|
20
17
|
//# sourceMappingURL=McpLocalStorage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpLocalStorage.d.ts","sourceRoot":"","sources":["../../../src/resources/storage/McpLocalStorage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"McpLocalStorage.d.ts","sourceRoot":"","sources":["../../../src/resources/storage/McpLocalStorage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAgB;AAM1C,OAAO,EACL,4BAA4B,EAC5B,2BAA2B,GAC5B,wCAAqC;AAEtC,MAAM,MAAM,sBAAsB,GAAG;IACnC,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AA+EF,eAAO,MAAM,eAAe,+FAA+B,CAAC"}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { assertValidServerId } from "../../utils/serverId.js";
|
|
1
|
+
import { normalizeCustomServerRecords, normalizePersistedAuthState } from "./McpStoredDataNormalization.js";
|
|
3
2
|
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
4
3
|
import "@assistant-ui/tap/react-shim";
|
|
5
4
|
import { resource } from "@assistant-ui/tap";
|
|
6
|
-
import { OAuthClientInformationFullSchema, OAuthMetadataSchema, OAuthProtectedResourceMetadataSchema, OAuthTokensSchema } from "@modelcontextprotocol/core";
|
|
7
5
|
//#region src/resources/storage/McpLocalStorage.ts
|
|
8
6
|
function resolveStorage(opts) {
|
|
9
7
|
if (opts.storage) return opts.storage;
|
|
@@ -14,103 +12,6 @@ function resolveStorage(opts) {
|
|
|
14
12
|
}
|
|
15
13
|
return null;
|
|
16
14
|
}
|
|
17
|
-
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18
|
-
const isOptionalString = (value) => value === void 0 || typeof value === "string";
|
|
19
|
-
const isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
|
|
20
|
-
const isOptionalNonEmptyString = (value) => value === void 0 || isNonEmptyString(value);
|
|
21
|
-
const isOptionalStringArray = (value) => value === void 0 || Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
22
|
-
const isOptionalConnectionTimeout = (value) => value === void 0 || typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
23
|
-
const isValidServerId = (id) => {
|
|
24
|
-
try {
|
|
25
|
-
assertValidServerId(id);
|
|
26
|
-
return true;
|
|
27
|
-
} catch {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
const isMCPAuthConfig = (auth) => {
|
|
32
|
-
if (!isRecord(auth)) return false;
|
|
33
|
-
switch (auth.type) {
|
|
34
|
-
case "none": return true;
|
|
35
|
-
case "bearer": return isOptionalNonEmptyString(auth.token);
|
|
36
|
-
case "oauth": return isOptionalStringArray(auth.scopes) && isOptionalString(auth.authorizationEndpoint) && isOptionalString(auth.tokenEndpoint) && isOptionalString(auth.registrationEndpoint) && isOptionalString(auth.clientId) && isOptionalString(auth.clientSecret);
|
|
37
|
-
default: return false;
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
const isCustomServerRecord = (value) => {
|
|
41
|
-
if (!isRecord(value)) return false;
|
|
42
|
-
if (typeof value.id !== "string" || !isValidServerId(value.id)) return false;
|
|
43
|
-
return isNonEmptyString(value.name) && isNonEmptyString(value.url) && Number.isFinite(value.createdAt) && isMCPAuthConfig(value.auth) && isOptionalConnectionTimeout(value.connectionTimeout);
|
|
44
|
-
};
|
|
45
|
-
const normalizeCustomServerRecord = (value) => {
|
|
46
|
-
if (isCustomServerRecord(value)) return value;
|
|
47
|
-
if (!isRecord(value)) return null;
|
|
48
|
-
const record = { ...value };
|
|
49
|
-
delete record.connectionTimeout;
|
|
50
|
-
return isCustomServerRecord(record) ? record : null;
|
|
51
|
-
};
|
|
52
|
-
const normalizeCustomServerRecords = (value) => {
|
|
53
|
-
if (!Array.isArray(value)) return [];
|
|
54
|
-
return value.flatMap((item) => {
|
|
55
|
-
const record = normalizeCustomServerRecord(item);
|
|
56
|
-
return record === null ? [] : [record];
|
|
57
|
-
});
|
|
58
|
-
};
|
|
59
|
-
const normalizeOAuthTokens = (value) => {
|
|
60
|
-
const result = OAuthTokensSchema.safeParse(value);
|
|
61
|
-
return result.success ? result.data : void 0;
|
|
62
|
-
};
|
|
63
|
-
const normalizeClientInformation = (value) => {
|
|
64
|
-
const result = OAuthClientInformationFullSchema.safeParse(value);
|
|
65
|
-
return result.success ? result.data : void 0;
|
|
66
|
-
};
|
|
67
|
-
const isSecureNetworkUrl = (value) => {
|
|
68
|
-
if (!isNonEmptyString(value)) return false;
|
|
69
|
-
try {
|
|
70
|
-
const url = new URL(value);
|
|
71
|
-
const isIpv4Loopback = /^127(?:\.\d{1,3}){3}$/.test(url.hostname);
|
|
72
|
-
return url.protocol === "https:" || url.protocol === "http:" && (url.hostname === "localhost" || url.hostname.endsWith(".localhost") || isIpv4Loopback || url.hostname === "[::1]");
|
|
73
|
-
} catch {
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
const isMcpServerUrl = (value) => {
|
|
78
|
-
if (typeof value !== "string") return false;
|
|
79
|
-
try {
|
|
80
|
-
const url = new URL(value);
|
|
81
|
-
return url.protocol === "https:" || url.protocol === "http:";
|
|
82
|
-
} catch {
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
const normalizeDiscoveryState = (value) => {
|
|
87
|
-
if (!isRecord(value) || !isSecureNetworkUrl(value.authorizationServerUrl)) return;
|
|
88
|
-
const state = { authorizationServerUrl: value.authorizationServerUrl };
|
|
89
|
-
if (isSecureNetworkUrl(value.resourceMetadataUrl)) state.resourceMetadataUrl = value.resourceMetadataUrl;
|
|
90
|
-
const metadata = OAuthMetadataSchema.safeParse(value.authorizationServerMetadata);
|
|
91
|
-
if (metadata.success) state.authorizationServerMetadata = metadata.data;
|
|
92
|
-
const resourceMetadata = OAuthProtectedResourceMetadataSchema.safeParse(value.resourceMetadata);
|
|
93
|
-
if (resourceMetadata.success) state.resourceMetadata = resourceMetadata.data;
|
|
94
|
-
return state;
|
|
95
|
-
};
|
|
96
|
-
const normalizePersistedAuthState = (value) => {
|
|
97
|
-
if (!isRecord(value)) return null;
|
|
98
|
-
if ("serverUrl" in value && !isMcpServerUrl(value.serverUrl)) return null;
|
|
99
|
-
const state = {};
|
|
100
|
-
if (isMcpServerUrl(value.serverUrl)) state.serverUrl = normalizeMcpServerUrl(value.serverUrl);
|
|
101
|
-
if (isNonEmptyString(value.token)) state.token = value.token;
|
|
102
|
-
if (isNonEmptyString(value.tokensClientId)) state.tokensClientId = value.tokensClientId;
|
|
103
|
-
if (isNonEmptyString(value.codeVerifier)) state.codeVerifier = value.codeVerifier;
|
|
104
|
-
if (isNonEmptyString(value.state)) state.state = value.state;
|
|
105
|
-
const tokens = normalizeOAuthTokens(value.tokens);
|
|
106
|
-
if (tokens) state.tokens = tokens;
|
|
107
|
-
const clientInformation = normalizeClientInformation(value.clientInformation);
|
|
108
|
-
if (clientInformation) state.clientInformation = clientInformation;
|
|
109
|
-
if (value.clientInformationSource === "registered") state.clientInformationSource = value.clientInformationSource;
|
|
110
|
-
const discoveryState = normalizeDiscoveryState(value.discoveryState);
|
|
111
|
-
if (discoveryState) state.discoveryState = discoveryState;
|
|
112
|
-
return Object.keys(state).length > 0 ? state : null;
|
|
113
|
-
};
|
|
114
15
|
const useMcpLocalStorage = (t0) => {
|
|
115
16
|
const $ = c(36);
|
|
116
17
|
let t1;
|