@mcp-use/client 2.0.0-beta.8 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -19
- package/dist/.tsbuildinfo +1 -1
- package/dist/auth/browser.d.ts +62 -6
- package/dist/auth/browser.d.ts.map +1 -1
- package/dist/auth/callback.d.ts.map +1 -1
- package/dist/auth/flow.d.ts.map +1 -1
- package/dist/auth/node.d.ts +115 -5
- package/dist/auth/node.d.ts.map +1 -1
- package/dist/auth/session-store.d.ts +7 -1
- package/dist/auth/session-store.d.ts.map +1 -1
- package/dist/auth/storage.d.ts +12 -3
- package/dist/auth/storage.d.ts.map +1 -1
- package/dist/code-mode/executor-e2b.d.ts.map +1 -1
- package/dist/core/base.d.ts +4 -8
- package/dist/core/base.d.ts.map +1 -1
- package/dist/core/browser.d.ts +14 -2
- package/dist/core/browser.d.ts.map +1 -1
- package/dist/core/config.d.ts +82 -20
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/node.d.ts +7 -10
- package/dist/core/node.d.ts.map +1 -1
- package/dist/core/session.d.ts +102 -25
- package/dist/core/session.d.ts.map +1 -1
- package/dist/index-browser.d.ts +1 -0
- package/dist/index-browser.d.ts.map +1 -1
- package/dist/index-browser.js +568 -163
- package/dist/index-browser.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +407 -92
- package/dist/index.js.map +1 -1
- package/dist/react/McpClientProvider.d.ts +22 -10
- package/dist/react/McpClientProvider.d.ts.map +1 -1
- package/dist/react/index.d.ts +5 -2
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1104 -368
- package/dist/react/index.js.map +1 -1
- package/dist/react/rpc-logger.d.ts +5 -0
- package/dist/react/rpc-logger.d.ts.map +1 -1
- package/dist/react/storage.d.ts +57 -10
- package/dist/react/storage.d.ts.map +1 -1
- package/dist/react/token-expiry.d.ts +9 -0
- package/dist/react/token-expiry.d.ts.map +1 -0
- package/dist/react/types.d.ts +134 -22
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/useMcp-helpers.d.ts.map +1 -1
- package/dist/react/useMcp-operations.d.ts +24 -6
- package/dist/react/useMcp-operations.d.ts.map +1 -1
- package/dist/react/useMcp.d.ts.map +1 -1
- package/dist/react/useMcpServerQueues.d.ts +36 -9
- package/dist/react/useMcpServerQueues.d.ts.map +1 -1
- package/dist/react/view/ViewRenderer.d.ts +12 -4
- package/dist/react/view/ViewRenderer.d.ts.map +1 -1
- package/dist/react/view/ext-apps-bridge.d.ts +1 -1
- package/dist/react/view/ext-apps-bridge.d.ts.map +1 -1
- package/dist/react/view/inject-openai-file-apis.d.ts.map +1 -1
- package/dist/react/view/parse-custom-props.d.ts +8 -0
- package/dist/react/view/parse-custom-props.d.ts.map +1 -1
- package/dist/react/view/resolve-view-resource.d.ts +10 -0
- package/dist/react/view/resolve-view-resource.d.ts.map +1 -1
- package/dist/react/view/sandbox-blob-url.d.ts +2 -0
- package/dist/react/view/sandbox-blob-url.d.ts.map +1 -1
- package/dist/react/view/types.d.ts +111 -4
- package/dist/react/view/types.d.ts.map +1 -1
- package/dist/react/view/view-detection.d.ts +18 -0
- package/dist/react/view/view-detection.d.ts.map +1 -1
- package/dist/react/view/view-host-policy.d.ts +61 -0
- package/dist/react/view/view-host-policy.d.ts.map +1 -0
- package/dist/sandbox.d.ts +2 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +283 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/telemetry/events.d.ts.map +1 -1
- package/dist/telemetry/tel-fetch.d.ts.map +1 -1
- package/dist/telemetry/telemetry.d.ts +5 -1
- package/dist/telemetry/telemetry.d.ts.map +1 -1
- package/dist/transport/base.d.ts +169 -34
- package/dist/transport/base.d.ts.map +1 -1
- package/dist/transport/connection-manager.d.ts +5 -1
- package/dist/transport/connection-manager.d.ts.map +1 -1
- package/dist/transport/http.d.ts +51 -2
- package/dist/transport/http.d.ts.map +1 -1
- package/dist/transport/stdio.d.ts +29 -1
- package/dist/transport/stdio.d.ts.map +1 -1
- package/dist/utils/elicitation.d.ts +4 -2
- package/dist/utils/elicitation.d.ts.map +1 -1
- package/dist/utils/logging.d.ts +1 -0
- package/dist/utils/logging.d.ts.map +1 -1
- package/dist/utils/version.d.ts +6 -0
- package/dist/utils/version.d.ts.map +1 -1
- package/package.json +9 -3
package/dist/index-browser.js
CHANGED
|
@@ -128,7 +128,7 @@ var Logger = class {
|
|
|
128
128
|
var logger = Logger.get();
|
|
129
129
|
|
|
130
130
|
// src/utils/version.ts
|
|
131
|
-
var VERSION = "2.0.0
|
|
131
|
+
var VERSION = "2.0.0";
|
|
132
132
|
function getPackageVersion() {
|
|
133
133
|
return VERSION;
|
|
134
134
|
}
|
|
@@ -148,16 +148,13 @@ var MCPAgentExecutionEvent = class extends BaseTelemetryEvent {
|
|
|
148
148
|
return {
|
|
149
149
|
// Core execution info
|
|
150
150
|
execution_method: this.data.executionMethod,
|
|
151
|
-
query: this.data.query,
|
|
152
151
|
query_length: this.data.query.length,
|
|
153
152
|
success: this.data.success,
|
|
154
153
|
// Agent configuration
|
|
155
154
|
model_provider: this.data.modelProvider,
|
|
156
155
|
model_name: this.data.modelName,
|
|
157
156
|
server_count: this.data.serverCount,
|
|
158
|
-
server_identifiers: this.data.serverIdentifiers,
|
|
159
157
|
total_tools_available: this.data.totalToolsAvailable,
|
|
160
|
-
tools_available_names: this.data.toolsAvailableNames,
|
|
161
158
|
max_steps_configured: this.data.maxStepsConfigured,
|
|
162
159
|
memory_enabled: this.data.memoryEnabled,
|
|
163
160
|
use_server_manager: this.data.useServerManager,
|
|
@@ -168,8 +165,6 @@ var MCPAgentExecutionEvent = class extends BaseTelemetryEvent {
|
|
|
168
165
|
// Execution results (always include, even if null)
|
|
169
166
|
steps_taken: this.data.stepsTaken ?? null,
|
|
170
167
|
tools_used_count: this.data.toolsUsedCount ?? null,
|
|
171
|
-
tools_used_names: this.data.toolsUsedNames ?? null,
|
|
172
|
-
response: this.data.response ?? null,
|
|
173
168
|
response_length: this.data.response ? this.data.response.length : null,
|
|
174
169
|
execution_time_ms: this.data.executionTimeMs ?? null,
|
|
175
170
|
error_type: this.data.errorType ?? null,
|
|
@@ -265,21 +260,72 @@ async function telFetch(url, init) {
|
|
|
265
260
|
}
|
|
266
261
|
var POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
267
262
|
var POSTHOG_API_KEY = "phc_lyTtbYwvkdSbrcMQNPiKiiRWrrM1seyKIMjycSvItEI";
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
263
|
+
var CONTENT_PROPERTY = /(^|_)(arguments?|args|body|command|headers?|location|message|query|response|secret|subject|token|uri|url|user_agent)(_|$)/i;
|
|
264
|
+
var IDENTIFYING_PROPERTY = /(^|_)(server_identifiers?|server_names?|servers|tool_names?|tools_(available|used)_names)(_|$)/i;
|
|
265
|
+
var AGGREGATE_PROPERTY = /(_count|_length|_duration(?:_ms)?|_time_ms|(^|_)num_[a-z0-9_]+)$/i;
|
|
266
|
+
function normalizePropertyKey(key) {
|
|
267
|
+
return key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-z0-9_$]+/gi, "_").toLowerCase();
|
|
268
|
+
}
|
|
269
|
+
function sanitizeValue(value, seen) {
|
|
270
|
+
if (Array.isArray(value)) {
|
|
271
|
+
if (seen.has(value)) {
|
|
272
|
+
throw new TypeError("Cyclic telemetry properties are not supported");
|
|
273
|
+
}
|
|
274
|
+
seen.add(value);
|
|
275
|
+
const sanitized = value.map((item) => sanitizeValue(item, seen));
|
|
276
|
+
seen.delete(value);
|
|
277
|
+
return sanitized;
|
|
278
|
+
}
|
|
279
|
+
if (value !== null && typeof value === "object" && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)) {
|
|
280
|
+
if (seen.has(value)) {
|
|
281
|
+
throw new TypeError("Cyclic telemetry properties are not supported");
|
|
282
|
+
}
|
|
283
|
+
seen.add(value);
|
|
284
|
+
const sanitized = sanitizeProperties(
|
|
285
|
+
value,
|
|
286
|
+
seen
|
|
287
|
+
);
|
|
288
|
+
seen.delete(value);
|
|
289
|
+
return sanitized;
|
|
290
|
+
}
|
|
291
|
+
return value;
|
|
292
|
+
}
|
|
293
|
+
function sanitizeProperties(properties, seen = /* @__PURE__ */ new WeakSet()) {
|
|
294
|
+
const sanitized = {};
|
|
295
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
296
|
+
const normalizedKey = normalizePropertyKey(key);
|
|
297
|
+
if (AGGREGATE_PROPERTY.test(normalizedKey)) {
|
|
298
|
+
if (value === null || typeof value === "number") {
|
|
299
|
+
sanitized[key] = value;
|
|
300
|
+
}
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (IDENTIFYING_PROPERTY.test(normalizedKey) || CONTENT_PROPERTY.test(normalizedKey)) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
sanitized[key] = sanitizeValue(value, seen);
|
|
307
|
+
}
|
|
308
|
+
return sanitized;
|
|
309
|
+
}
|
|
310
|
+
async function capturePostHog(params) {
|
|
311
|
+
try {
|
|
312
|
+
const host = params.host ?? POSTHOG_HOST;
|
|
313
|
+
const apiKey = params.apiKey ?? POSTHOG_API_KEY;
|
|
314
|
+
const body = JSON.stringify({
|
|
276
315
|
api_key: apiKey,
|
|
277
316
|
event: params.event,
|
|
278
317
|
distinct_id: params.distinctId,
|
|
279
|
-
properties: params.properties,
|
|
318
|
+
properties: sanitizeProperties(params.properties),
|
|
280
319
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
281
|
-
})
|
|
282
|
-
|
|
320
|
+
});
|
|
321
|
+
await telFetch(`${host}/i/v0/e/`, {
|
|
322
|
+
method: "POST",
|
|
323
|
+
headers: { "Content-Type": "application/json" },
|
|
324
|
+
keepalive: true,
|
|
325
|
+
body
|
|
326
|
+
});
|
|
327
|
+
} catch {
|
|
328
|
+
}
|
|
283
329
|
}
|
|
284
330
|
|
|
285
331
|
// src/telemetry/telemetry.ts
|
|
@@ -589,6 +635,186 @@ setConnectorTelemetryTracker(
|
|
|
589
635
|
(data) => Telemetry.getInstance().trackConnectorInit(data).catch(() => void 0)
|
|
590
636
|
);
|
|
591
637
|
|
|
638
|
+
// src/auth/storage.ts
|
|
639
|
+
var AUTH_CRYPTO_DATABASE = "mcp-use-oauth-crypto";
|
|
640
|
+
var AUTH_CRYPTO_STORE = "keys";
|
|
641
|
+
var AUTH_CRYPTO_KEY = "aes-gcm-v1";
|
|
642
|
+
var textEncoder = new TextEncoder();
|
|
643
|
+
var textDecoder = new TextDecoder();
|
|
644
|
+
var LocalStorageKVStore = class {
|
|
645
|
+
fallback = /* @__PURE__ */ new Map();
|
|
646
|
+
keyPromise;
|
|
647
|
+
durable = true;
|
|
648
|
+
async get(key) {
|
|
649
|
+
if (!this.durable) return this.fallback.get(key) ?? null;
|
|
650
|
+
let stored;
|
|
651
|
+
try {
|
|
652
|
+
stored = localStorage.getItem(key);
|
|
653
|
+
} catch {
|
|
654
|
+
this.durable = false;
|
|
655
|
+
return this.fallback.get(key) ?? null;
|
|
656
|
+
}
|
|
657
|
+
if (stored === null) return null;
|
|
658
|
+
const envelope = parseEncryptedEnvelope(stored);
|
|
659
|
+
if (!envelope) {
|
|
660
|
+
await this.set(key, stored);
|
|
661
|
+
return stored;
|
|
662
|
+
}
|
|
663
|
+
try {
|
|
664
|
+
const cryptoKey = await this.getCryptoKey();
|
|
665
|
+
const plaintext = await globalThis.crypto.subtle.decrypt(
|
|
666
|
+
{
|
|
667
|
+
name: "AES-GCM",
|
|
668
|
+
iv: decodeBase64(envelope.iv),
|
|
669
|
+
additionalData: textEncoder.encode(key)
|
|
670
|
+
},
|
|
671
|
+
cryptoKey,
|
|
672
|
+
decodeBase64(envelope.ciphertext)
|
|
673
|
+
);
|
|
674
|
+
return textDecoder.decode(plaintext);
|
|
675
|
+
} catch {
|
|
676
|
+
await this.remove(key);
|
|
677
|
+
return null;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
async set(key, value) {
|
|
681
|
+
if (!this.durable) {
|
|
682
|
+
this.fallback.set(key, value);
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
try {
|
|
686
|
+
const cryptoKey = await this.getCryptoKey();
|
|
687
|
+
const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));
|
|
688
|
+
const ciphertext = await globalThis.crypto.subtle.encrypt(
|
|
689
|
+
{
|
|
690
|
+
name: "AES-GCM",
|
|
691
|
+
iv,
|
|
692
|
+
additionalData: textEncoder.encode(key)
|
|
693
|
+
},
|
|
694
|
+
cryptoKey,
|
|
695
|
+
textEncoder.encode(value)
|
|
696
|
+
);
|
|
697
|
+
const envelope = {
|
|
698
|
+
v: 1,
|
|
699
|
+
alg: "A256GCM",
|
|
700
|
+
iv: encodeBase64(iv),
|
|
701
|
+
ciphertext: encodeBase64(new Uint8Array(ciphertext))
|
|
702
|
+
};
|
|
703
|
+
localStorage.setItem(key, JSON.stringify(envelope));
|
|
704
|
+
this.fallback.delete(key);
|
|
705
|
+
} catch {
|
|
706
|
+
this.durable = false;
|
|
707
|
+
try {
|
|
708
|
+
localStorage.removeItem(key);
|
|
709
|
+
} catch {
|
|
710
|
+
}
|
|
711
|
+
this.fallback.set(key, value);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
remove(key) {
|
|
715
|
+
this.fallback.delete(key);
|
|
716
|
+
try {
|
|
717
|
+
localStorage.removeItem(key);
|
|
718
|
+
} catch {
|
|
719
|
+
this.durable = false;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
keys() {
|
|
723
|
+
const out = new Set(this.fallback.keys());
|
|
724
|
+
if (this.durable) {
|
|
725
|
+
try {
|
|
726
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
727
|
+
const key = localStorage.key(i);
|
|
728
|
+
if (key) out.add(key);
|
|
729
|
+
}
|
|
730
|
+
} catch {
|
|
731
|
+
this.durable = false;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
return [...out];
|
|
735
|
+
}
|
|
736
|
+
getCryptoKey() {
|
|
737
|
+
this.keyPromise ??= getOrCreateCryptoKey();
|
|
738
|
+
return this.keyPromise;
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
function parseEncryptedEnvelope(value) {
|
|
742
|
+
try {
|
|
743
|
+
const parsed = JSON.parse(value);
|
|
744
|
+
if (!parsed || typeof parsed !== "object" || !("v" in parsed) || parsed.v !== 1 || !("alg" in parsed) || parsed.alg !== "A256GCM" || !("iv" in parsed) || typeof parsed.iv !== "string" || !("ciphertext" in parsed) || typeof parsed.ciphertext !== "string") {
|
|
745
|
+
return void 0;
|
|
746
|
+
}
|
|
747
|
+
return parsed;
|
|
748
|
+
} catch {
|
|
749
|
+
return void 0;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
async function getOrCreateCryptoKey() {
|
|
753
|
+
if (!globalThis.crypto?.subtle || typeof indexedDB === "undefined") {
|
|
754
|
+
throw new Error("Durable browser cryptography is unavailable");
|
|
755
|
+
}
|
|
756
|
+
const candidate = await globalThis.crypto.subtle.generateKey(
|
|
757
|
+
{ name: "AES-GCM", length: 256 },
|
|
758
|
+
false,
|
|
759
|
+
["encrypt", "decrypt"]
|
|
760
|
+
);
|
|
761
|
+
const database = await openCryptoDatabase();
|
|
762
|
+
try {
|
|
763
|
+
return await new Promise((resolve, reject) => {
|
|
764
|
+
const transaction = database.transaction(AUTH_CRYPTO_STORE, "readwrite");
|
|
765
|
+
const store = transaction.objectStore(AUTH_CRYPTO_STORE);
|
|
766
|
+
const request = store.get(AUTH_CRYPTO_KEY);
|
|
767
|
+
let selected;
|
|
768
|
+
request.onsuccess = () => {
|
|
769
|
+
selected = request.result;
|
|
770
|
+
if (!selected) {
|
|
771
|
+
selected = candidate;
|
|
772
|
+
store.put(candidate, AUTH_CRYPTO_KEY);
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
request.onerror = () => reject(request.error);
|
|
776
|
+
transaction.oncomplete = () => {
|
|
777
|
+
if (selected) resolve(selected);
|
|
778
|
+
else reject(new Error("OAuth encryption key was not initialized"));
|
|
779
|
+
};
|
|
780
|
+
transaction.onerror = () => reject(transaction.error);
|
|
781
|
+
transaction.onabort = () => reject(transaction.error);
|
|
782
|
+
});
|
|
783
|
+
} finally {
|
|
784
|
+
database.close();
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
function openCryptoDatabase() {
|
|
788
|
+
return new Promise((resolve, reject) => {
|
|
789
|
+
const request = indexedDB.open(AUTH_CRYPTO_DATABASE, 1);
|
|
790
|
+
request.onupgradeneeded = () => {
|
|
791
|
+
const database = request.result;
|
|
792
|
+
if (!database.objectStoreNames.contains(AUTH_CRYPTO_STORE)) {
|
|
793
|
+
database.createObjectStore(AUTH_CRYPTO_STORE);
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
request.onsuccess = () => resolve(request.result);
|
|
797
|
+
request.onerror = () => reject(request.error);
|
|
798
|
+
request.onblocked = () => reject(new Error("OAuth encryption database is blocked"));
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
function encodeBase64(bytes) {
|
|
802
|
+
let binary = "";
|
|
803
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
804
|
+
return btoa(binary);
|
|
805
|
+
}
|
|
806
|
+
function decodeBase64(value) {
|
|
807
|
+
const binary = atob(value);
|
|
808
|
+
const bytes = new Uint8Array(binary.length);
|
|
809
|
+
for (let index = 0; index < binary.length; index++) {
|
|
810
|
+
bytes[index] = binary.charCodeAt(index);
|
|
811
|
+
}
|
|
812
|
+
return bytes;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// src/auth/session-store.ts
|
|
816
|
+
import { validateClientMetadataUrl } from "@modelcontextprotocol/client";
|
|
817
|
+
|
|
592
818
|
// src/auth/url.ts
|
|
593
819
|
function sanitizeUrl(raw) {
|
|
594
820
|
const abort = () => {
|
|
@@ -613,29 +839,7 @@ function sanitizeParam([k, v]) {
|
|
|
613
839
|
return `${encodeURIComponent(k)}${v.length > 0 ? `=${encodeURIComponent(v)}` : ""}`;
|
|
614
840
|
}
|
|
615
841
|
|
|
616
|
-
// src/auth/storage.ts
|
|
617
|
-
var LocalStorageKVStore = class {
|
|
618
|
-
get(key) {
|
|
619
|
-
return localStorage.getItem(key);
|
|
620
|
-
}
|
|
621
|
-
set(key, value) {
|
|
622
|
-
localStorage.setItem(key, value);
|
|
623
|
-
}
|
|
624
|
-
remove(key) {
|
|
625
|
-
localStorage.removeItem(key);
|
|
626
|
-
}
|
|
627
|
-
keys() {
|
|
628
|
-
const out = [];
|
|
629
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
630
|
-
const k = localStorage.key(i);
|
|
631
|
-
if (k) out.push(k);
|
|
632
|
-
}
|
|
633
|
-
return out;
|
|
634
|
-
}
|
|
635
|
-
};
|
|
636
|
-
|
|
637
842
|
// src/auth/session-store.ts
|
|
638
|
-
import { validateClientMetadataUrl } from "@modelcontextprotocol/client";
|
|
639
843
|
var OAuthSessionStore = class _OAuthSessionStore {
|
|
640
844
|
serverUrl;
|
|
641
845
|
storageKeyPrefix;
|
|
@@ -741,29 +945,39 @@ var OAuthSessionStore = class _OAuthSessionStore {
|
|
|
741
945
|
if (ctx) await this.store.set(this.credentialKey("tokens"), serialized);
|
|
742
946
|
await this.store.remove(this.getKey("code_verifier"));
|
|
743
947
|
await this.store.remove(this.getKey("last_auth_url"));
|
|
948
|
+
await this.store.remove(this.getKey("last_auth_callback_url"));
|
|
744
949
|
}
|
|
745
950
|
async clientInformation(ctx) {
|
|
951
|
+
if (!this.allowClientSecret) {
|
|
952
|
+
const registeredRedirectUri = await this.store.get(
|
|
953
|
+
this.getKey("client_info_redirect_uri")
|
|
954
|
+
);
|
|
955
|
+
if (registeredRedirectUri !== this.redirectUrl) {
|
|
956
|
+
await this.invalidateCredentials("registration");
|
|
957
|
+
console.info(
|
|
958
|
+
`[${this.storageKeyPrefix}] Re-registering browser OAuth client after its Inspector callback changed or could not be verified.`
|
|
959
|
+
);
|
|
960
|
+
return void 0;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
746
963
|
const stored = await this.readCredential("client_info", ctx);
|
|
747
964
|
if (!stored) return void 0;
|
|
748
965
|
const { key, value: clientInfo } = stored;
|
|
749
966
|
try {
|
|
750
967
|
if (!this.allowClientSecret && clientInfo.client_secret) {
|
|
751
|
-
await this.
|
|
752
|
-
if (ctx) await this.store.remove(this.credentialKey("client_info"));
|
|
968
|
+
await this.invalidateCredentials("registration");
|
|
753
969
|
console.warn(
|
|
754
|
-
`[${this.storageKeyPrefix}]
|
|
970
|
+
`[${this.storageKeyPrefix}] Recovered stale browser OAuth credentials containing a client_secret.`
|
|
755
971
|
);
|
|
756
972
|
return void 0;
|
|
757
973
|
}
|
|
758
974
|
const storedRedirectUris = Array.isArray(clientInfo.redirect_uris) ? clientInfo.redirect_uris : [];
|
|
759
|
-
const hasMatchingRedirect = storedRedirectUris.length === 0 || storedRedirectUris.includes(this.redirectUrl);
|
|
975
|
+
const hasMatchingRedirect = storedRedirectUris.length === 0 && this.allowClientSecret || storedRedirectUris.includes(this.redirectUrl);
|
|
760
976
|
if (!hasMatchingRedirect) {
|
|
761
977
|
console.info(
|
|
762
|
-
`[${this.storageKeyPrefix}]
|
|
978
|
+
`[${this.storageKeyPrefix}] Recovering cached OAuth credentials after a redirect URI change.`
|
|
763
979
|
);
|
|
764
|
-
await this.
|
|
765
|
-
await this.store.remove(this.credentialKey("tokens", ctx));
|
|
766
|
-
await this.store.remove(this.getKey("last_auth_url"));
|
|
980
|
+
await this.invalidateCredentials("registration");
|
|
767
981
|
return void 0;
|
|
768
982
|
}
|
|
769
983
|
return clientInfo;
|
|
@@ -781,11 +995,20 @@ var OAuthSessionStore = class _OAuthSessionStore {
|
|
|
781
995
|
"Browser OAuth clients must be public clients; client_secret persistence is not allowed."
|
|
782
996
|
);
|
|
783
997
|
}
|
|
784
|
-
const
|
|
998
|
+
const persistedClientInformation = !this.allowClientSecret && (!("redirect_uris" in clientInformation) || !Array.isArray(
|
|
999
|
+
clientInformation.redirect_uris
|
|
1000
|
+
) || clientInformation.redirect_uris.length === 0) ? { ...clientInformation, redirect_uris: [this.redirectUrl] } : clientInformation;
|
|
1001
|
+
const serialized = JSON.stringify(persistedClientInformation);
|
|
785
1002
|
await this.store.set(this.credentialKey("client_info", ctx), serialized);
|
|
786
1003
|
if (ctx) {
|
|
787
1004
|
await this.store.set(this.credentialKey("client_info"), serialized);
|
|
788
1005
|
}
|
|
1006
|
+
if (!this.allowClientSecret) {
|
|
1007
|
+
await this.store.set(
|
|
1008
|
+
this.getKey("client_info_redirect_uri"),
|
|
1009
|
+
this.redirectUrl
|
|
1010
|
+
);
|
|
1011
|
+
}
|
|
789
1012
|
}
|
|
790
1013
|
async saveCodeVerifier(codeVerifier) {
|
|
791
1014
|
await this.store.set(this.getKey("code_verifier"), codeVerifier);
|
|
@@ -810,11 +1033,22 @@ var OAuthSessionStore = class _OAuthSessionStore {
|
|
|
810
1033
|
}
|
|
811
1034
|
};
|
|
812
1035
|
switch (scope) {
|
|
1036
|
+
case "registration":
|
|
1037
|
+
await removeCredentialKeys("tokens");
|
|
1038
|
+
await removeCredentialKeys("client_info");
|
|
1039
|
+
await this.store.remove(this.getKey("code_verifier"));
|
|
1040
|
+
await this.store.remove(this.getKey("last_auth_url"));
|
|
1041
|
+
await this.store.remove(this.getKey("last_auth_callback_url"));
|
|
1042
|
+
await this.store.remove(this.getKey("client_info_redirect_uri"));
|
|
1043
|
+
await this.store.remove(this.getKey("token_endpoint"));
|
|
1044
|
+
break;
|
|
813
1045
|
case "all":
|
|
814
1046
|
await removeCredentialKeys("tokens");
|
|
815
1047
|
await removeCredentialKeys("client_info");
|
|
816
1048
|
await this.store.remove(this.getKey("code_verifier"));
|
|
817
1049
|
await this.store.remove(this.getKey("last_auth_url"));
|
|
1050
|
+
await this.store.remove(this.getKey("last_auth_callback_url"));
|
|
1051
|
+
await this.store.remove(this.getKey("client_info_redirect_uri"));
|
|
818
1052
|
await this.store.remove(this.getKey("discovery_state"));
|
|
819
1053
|
await this.store.remove(this.getKey("token_endpoint"));
|
|
820
1054
|
break;
|
|
@@ -864,7 +1098,7 @@ var OAuthSessionStore = class _OAuthSessionStore {
|
|
|
864
1098
|
*/
|
|
865
1099
|
async storeAuthorizationState(authorizationUrl, opts = {}) {
|
|
866
1100
|
const state = globalThis.crypto.randomUUID();
|
|
867
|
-
const stateKey = `${this.storageKeyPrefix}
|
|
1101
|
+
const stateKey = `${this.storageKeyPrefix}_${this.serverUrlHash}_state_${state}`;
|
|
868
1102
|
const stateData = {
|
|
869
1103
|
serverUrlHash: this.serverUrlHash,
|
|
870
1104
|
expiry: Date.now() + 1e3 * 60 * 10,
|
|
@@ -884,6 +1118,10 @@ var OAuthSessionStore = class _OAuthSessionStore {
|
|
|
884
1118
|
authorizationUrl.searchParams.set("state", state);
|
|
885
1119
|
const sanitizedAuthUrl = sanitizeUrl(authorizationUrl.toString());
|
|
886
1120
|
await this.store.set(stateKey, JSON.stringify(stateData));
|
|
1121
|
+
await this.store.set(
|
|
1122
|
+
this.getKey("last_auth_callback_url"),
|
|
1123
|
+
this.redirectUrl
|
|
1124
|
+
);
|
|
887
1125
|
await this.store.set(this.getKey("last_auth_url"), sanitizedAuthUrl);
|
|
888
1126
|
return sanitizedAuthUrl;
|
|
889
1127
|
}
|
|
@@ -913,16 +1151,30 @@ async function serializeBody(body) {
|
|
|
913
1151
|
if (body instanceof Blob) return await body.text();
|
|
914
1152
|
return body;
|
|
915
1153
|
}
|
|
1154
|
+
function trimTrailingSlashes(value) {
|
|
1155
|
+
let end = value.length;
|
|
1156
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47) {
|
|
1157
|
+
end--;
|
|
1158
|
+
}
|
|
1159
|
+
return value.slice(0, end);
|
|
1160
|
+
}
|
|
916
1161
|
var BrowserOAuthClientProvider = class {
|
|
1162
|
+
/** Protected MCP server URL associated with this provider. */
|
|
917
1163
|
serverUrl;
|
|
1164
|
+
/** Pre-registered public client information, when configured. */
|
|
918
1165
|
staticClientInfo;
|
|
919
1166
|
session;
|
|
1167
|
+
storage;
|
|
920
1168
|
// Browser-only state
|
|
1169
|
+
/** Whether initial connection waits for explicit authentication. */
|
|
921
1170
|
preventAutoAuth;
|
|
922
1171
|
useRedirectFlow;
|
|
923
1172
|
oauthProxyUrl;
|
|
924
1173
|
connectionUrl;
|
|
925
1174
|
proxyOAuthRequests;
|
|
1175
|
+
lastAttemptedAuthUrl = null;
|
|
1176
|
+
authorizationPending = false;
|
|
1177
|
+
/** Callback invoked immediately before an authorization popup opens. */
|
|
926
1178
|
onPopupWindow;
|
|
927
1179
|
constructor(serverUrl, options = {}) {
|
|
928
1180
|
if (options.staticClientInfo?.client_secret) {
|
|
@@ -931,10 +1183,11 @@ var BrowserOAuthClientProvider = class {
|
|
|
931
1183
|
);
|
|
932
1184
|
}
|
|
933
1185
|
this.serverUrl = serverUrl;
|
|
1186
|
+
this.storage = new LocalStorageKVStore();
|
|
934
1187
|
this.session = new OAuthSessionStore(
|
|
935
1188
|
serverUrl,
|
|
936
1189
|
{ ...options, allowClientSecret: false },
|
|
937
|
-
|
|
1190
|
+
this.storage
|
|
938
1191
|
);
|
|
939
1192
|
this.preventAutoAuth = options.preventAutoAuth;
|
|
940
1193
|
this.useRedirectFlow = options.useRedirectFlow;
|
|
@@ -945,33 +1198,54 @@ var BrowserOAuthClientProvider = class {
|
|
|
945
1198
|
this.onPopupWindow = options.onPopupWindow;
|
|
946
1199
|
}
|
|
947
1200
|
// --- Identity / key fields exposed for callback handling ---
|
|
1201
|
+
/** Prefix used for persisted OAuth keys. */
|
|
948
1202
|
get storageKeyPrefix() {
|
|
949
1203
|
return this.session.storageKeyPrefix;
|
|
950
1204
|
}
|
|
1205
|
+
/** Stable hash used to namespace storage for this server. */
|
|
951
1206
|
get serverUrlHash() {
|
|
952
1207
|
return this.session.serverUrlHash;
|
|
953
1208
|
}
|
|
1209
|
+
/** Human-readable OAuth client name. */
|
|
954
1210
|
get clientName() {
|
|
955
1211
|
return this.session.clientName;
|
|
956
1212
|
}
|
|
1213
|
+
/** Public website describing the OAuth client. */
|
|
957
1214
|
get clientUri() {
|
|
958
1215
|
return this.session.clientUri;
|
|
959
1216
|
}
|
|
1217
|
+
/** Public OAuth client logo URL. */
|
|
960
1218
|
get logoUri() {
|
|
961
1219
|
return this.session.logoUri;
|
|
962
1220
|
}
|
|
1221
|
+
/** OAuth redirect URI. */
|
|
963
1222
|
get callbackUrl() {
|
|
964
1223
|
return this.session.callbackUrl;
|
|
965
1224
|
}
|
|
1225
|
+
/** Space-delimited OAuth scopes requested by the client. */
|
|
966
1226
|
get scope() {
|
|
967
1227
|
return this.session.scope;
|
|
968
1228
|
}
|
|
969
1229
|
get clientMetadataUrl() {
|
|
970
1230
|
return this.session.clientMetadataUrl;
|
|
971
1231
|
}
|
|
1232
|
+
/**
|
|
1233
|
+
* Returns a provider-scoped storage key.
|
|
1234
|
+
*
|
|
1235
|
+
* @param keySuffix - Suffix identifying the stored value.
|
|
1236
|
+
* @returns Namespaced storage key.
|
|
1237
|
+
*/
|
|
972
1238
|
getKey(keySuffix) {
|
|
973
1239
|
return this.session.getKey(keySuffix);
|
|
974
1240
|
}
|
|
1241
|
+
/** Whether an authorization flow is awaiting completion. */
|
|
1242
|
+
get hasPendingFlow() {
|
|
1243
|
+
return this.authorizationPending;
|
|
1244
|
+
}
|
|
1245
|
+
/** Marks the current authorization flow as complete. */
|
|
1246
|
+
markFlowComplete() {
|
|
1247
|
+
this.authorizationPending = false;
|
|
1248
|
+
}
|
|
975
1249
|
/**
|
|
976
1250
|
* Re-anchor an SDK-derived OAuth discovery URL from the MCP connection
|
|
977
1251
|
* (proxy) origin onto the actual MCP server.
|
|
@@ -999,8 +1273,8 @@ var BrowserOAuthClientProvider = class {
|
|
|
999
1273
|
const [doc, ...suffixParts] = rest.split("/");
|
|
1000
1274
|
if (!doc) return url;
|
|
1001
1275
|
const suffix = suffixParts.length ? `/${suffixParts.join("/")}` : "";
|
|
1002
|
-
const connectionPath = connection.pathname
|
|
1003
|
-
const targetPath = target.pathname
|
|
1276
|
+
const connectionPath = trimTrailingSlashes(connection.pathname);
|
|
1277
|
+
const targetPath = trimTrailingSlashes(target.pathname);
|
|
1004
1278
|
const newSuffix = suffix && suffix === connectionPath ? targetPath : suffix;
|
|
1005
1279
|
return `${target.origin}/.well-known/${doc}${newSuffix}${requested.search}`;
|
|
1006
1280
|
} catch {
|
|
@@ -1019,20 +1293,19 @@ var BrowserOAuthClientProvider = class {
|
|
|
1019
1293
|
* therefore never alters fetch behavior for other servers, other
|
|
1020
1294
|
* connections, or the rest of the page.
|
|
1021
1295
|
*
|
|
1022
|
-
*
|
|
1023
|
-
*
|
|
1024
|
-
*
|
|
1025
|
-
*
|
|
1296
|
+
* OAuth metadata is always fetched with `cache: "no-store"`, including in
|
|
1297
|
+
* direct mode. Authorization servers commonly vary CORS headers by Origin;
|
|
1298
|
+
* bypassing the browser HTTP cache prevents a revalidated response cached
|
|
1299
|
+
* for another localhost origin from poisoning discovery. When OAuth proxying
|
|
1300
|
+
* is disabled or no `oauthProxyUrl` is configured, all requests still go
|
|
1301
|
+
* directly to their original URLs.
|
|
1026
1302
|
*
|
|
1027
1303
|
* @param baseFetch - The fetch used for non-OAuth requests and for the
|
|
1028
1304
|
* underlying proxy calls. Defaults to the global `fetch`.
|
|
1029
1305
|
*/
|
|
1030
1306
|
getProxyFetch(baseFetch) {
|
|
1031
|
-
if (!this.proxyOAuthRequests || !this.oauthProxyUrl) {
|
|
1032
|
-
return baseFetch;
|
|
1033
|
-
}
|
|
1034
1307
|
const base = baseFetch ?? globalThis.fetch.bind(globalThis);
|
|
1035
|
-
const oauthProxyUrl = this.oauthProxyUrl;
|
|
1308
|
+
const oauthProxyUrl = this.proxyOAuthRequests && this.oauthProxyUrl ? this.oauthProxyUrl : void 0;
|
|
1036
1309
|
const discoveredEndpoints = /* @__PURE__ */ new Set();
|
|
1037
1310
|
let restoredDiscovery = false;
|
|
1038
1311
|
return async (input, init) => {
|
|
@@ -1045,6 +1318,12 @@ var BrowserOAuthClientProvider = class {
|
|
|
1045
1318
|
return await base(input, init);
|
|
1046
1319
|
}
|
|
1047
1320
|
const isMetadata = pathname.includes("/.well-known/");
|
|
1321
|
+
if (!oauthProxyUrl) {
|
|
1322
|
+
return await base(
|
|
1323
|
+
isMetadata ? url : input,
|
|
1324
|
+
isMetadata ? { ...init, cache: "no-store" } : init
|
|
1325
|
+
);
|
|
1326
|
+
}
|
|
1048
1327
|
if (!restoredDiscovery) {
|
|
1049
1328
|
restoredDiscovery = true;
|
|
1050
1329
|
const metadata = (await this.discoveryState())?.authorizationServerMetadata;
|
|
@@ -1079,7 +1358,8 @@ var BrowserOAuthClientProvider = class {
|
|
|
1079
1358
|
if (isMetadata) {
|
|
1080
1359
|
const response2 = await base(proxyEndpoint, {
|
|
1081
1360
|
...init,
|
|
1082
|
-
method: "GET"
|
|
1361
|
+
method: "GET",
|
|
1362
|
+
cache: "no-store"
|
|
1083
1363
|
});
|
|
1084
1364
|
try {
|
|
1085
1365
|
const metadata = await response2.clone().json();
|
|
@@ -1097,23 +1377,40 @@ var BrowserOAuthClientProvider = class {
|
|
|
1097
1377
|
}
|
|
1098
1378
|
return response2;
|
|
1099
1379
|
}
|
|
1100
|
-
const
|
|
1380
|
+
const inputRequest = input instanceof Request ? input : void 0;
|
|
1381
|
+
const method = init?.method ?? inputRequest?.method ?? "POST";
|
|
1382
|
+
const requestHeaders = init?.headers ?? inputRequest?.headers;
|
|
1383
|
+
let body;
|
|
1384
|
+
if (init?.body !== void 0 && init.body !== null) {
|
|
1385
|
+
body = await serializeBody(init.body);
|
|
1386
|
+
} else if (inputRequest?.body && method !== "GET" && method !== "HEAD") {
|
|
1387
|
+
body = await inputRequest.clone().text();
|
|
1388
|
+
}
|
|
1101
1389
|
const response = await base(proxyEndpoint, {
|
|
1102
1390
|
method: "POST",
|
|
1103
1391
|
headers: { "Content-Type": "application/json" },
|
|
1104
1392
|
body: JSON.stringify({
|
|
1105
1393
|
serverUrl: this.serverUrl,
|
|
1106
1394
|
url,
|
|
1107
|
-
method
|
|
1108
|
-
headers:
|
|
1395
|
+
method,
|
|
1396
|
+
headers: requestHeaders ? Object.fromEntries(new Headers(requestHeaders)) : {},
|
|
1109
1397
|
body
|
|
1110
1398
|
})
|
|
1111
1399
|
});
|
|
1112
1400
|
const data = await response.json();
|
|
1401
|
+
if (!response.ok || typeof data.status !== "number") {
|
|
1402
|
+
return new Response(JSON.stringify(data), {
|
|
1403
|
+
status: response.status,
|
|
1404
|
+
statusText: response.statusText,
|
|
1405
|
+
headers: response.headers
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1113
1408
|
return new Response(JSON.stringify(data.body), {
|
|
1114
1409
|
status: data.status,
|
|
1115
|
-
statusText: data.statusText,
|
|
1116
|
-
headers: new Headers(
|
|
1410
|
+
statusText: typeof data.statusText === "string" ? data.statusText : void 0,
|
|
1411
|
+
headers: new Headers(
|
|
1412
|
+
data.headers && typeof data.headers === "object" ? data.headers : void 0
|
|
1413
|
+
)
|
|
1117
1414
|
});
|
|
1118
1415
|
};
|
|
1119
1416
|
}
|
|
@@ -1128,15 +1425,41 @@ var BrowserOAuthClientProvider = class {
|
|
|
1128
1425
|
return this.session.tokens(ctx);
|
|
1129
1426
|
}
|
|
1130
1427
|
saveTokens(tokens, ctx) {
|
|
1428
|
+
this.lastAttemptedAuthUrl = null;
|
|
1429
|
+
this.authorizationPending = false;
|
|
1131
1430
|
return this.session.saveTokens(tokens, ctx);
|
|
1132
1431
|
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Returns the configured or dynamically registered OAuth client information.
|
|
1434
|
+
*
|
|
1435
|
+
* @param ctx - Optional registration context.
|
|
1436
|
+
* @returns OAuth client information, or `undefined` when not registered.
|
|
1437
|
+
*/
|
|
1133
1438
|
async clientInformation(ctx) {
|
|
1134
1439
|
if (this.staticClientInfo) return this.staticClientInfo;
|
|
1135
1440
|
return this.session.clientInformation(ctx);
|
|
1136
1441
|
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Persists public OAuth client registration information.
|
|
1444
|
+
*
|
|
1445
|
+
* Static client configuration takes precedence, and browser providers discard
|
|
1446
|
+
* any client secret returned for a public client.
|
|
1447
|
+
*
|
|
1448
|
+
* @param clientInformation - Registration information to save.
|
|
1449
|
+
* @param ctx - Optional registration context.
|
|
1450
|
+
*/
|
|
1137
1451
|
async saveClientInformation(clientInformation, ctx) {
|
|
1138
1452
|
if (this.staticClientInfo) return;
|
|
1139
|
-
|
|
1453
|
+
const { client_secret: discardedClientSecret, ...publicClientInformation } = clientInformation;
|
|
1454
|
+
if (discardedClientSecret) {
|
|
1455
|
+
console.info(
|
|
1456
|
+
`[${this.storageKeyPrefix}] Discarded client_secret returned for a public browser OAuth client.`
|
|
1457
|
+
);
|
|
1458
|
+
}
|
|
1459
|
+
return this.session.saveClientInformation(
|
|
1460
|
+
publicClientInformation,
|
|
1461
|
+
ctx
|
|
1462
|
+
);
|
|
1140
1463
|
}
|
|
1141
1464
|
codeVerifier() {
|
|
1142
1465
|
return this.session.codeVerifier();
|
|
@@ -1185,20 +1508,26 @@ var BrowserOAuthClientProvider = class {
|
|
|
1185
1508
|
* use `redirectToAuthorization` for that.
|
|
1186
1509
|
*/
|
|
1187
1510
|
async prepareAuthorizationUrl(authorizationUrl) {
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1511
|
+
const prepared = await this.session.storeAuthorizationState(
|
|
1512
|
+
authorizationUrl,
|
|
1513
|
+
{
|
|
1514
|
+
extraProviderOptions: {
|
|
1515
|
+
oauthProxyUrl: this.oauthProxyUrl,
|
|
1516
|
+
...this.clientMetadataUrl ? { clientMetadataUrl: this.clientMetadataUrl } : {},
|
|
1517
|
+
...this.staticClientInfo ? { staticClientInfo: this.staticClientInfo } : {},
|
|
1518
|
+
...this.scope ? { scope: this.scope } : {}
|
|
1519
|
+
},
|
|
1520
|
+
flowType: this.useRedirectFlow ? "redirect" : "popup",
|
|
1521
|
+
returnUrl: typeof window !== "undefined" ? window.location.href : void 0
|
|
1522
|
+
}
|
|
1523
|
+
);
|
|
1524
|
+
this.lastAttemptedAuthUrl = prepared;
|
|
1525
|
+
this.authorizationPending = true;
|
|
1526
|
+
return prepared;
|
|
1198
1527
|
}
|
|
1199
1528
|
/**
|
|
1200
1529
|
* Redirects the user agent to the authorization URL, storing necessary state.
|
|
1201
|
-
* @param authorizationUrl The fully constructed authorization URL from the SDK.
|
|
1530
|
+
* @param authorizationUrl - The fully constructed authorization URL from the SDK.
|
|
1202
1531
|
*/
|
|
1203
1532
|
async redirectToAuthorization(authorizationUrl) {
|
|
1204
1533
|
const sanitizedAuthUrl = await this.prepareAuthorizationUrl(authorizationUrl);
|
|
@@ -1246,40 +1575,27 @@ var BrowserOAuthClientProvider = class {
|
|
|
1246
1575
|
* Retrieves the last URL passed to `redirectToAuthorization`. Useful for manual fallback.
|
|
1247
1576
|
*/
|
|
1248
1577
|
getLastAttemptedAuthUrl() {
|
|
1249
|
-
|
|
1250
|
-
if (!storedUrl) return null;
|
|
1251
|
-
return sanitizeUrl(storedUrl);
|
|
1578
|
+
return this.lastAttemptedAuthUrl;
|
|
1252
1579
|
}
|
|
1580
|
+
/**
|
|
1581
|
+
* Removes OAuth state stored for this server.
|
|
1582
|
+
*
|
|
1583
|
+
* @returns The number of storage entries removed.
|
|
1584
|
+
*/
|
|
1253
1585
|
clearStorage() {
|
|
1586
|
+
this.lastAttemptedAuthUrl = null;
|
|
1587
|
+
this.authorizationPending = false;
|
|
1254
1588
|
const prefixPattern = `${this.storageKeyPrefix}_${this.serverUrlHash}_`;
|
|
1255
|
-
const statePattern = `${this.storageKeyPrefix}:state_`;
|
|
1256
1589
|
const keysToRemove = [];
|
|
1257
1590
|
let count = 0;
|
|
1258
|
-
for (
|
|
1259
|
-
const key = localStorage.key(i);
|
|
1260
|
-
if (!key) continue;
|
|
1591
|
+
for (const key of this.storage.keys()) {
|
|
1261
1592
|
if (key.startsWith(prefixPattern)) {
|
|
1262
1593
|
keysToRemove.push(key);
|
|
1263
|
-
} else if (key.startsWith(statePattern)) {
|
|
1264
|
-
try {
|
|
1265
|
-
const item = localStorage.getItem(key);
|
|
1266
|
-
if (item) {
|
|
1267
|
-
const state = JSON.parse(item);
|
|
1268
|
-
if (state.serverUrlHash === this.serverUrlHash) {
|
|
1269
|
-
keysToRemove.push(key);
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
} catch (e) {
|
|
1273
|
-
console.warn(
|
|
1274
|
-
`[${this.storageKeyPrefix}] Error parsing state key ${key} during clearStorage:`,
|
|
1275
|
-
e
|
|
1276
|
-
);
|
|
1277
|
-
}
|
|
1278
1594
|
}
|
|
1279
1595
|
}
|
|
1280
1596
|
const uniqueKeysToRemove = [...new Set(keysToRemove)];
|
|
1281
1597
|
uniqueKeysToRemove.forEach((key) => {
|
|
1282
|
-
|
|
1598
|
+
this.storage.remove(key);
|
|
1283
1599
|
count++;
|
|
1284
1600
|
});
|
|
1285
1601
|
return count;
|
|
@@ -1487,20 +1803,14 @@ function renderResult(title, message, error, returnUrl) {
|
|
|
1487
1803
|
}
|
|
1488
1804
|
document.body.appendChild(container);
|
|
1489
1805
|
}
|
|
1490
|
-
function findStoredState(state) {
|
|
1491
|
-
const
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
key = candidate;
|
|
1499
|
-
break;
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1503
|
-
const serialized = key ? localStorage.getItem(key) : null;
|
|
1806
|
+
async function findStoredState(state) {
|
|
1807
|
+
const store = new LocalStorageKVStore();
|
|
1808
|
+
const legacySuffix = `:state_${state}`;
|
|
1809
|
+
const scopedSuffix = `_state_${state}`;
|
|
1810
|
+
const key = (await store.keys()).find(
|
|
1811
|
+
(candidate) => candidate.endsWith(legacySuffix) || candidate.endsWith(scopedSuffix)
|
|
1812
|
+
);
|
|
1813
|
+
const serialized = key ? await store.get(key) : null;
|
|
1504
1814
|
if (!key || !serialized) {
|
|
1505
1815
|
throw new Error(`Invalid or expired OAuth state "${state}".`);
|
|
1506
1816
|
}
|
|
@@ -1508,10 +1818,10 @@ function findStoredState(state) {
|
|
|
1508
1818
|
try {
|
|
1509
1819
|
value = JSON.parse(serialized);
|
|
1510
1820
|
} catch {
|
|
1511
|
-
|
|
1821
|
+
await store.remove(key);
|
|
1512
1822
|
throw new Error("Failed to parse stored OAuth state.");
|
|
1513
1823
|
}
|
|
1514
|
-
return { key, value };
|
|
1824
|
+
return { key, value, store };
|
|
1515
1825
|
}
|
|
1516
1826
|
function redirectWithError(returnUrl, message) {
|
|
1517
1827
|
const url = new URL(returnUrl);
|
|
@@ -1570,17 +1880,19 @@ async function completeAuthorization() {
|
|
|
1570
1880
|
const callbackParams = new URLSearchParams(window.location.search);
|
|
1571
1881
|
const state = callbackParams.get("state");
|
|
1572
1882
|
let stateKey = null;
|
|
1883
|
+
let stateStore = null;
|
|
1573
1884
|
let storedState = null;
|
|
1574
1885
|
let provider = null;
|
|
1575
1886
|
try {
|
|
1576
1887
|
if (!state) {
|
|
1577
1888
|
throw new Error("OAuth callback is missing the state parameter.");
|
|
1578
1889
|
}
|
|
1579
|
-
const stored = findStoredState(state);
|
|
1890
|
+
const stored = await findStoredState(state);
|
|
1580
1891
|
stateKey = stored.key;
|
|
1892
|
+
stateStore = stored.store;
|
|
1581
1893
|
storedState = stored.value;
|
|
1582
1894
|
if (!storedState.expiry || storedState.expiry < Date.now()) {
|
|
1583
|
-
|
|
1895
|
+
await stateStore.remove(stateKey);
|
|
1584
1896
|
throw new Error(
|
|
1585
1897
|
"OAuth state has expired. Please start authentication again."
|
|
1586
1898
|
);
|
|
@@ -1595,7 +1907,7 @@ async function completeAuthorization() {
|
|
|
1595
1907
|
fetch: provider.getProxyFetch()
|
|
1596
1908
|
});
|
|
1597
1909
|
await transport.finishAuth(callbackParams);
|
|
1598
|
-
|
|
1910
|
+
await stateStore.remove(stateKey);
|
|
1599
1911
|
signalResult(true, void 0, storedState, {
|
|
1600
1912
|
state,
|
|
1601
1913
|
serverUrlHash: storedState.serverUrlHash
|
|
@@ -1603,8 +1915,12 @@ async function completeAuthorization() {
|
|
|
1603
1915
|
} catch (error) {
|
|
1604
1916
|
const message = error instanceof Error ? error.message : String(error);
|
|
1605
1917
|
console.error("[mcp-callback] OAuth callback failed:", error);
|
|
1606
|
-
if (stateKey)
|
|
1607
|
-
if (provider)
|
|
1918
|
+
if (stateKey && stateStore) await stateStore.remove(stateKey);
|
|
1919
|
+
if (provider) {
|
|
1920
|
+
await (stateStore ?? new LocalStorageKVStore()).remove(
|
|
1921
|
+
provider.getKey("last_auth_url")
|
|
1922
|
+
);
|
|
1923
|
+
}
|
|
1608
1924
|
signalResult(false, message, storedState, {
|
|
1609
1925
|
state,
|
|
1610
1926
|
serverUrlHash: storedState?.serverUrlHash
|
|
@@ -1646,11 +1962,12 @@ async function completeOAuthFlow(provider, serverUrl, options = {}) {
|
|
|
1646
1962
|
throw new Error(`Unexpected OAuth auth() result: ${result}`);
|
|
1647
1963
|
}
|
|
1648
1964
|
}
|
|
1649
|
-
if (typeof flowProvider.getAuthorizationCode === "function") {
|
|
1650
|
-
const
|
|
1965
|
+
if (typeof flowProvider.getAuthorizationResponse === "function" || typeof flowProvider.getAuthorizationCode === "function") {
|
|
1966
|
+
const response = typeof flowProvider.getAuthorizationResponse === "function" ? await flowProvider.getAuthorizationResponse() : { code: await flowProvider.getAuthorizationCode() };
|
|
1651
1967
|
await auth(provider, {
|
|
1652
1968
|
serverUrl,
|
|
1653
|
-
authorizationCode: code,
|
|
1969
|
+
authorizationCode: response.code,
|
|
1970
|
+
...response.iss !== void 0 ? { iss: response.iss } : {},
|
|
1654
1971
|
fetchFn
|
|
1655
1972
|
});
|
|
1656
1973
|
return;
|
|
@@ -1664,6 +1981,8 @@ async function waitForBrowserAuthComplete(provider, timeoutMs) {
|
|
|
1664
1981
|
);
|
|
1665
1982
|
}
|
|
1666
1983
|
if (provider.useRedirectFlow) {
|
|
1984
|
+
await new Promise(() => {
|
|
1985
|
+
});
|
|
1667
1986
|
return;
|
|
1668
1987
|
}
|
|
1669
1988
|
const tokensKey = provider.getKey?.("tokens");
|
|
@@ -1680,25 +1999,29 @@ async function waitForBrowserAuthComplete(provider, timeoutMs) {
|
|
|
1680
1999
|
} catch {
|
|
1681
2000
|
}
|
|
1682
2001
|
}
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
2002
|
+
try {
|
|
2003
|
+
const result = await runAuthPopup({
|
|
2004
|
+
popup: null,
|
|
2005
|
+
state,
|
|
2006
|
+
tokensKey,
|
|
2007
|
+
timeoutMs
|
|
2008
|
+
});
|
|
2009
|
+
switch (result.kind) {
|
|
2010
|
+
case "success":
|
|
2011
|
+
return;
|
|
2012
|
+
case "cancelled":
|
|
2013
|
+
throw new Error("OAuth authentication was cancelled.");
|
|
2014
|
+
case "timeout":
|
|
2015
|
+
throw new Error(
|
|
2016
|
+
`OAuth callback not received within ${timeoutMs}ms. Ensure /oauth/callback calls onMcpAuthorization().`
|
|
2017
|
+
);
|
|
2018
|
+
case "error":
|
|
2019
|
+
throw new Error(result.error);
|
|
2020
|
+
default:
|
|
2021
|
+
throw new Error("Unexpected OAuth popup result");
|
|
2022
|
+
}
|
|
2023
|
+
} finally {
|
|
2024
|
+
provider.markFlowComplete?.();
|
|
1702
2025
|
}
|
|
1703
2026
|
}
|
|
1704
2027
|
|
|
@@ -1769,6 +2092,11 @@ var BaseConnector = class {
|
|
|
1769
2092
|
notificationHandlers = [];
|
|
1770
2093
|
rootsCache = [];
|
|
1771
2094
|
activeProgressHandlers = /* @__PURE__ */ new Set();
|
|
2095
|
+
/**
|
|
2096
|
+
* Creates a connector with shared SDK and callback options.
|
|
2097
|
+
*
|
|
2098
|
+
* @param opts - Connector initialization options.
|
|
2099
|
+
*/
|
|
1772
2100
|
constructor(opts = {}) {
|
|
1773
2101
|
this.opts = opts;
|
|
1774
2102
|
if (opts.roots) {
|
|
@@ -1951,7 +2279,9 @@ var BaseConnector = class {
|
|
|
1951
2279
|
}
|
|
1952
2280
|
}
|
|
1953
2281
|
/**
|
|
1954
|
-
*
|
|
2282
|
+
* Returns the roots currently advertised to the server.
|
|
2283
|
+
*
|
|
2284
|
+
* @returns A copy of the configured roots.
|
|
1955
2285
|
*/
|
|
1956
2286
|
getRoots() {
|
|
1957
2287
|
return [...this.rootsCache];
|
|
@@ -2020,7 +2350,11 @@ var BaseConnector = class {
|
|
|
2020
2350
|
"setupElicitationHandler: Elicitation handler registered successfully"
|
|
2021
2351
|
);
|
|
2022
2352
|
}
|
|
2023
|
-
/**
|
|
2353
|
+
/**
|
|
2354
|
+
* Disconnects the SDK client and releases transport resources.
|
|
2355
|
+
*
|
|
2356
|
+
* @returns A promise that resolves after cleanup completes.
|
|
2357
|
+
*/
|
|
2024
2358
|
async disconnect() {
|
|
2025
2359
|
if (!this.connected) {
|
|
2026
2360
|
logger.debug("Not connected to MCP implementation");
|
|
@@ -2031,7 +2365,7 @@ var BaseConnector = class {
|
|
|
2031
2365
|
this.connected = false;
|
|
2032
2366
|
logger.debug("Disconnected from MCP implementation");
|
|
2033
2367
|
}
|
|
2034
|
-
/**
|
|
2368
|
+
/** Whether an SDK client currently exists for this connector. */
|
|
2035
2369
|
get isClientConnected() {
|
|
2036
2370
|
return this.client != null;
|
|
2037
2371
|
}
|
|
@@ -2041,6 +2375,10 @@ var BaseConnector = class {
|
|
|
2041
2375
|
* In the SDK, `Client.connect(transport)` automatically performs the
|
|
2042
2376
|
* protocol‑level `initialize` handshake, so we only need to cache the list of
|
|
2043
2377
|
* tools and expose some server info.
|
|
2378
|
+
*
|
|
2379
|
+
* @param defaultRequestOptions - Options used while fetching the initial tool list.
|
|
2380
|
+
* @returns The capabilities advertised by the server.
|
|
2381
|
+
* @throws When {@link BaseConnector.connect} has not completed.
|
|
2044
2382
|
*/
|
|
2045
2383
|
async initialize(defaultRequestOptions = this.opts.defaultRequestOptions ?? {}) {
|
|
2046
2384
|
if (!this.client) {
|
|
@@ -2078,18 +2416,22 @@ var BaseConnector = class {
|
|
|
2078
2416
|
logger.debug("Server info:", serverInfo);
|
|
2079
2417
|
return capabilities;
|
|
2080
2418
|
}
|
|
2081
|
-
/**
|
|
2419
|
+
/**
|
|
2420
|
+
* Returns the tool list cached during initialization.
|
|
2421
|
+
*
|
|
2422
|
+
* @throws When {@link BaseConnector.initialize} has not completed.
|
|
2423
|
+
*/
|
|
2082
2424
|
get tools() {
|
|
2083
2425
|
if (!this.toolsCache) {
|
|
2084
2426
|
throw new Error("MCP client is not initialized; call initialize() first");
|
|
2085
2427
|
}
|
|
2086
2428
|
return this.toolsCache;
|
|
2087
2429
|
}
|
|
2088
|
-
/**
|
|
2430
|
+
/** Capabilities cached during initialization, or an empty object. */
|
|
2089
2431
|
get serverCapabilities() {
|
|
2090
2432
|
return this.capabilitiesCache || {};
|
|
2091
2433
|
}
|
|
2092
|
-
/**
|
|
2434
|
+
/** Server identity cached during initialization, or `null`. */
|
|
2093
2435
|
get serverInfo() {
|
|
2094
2436
|
return this.serverInfoCache;
|
|
2095
2437
|
}
|
|
@@ -2110,7 +2452,15 @@ var BaseConnector = class {
|
|
|
2110
2452
|
get negotiatedProtocolVersion() {
|
|
2111
2453
|
return this.client?.getNegotiatedProtocolVersion?.();
|
|
2112
2454
|
}
|
|
2113
|
-
/**
|
|
2455
|
+
/**
|
|
2456
|
+
* Calls a tool on the connected server.
|
|
2457
|
+
*
|
|
2458
|
+
* @param name - Tool name.
|
|
2459
|
+
* @param args - Tool arguments.
|
|
2460
|
+
* @param options - Per-request timeout, cancellation, and progress options.
|
|
2461
|
+
* @returns The tool result returned by the server.
|
|
2462
|
+
* @throws When the connector is not connected or the tool call fails.
|
|
2463
|
+
*/
|
|
2114
2464
|
async callTool(name, args, options) {
|
|
2115
2465
|
if (!this.client) {
|
|
2116
2466
|
throw new Error("MCP client is not connected");
|
|
@@ -2235,7 +2585,13 @@ var BaseConnector = class {
|
|
|
2235
2585
|
);
|
|
2236
2586
|
return result;
|
|
2237
2587
|
}
|
|
2238
|
-
/**
|
|
2588
|
+
/**
|
|
2589
|
+
* Reads a resource by URI.
|
|
2590
|
+
*
|
|
2591
|
+
* @param uri - Resource URI to read.
|
|
2592
|
+
* @param options - Per-request options.
|
|
2593
|
+
* @returns The resource contents returned by the server.
|
|
2594
|
+
*/
|
|
2239
2595
|
async readResource(uri, options) {
|
|
2240
2596
|
if (!this.client) {
|
|
2241
2597
|
throw new Error("MCP client is not connected");
|
|
@@ -2270,6 +2626,11 @@ var BaseConnector = class {
|
|
|
2270
2626
|
logger.debug(`Unsubscribing from resource: ${uri}`);
|
|
2271
2627
|
return await this.client.unsubscribeResource({ uri }, options);
|
|
2272
2628
|
}
|
|
2629
|
+
/**
|
|
2630
|
+
* Lists prompts exposed by the server.
|
|
2631
|
+
*
|
|
2632
|
+
* @returns The prompt list, or an empty list when prompts are unsupported.
|
|
2633
|
+
*/
|
|
2273
2634
|
async listPrompts() {
|
|
2274
2635
|
if (!this.client) {
|
|
2275
2636
|
throw new Error("MCP client is not connected");
|
|
@@ -2290,6 +2651,13 @@ var BaseConnector = class {
|
|
|
2290
2651
|
throw err;
|
|
2291
2652
|
}
|
|
2292
2653
|
}
|
|
2654
|
+
/**
|
|
2655
|
+
* Gets a prompt with the supplied arguments.
|
|
2656
|
+
*
|
|
2657
|
+
* @param name - Prompt name.
|
|
2658
|
+
* @param args - Prompt arguments.
|
|
2659
|
+
* @returns The rendered prompt returned by the server.
|
|
2660
|
+
*/
|
|
2293
2661
|
async getPrompt(name, args) {
|
|
2294
2662
|
if (!this.client) {
|
|
2295
2663
|
throw new Error("MCP client is not connected");
|
|
@@ -2297,7 +2665,14 @@ var BaseConnector = class {
|
|
|
2297
2665
|
logger.debug(`Getting prompt ${name}`);
|
|
2298
2666
|
return await this.client.getPrompt({ name, arguments: args });
|
|
2299
2667
|
}
|
|
2300
|
-
/**
|
|
2668
|
+
/**
|
|
2669
|
+
* Sends a raw, potentially non-standard request through the SDK client.
|
|
2670
|
+
*
|
|
2671
|
+
* @param method - JSON-RPC method name.
|
|
2672
|
+
* @param params - Request parameters. Defaults to an empty object.
|
|
2673
|
+
* @param options - Per-request options.
|
|
2674
|
+
* @returns The unvalidated result returned by the server.
|
|
2675
|
+
*/
|
|
2301
2676
|
async request(method, params = null, options) {
|
|
2302
2677
|
if (!this.client) {
|
|
2303
2678
|
throw new Error("MCP client is not connected");
|
|
@@ -2396,6 +2771,12 @@ var HttpConnector = class extends BaseConnector {
|
|
|
2396
2771
|
reconnectionOptions;
|
|
2397
2772
|
transportType = null;
|
|
2398
2773
|
streamableTransport = null;
|
|
2774
|
+
/**
|
|
2775
|
+
* Creates an HTTP connector.
|
|
2776
|
+
*
|
|
2777
|
+
* @param baseUrl - MCP endpoint URL.
|
|
2778
|
+
* @param opts - Authentication, transport, SDK, and reconnection options.
|
|
2779
|
+
*/
|
|
2399
2780
|
constructor(baseUrl, opts = {}) {
|
|
2400
2781
|
super(opts);
|
|
2401
2782
|
const originalUrl = baseUrl.replace(/\/$/, "");
|
|
@@ -2513,7 +2894,12 @@ var HttpConnector = class extends BaseConnector {
|
|
|
2513
2894
|
}
|
|
2514
2895
|
return { fallbackReason, is401Error, httpStatusCode };
|
|
2515
2896
|
}
|
|
2516
|
-
/**
|
|
2897
|
+
/**
|
|
2898
|
+
* Establishes a streamable HTTP connection to the MCP server.
|
|
2899
|
+
*
|
|
2900
|
+
* @returns A promise that resolves after protocol negotiation completes.
|
|
2901
|
+
* @throws An error with `code: 401` when authentication is required.
|
|
2902
|
+
*/
|
|
2517
2903
|
async connect() {
|
|
2518
2904
|
if (this.connected) {
|
|
2519
2905
|
logger.debug("Already connected to MCP implementation");
|
|
@@ -2741,6 +3127,11 @@ var HttpConnector = class extends BaseConnector {
|
|
|
2741
3127
|
throw err;
|
|
2742
3128
|
}
|
|
2743
3129
|
}
|
|
3130
|
+
/**
|
|
3131
|
+
* Returns fields that identify the endpoint and negotiated transport.
|
|
3132
|
+
*
|
|
3133
|
+
* @returns HTTP connector identity metadata.
|
|
3134
|
+
*/
|
|
2744
3135
|
get publicIdentifier() {
|
|
2745
3136
|
return {
|
|
2746
3137
|
type: "http",
|
|
@@ -2749,7 +3140,11 @@ var HttpConnector = class extends BaseConnector {
|
|
|
2749
3140
|
protocolEra: this.protocolEra ?? "unknown"
|
|
2750
3141
|
};
|
|
2751
3142
|
}
|
|
2752
|
-
/**
|
|
3143
|
+
/**
|
|
3144
|
+
* Returns the active transport type.
|
|
3145
|
+
*
|
|
3146
|
+
* @returns `"streamable-http"` after connection, otherwise `null`.
|
|
3147
|
+
*/
|
|
2753
3148
|
getTransportType() {
|
|
2754
3149
|
return this.transportType;
|
|
2755
3150
|
}
|
|
@@ -3117,7 +3512,7 @@ var MCPConnection = class {
|
|
|
3117
3512
|
/**
|
|
3118
3513
|
* Normalized server metadata for this ready connection.
|
|
3119
3514
|
*
|
|
3120
|
-
* @throws
|
|
3515
|
+
* @throws When called before protocol negotiation completes.
|
|
3121
3516
|
*/
|
|
3122
3517
|
get info() {
|
|
3123
3518
|
const protocolEra = this.protocolEra;
|
|
@@ -3329,12 +3724,10 @@ function isOAuthClientProvider(provider) {
|
|
|
3329
3724
|
var BaseMCPClient = class {
|
|
3330
3725
|
/**
|
|
3331
3726
|
* Internal configuration object containing MCP server definitions.
|
|
3332
|
-
* @protected
|
|
3333
3727
|
*/
|
|
3334
3728
|
config = {};
|
|
3335
3729
|
/**
|
|
3336
3730
|
* Map of server names to their active sessions.
|
|
3337
|
-
* @protected
|
|
3338
3731
|
*/
|
|
3339
3732
|
sessions = {};
|
|
3340
3733
|
/**
|
|
@@ -3378,7 +3771,7 @@ var BaseMCPClient = class {
|
|
|
3378
3771
|
*
|
|
3379
3772
|
* @param _cfg - Configuration dictionary
|
|
3380
3773
|
* @returns Client instance
|
|
3381
|
-
* @throws
|
|
3774
|
+
* @throws If called on the base class instead of a concrete implementation
|
|
3382
3775
|
*
|
|
3383
3776
|
* @example
|
|
3384
3777
|
* ```typescript
|
|
@@ -3520,7 +3913,7 @@ var BaseMCPClient = class {
|
|
|
3520
3913
|
* @param serverName - The name of the server as defined in the client configuration
|
|
3521
3914
|
* @param autoInitialize - Whether to automatically initialize the session (default: true)
|
|
3522
3915
|
* @returns A promise that resolves to the created MCPSession instance
|
|
3523
|
-
* @throws
|
|
3916
|
+
* @throws If the server is not found in the configuration
|
|
3524
3917
|
*
|
|
3525
3918
|
* @example
|
|
3526
3919
|
* ```typescript
|
|
@@ -3690,7 +4083,7 @@ var BaseMCPClient = class {
|
|
|
3690
4083
|
*
|
|
3691
4084
|
* @param serverName - Name of the server
|
|
3692
4085
|
* @returns The session instance
|
|
3693
|
-
* @throws
|
|
4086
|
+
* @throws If the session is not found
|
|
3694
4087
|
*
|
|
3695
4088
|
* @example
|
|
3696
4089
|
* ```typescript
|
|
@@ -3859,16 +4252,28 @@ function trackBrowserClientInit(config) {
|
|
|
3859
4252
|
}
|
|
3860
4253
|
var BrowserMCPClient = class _BrowserMCPClient extends BaseMCPClient {
|
|
3861
4254
|
/**
|
|
3862
|
-
*
|
|
3863
|
-
*
|
|
4255
|
+
* Returns the installed `@mcp-use/client` package version.
|
|
4256
|
+
*
|
|
4257
|
+
* @returns The package version string.
|
|
3864
4258
|
*/
|
|
3865
4259
|
static getPackageVersion() {
|
|
3866
4260
|
return getPackageVersion();
|
|
3867
4261
|
}
|
|
4262
|
+
/**
|
|
4263
|
+
* Creates a browser MCP client.
|
|
4264
|
+
*
|
|
4265
|
+
* @param config - Client configuration containing an optional `mcpServers` map.
|
|
4266
|
+
*/
|
|
3868
4267
|
constructor(config) {
|
|
3869
4268
|
super(config);
|
|
3870
4269
|
trackBrowserClientInit(this.config);
|
|
3871
4270
|
}
|
|
4271
|
+
/**
|
|
4272
|
+
* Creates a browser client from an inline configuration object.
|
|
4273
|
+
*
|
|
4274
|
+
* @param cfg - Client configuration containing an optional `mcpServers` map.
|
|
4275
|
+
* @returns A browser client initialized with `cfg`.
|
|
4276
|
+
*/
|
|
3872
4277
|
static fromDict(cfg) {
|
|
3873
4278
|
return new _BrowserMCPClient(cfg);
|
|
3874
4279
|
}
|