@mcp-ts/sdk 1.3.9 → 1.4.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 +0 -1
- package/dist/adapters/langchain-adapter.js.map +1 -1
- package/dist/adapters/langchain-adapter.mjs.map +1 -1
- package/dist/client/index.d.mts +3 -189
- package/dist/client/index.d.ts +3 -189
- package/dist/client/index.js +218 -54
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +215 -55
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/react.d.mts +29 -40
- package/dist/client/react.d.ts +29 -40
- package/dist/client/react.js +492 -147
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +490 -149
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.d.mts +3 -2
- package/dist/client/vue.d.ts +3 -2
- package/dist/client/vue.js +239 -63
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs +236 -64
- package/dist/client/vue.mjs.map +1 -1
- package/dist/index-CQr9q0bF.d.mts +295 -0
- package/dist/index-nE_7Io0I.d.ts +295 -0
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +315 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +303 -65
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.js +93 -10
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +88 -10
- package/dist/server/index.mjs.map +1 -1
- package/package.json +13 -11
- package/src/adapters/langchain-adapter.ts +1 -1
- package/src/client/core/app-host.ts +252 -65
- package/src/client/core/constants.ts +30 -0
- package/src/client/index.ts +6 -1
- package/src/client/react/index.ts +6 -1
- package/src/client/react/use-app-host.ts +13 -19
- package/src/client/react/use-mcp-apps.tsx +297 -125
- package/src/client/react/use-mcp.ts +75 -36
- package/src/client/utils/app-host-utils.ts +62 -0
- package/src/client/vue/use-mcp.ts +23 -12
- package/src/server/mcp/oauth-client.ts +31 -8
- package/src/server/storage/crypto.ts +92 -0
- package/src/server/storage/supabase-backend.ts +7 -6
package/dist/index.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import { ListToolsResultSchema, CallToolResultSchema, ListPromptsResultSchema, G
|
|
|
7
7
|
import * as fs2 from 'fs';
|
|
8
8
|
import { promises } from 'fs';
|
|
9
9
|
import * as path from 'path';
|
|
10
|
+
import { createDecipheriv, randomBytes, createCipheriv } from 'crypto';
|
|
10
11
|
import { AppBridge, PostMessageTransport } from '@modelcontextprotocol/ext-apps/app-bridge';
|
|
11
12
|
|
|
12
13
|
var __defProp = Object.defineProperty;
|
|
@@ -710,6 +711,72 @@ var SqliteStorage = class {
|
|
|
710
711
|
}
|
|
711
712
|
}
|
|
712
713
|
};
|
|
714
|
+
var ALGORITHM = "aes-256-gcm";
|
|
715
|
+
var IV_LENGTH = 12;
|
|
716
|
+
var ENCRYPTION_PREFIX = "enc:1:";
|
|
717
|
+
var warningLogged = false;
|
|
718
|
+
function getKey() {
|
|
719
|
+
const keyString = process.env.STORAGE_ENCRYPTION_KEY;
|
|
720
|
+
if (!keyString) return null;
|
|
721
|
+
if (keyString.length === 64) {
|
|
722
|
+
return Buffer.from(keyString, "hex");
|
|
723
|
+
} else {
|
|
724
|
+
const keyBuffer = Buffer.alloc(32);
|
|
725
|
+
keyBuffer.write(keyString, 0, 32, "utf-8");
|
|
726
|
+
return keyBuffer;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
function encryptObject(data) {
|
|
730
|
+
if (data === void 0 || data === null) return data;
|
|
731
|
+
const key = getKey();
|
|
732
|
+
if (!key) {
|
|
733
|
+
if (!warningLogged) {
|
|
734
|
+
console.warn("[mcp-ts][Storage] WARNING: STORAGE_ENCRYPTION_KEY is not set. Saving sensitive data in plain-text.");
|
|
735
|
+
warningLogged = true;
|
|
736
|
+
}
|
|
737
|
+
return data;
|
|
738
|
+
}
|
|
739
|
+
try {
|
|
740
|
+
const text = JSON.stringify(data);
|
|
741
|
+
const iv = randomBytes(IV_LENGTH);
|
|
742
|
+
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
743
|
+
let encrypted = cipher.update(text, "utf-8", "hex");
|
|
744
|
+
encrypted += cipher.final("hex");
|
|
745
|
+
const authTag = cipher.getAuthTag().toString("hex");
|
|
746
|
+
return `${ENCRYPTION_PREFIX}${iv.toString("hex")}:${authTag}:${encrypted}`;
|
|
747
|
+
} catch (e) {
|
|
748
|
+
console.error("[mcp-ts][Storage] Encryption failed, falling back to plain-text.", e);
|
|
749
|
+
return data;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
function decryptObject(data) {
|
|
753
|
+
if (data === void 0 || data === null) return data;
|
|
754
|
+
if (typeof data !== "string" || !data.startsWith(ENCRYPTION_PREFIX)) {
|
|
755
|
+
return data;
|
|
756
|
+
}
|
|
757
|
+
const key = getKey();
|
|
758
|
+
if (!key) {
|
|
759
|
+
console.warn("[mcp-ts][Storage] WARNING: Found encrypted data but STORAGE_ENCRYPTION_KEY is missing. Returning raw encrypted string.");
|
|
760
|
+
return data;
|
|
761
|
+
}
|
|
762
|
+
try {
|
|
763
|
+
const parts = data.split(":");
|
|
764
|
+
if (parts.length !== 5) {
|
|
765
|
+
return data;
|
|
766
|
+
}
|
|
767
|
+
const iv = Buffer.from(parts[2], "hex");
|
|
768
|
+
const authTag = Buffer.from(parts[3], "hex");
|
|
769
|
+
const encryptedText = parts[4];
|
|
770
|
+
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
|
771
|
+
decipher.setAuthTag(authTag);
|
|
772
|
+
let decrypted = decipher.update(encryptedText, "hex", "utf-8");
|
|
773
|
+
decrypted += decipher.final("utf-8");
|
|
774
|
+
return JSON.parse(decrypted);
|
|
775
|
+
} catch (e) {
|
|
776
|
+
console.error("[mcp-ts][Storage] Decryption failed.", e);
|
|
777
|
+
return data;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
713
780
|
|
|
714
781
|
// src/server/storage/supabase-backend.ts
|
|
715
782
|
var SupabaseStorageBackend = class {
|
|
@@ -742,10 +809,10 @@ var SupabaseStorageBackend = class {
|
|
|
742
809
|
callbackUrl: row.callback_url,
|
|
743
810
|
createdAt: new Date(row.created_at).getTime(),
|
|
744
811
|
identity: row.identity,
|
|
745
|
-
headers: row.headers,
|
|
812
|
+
headers: decryptObject(row.headers),
|
|
746
813
|
active: row.active,
|
|
747
814
|
clientInformation: row.client_information,
|
|
748
|
-
tokens: row.tokens,
|
|
815
|
+
tokens: decryptObject(row.tokens),
|
|
749
816
|
codeVerifier: row.code_verifier,
|
|
750
817
|
clientId: row.client_id
|
|
751
818
|
};
|
|
@@ -766,10 +833,10 @@ var SupabaseStorageBackend = class {
|
|
|
766
833
|
callback_url: session.callbackUrl,
|
|
767
834
|
created_at: new Date(session.createdAt || Date.now()).toISOString(),
|
|
768
835
|
identity,
|
|
769
|
-
headers: session.headers,
|
|
836
|
+
headers: encryptObject(session.headers),
|
|
770
837
|
active: session.active ?? false,
|
|
771
838
|
client_information: session.clientInformation,
|
|
772
|
-
tokens: session.tokens,
|
|
839
|
+
tokens: encryptObject(session.tokens),
|
|
773
840
|
code_verifier: session.codeVerifier,
|
|
774
841
|
client_id: session.clientId,
|
|
775
842
|
expires_at: expiresAt
|
|
@@ -794,9 +861,9 @@ var SupabaseStorageBackend = class {
|
|
|
794
861
|
if ("transportType" in data) updateData.transport_type = data.transportType;
|
|
795
862
|
if ("callbackUrl" in data) updateData.callback_url = data.callbackUrl;
|
|
796
863
|
if ("active" in data) updateData.active = data.active;
|
|
797
|
-
if ("headers" in data) updateData.headers = data.headers;
|
|
864
|
+
if ("headers" in data) updateData.headers = encryptObject(data.headers);
|
|
798
865
|
if ("clientInformation" in data) updateData.client_information = data.clientInformation;
|
|
799
|
-
if ("tokens" in data) updateData.tokens = data.tokens;
|
|
866
|
+
if ("tokens" in data) updateData.tokens = encryptObject(data.tokens);
|
|
800
867
|
if ("codeVerifier" in data) updateData.code_verifier = data.codeVerifier;
|
|
801
868
|
if ("clientId" in data) updateData.client_id = data.clientId;
|
|
802
869
|
const { data: updatedRows, error } = await this.supabase.from("mcp_sessions").update(updateData).eq("identity", identity).eq("session_id", sessionId).select("id");
|
|
@@ -1658,13 +1725,24 @@ var MCPClient = class _MCPClient {
|
|
|
1658
1725
|
}
|
|
1659
1726
|
} catch (error) {
|
|
1660
1727
|
if (error instanceof UnauthorizedError$1 || error instanceof Error && error.message.toLowerCase().includes("unauthorized")) {
|
|
1661
|
-
this.emitStateChange("AUTHENTICATING");
|
|
1662
|
-
console.log(`[MCPClient] Saving session ${this.sessionId} with 10min TTL (OAuth pending)`);
|
|
1663
|
-
await this.saveSession(Math.floor(STATE_EXPIRATION_MS / 1e3), false);
|
|
1664
1728
|
let authUrl = "";
|
|
1665
1729
|
if (this.oauthProvider) {
|
|
1666
|
-
authUrl = this.oauthProvider.authUrl || "";
|
|
1730
|
+
authUrl = (this.oauthProvider.authUrl || "").trim();
|
|
1731
|
+
}
|
|
1732
|
+
if (!authUrl) {
|
|
1733
|
+
const detail = error instanceof Error && error.message.trim().length > 0 ? error.message.trim() : "Unauthorized";
|
|
1734
|
+
const message = detail.toLowerCase() === "unauthorized" ? "OAuth authorization URL not available" : `OAuth authorization URL not available: ${detail}`;
|
|
1735
|
+
this.emitError(message, "auth");
|
|
1736
|
+
this.emitStateChange("FAILED");
|
|
1737
|
+
try {
|
|
1738
|
+
await storage.removeSession(this.identity, this.sessionId);
|
|
1739
|
+
} catch {
|
|
1740
|
+
}
|
|
1741
|
+
throw new Error(message);
|
|
1667
1742
|
}
|
|
1743
|
+
this.emitStateChange("AUTHENTICATING");
|
|
1744
|
+
console.log(`[MCPClient] Saving session ${this.sessionId} with 10min TTL (OAuth pending)`);
|
|
1745
|
+
await this.saveSession(Math.floor(STATE_EXPIRATION_MS / 1e3), false);
|
|
1668
1746
|
if (this.serverId) {
|
|
1669
1747
|
this._onConnectionEvent.fire({
|
|
1670
1748
|
type: "auth_required",
|
|
@@ -3202,22 +3280,88 @@ var SSEClient = class {
|
|
|
3202
3280
|
}
|
|
3203
3281
|
}
|
|
3204
3282
|
};
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
"
|
|
3214
|
-
|
|
3215
|
-
"
|
|
3216
|
-
|
|
3217
|
-
"
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3283
|
+
|
|
3284
|
+
// src/client/core/constants.ts
|
|
3285
|
+
var SANDBOX_PROXY_READY_METHOD = "ui/notifications/sandbox-proxy-ready";
|
|
3286
|
+
var SANDBOX_RESOURCE_READY_METHOD = "ui/notifications/sandbox-resource-ready";
|
|
3287
|
+
var APP_HOST_DEFAULTS = {
|
|
3288
|
+
/** Default timeout for waiting for the sandbox proxy to be ready (ms). */
|
|
3289
|
+
SANDBOX_TIMEOUT_MS: 1e4,
|
|
3290
|
+
/** Default host info reported to guest apps. */
|
|
3291
|
+
HOST_INFO: { name: "mcp-ts-host", version: "1.0.0" },
|
|
3292
|
+
/** Supported MCP App URI schemes. */
|
|
3293
|
+
URI_SCHEMES: ["ui://", "mcp-app://"],
|
|
3294
|
+
/** Default theme for the host context. */
|
|
3295
|
+
THEME: "dark",
|
|
3296
|
+
/** Default platform for the host context. */
|
|
3297
|
+
PLATFORM: "web",
|
|
3298
|
+
/** Default max height for the iframe container (px). */
|
|
3299
|
+
MAX_HEIGHT: 6e3
|
|
3300
|
+
};
|
|
3301
|
+
|
|
3302
|
+
// src/client/utils/app-host-utils.ts
|
|
3303
|
+
var DEFAULT_SANDBOX_TIMEOUT_MS = APP_HOST_DEFAULTS.SANDBOX_TIMEOUT_MS;
|
|
3304
|
+
async function setupSandboxProxyIframe(iframe, sandboxProxyUrl) {
|
|
3305
|
+
iframe.style.width = "100%";
|
|
3306
|
+
iframe.style.height = "100%";
|
|
3307
|
+
iframe.style.border = "none";
|
|
3308
|
+
iframe.style.backgroundColor = "transparent";
|
|
3309
|
+
iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-downloads");
|
|
3310
|
+
const onReady = new Promise((resolve, reject) => {
|
|
3311
|
+
let settled = false;
|
|
3312
|
+
const cleanup = () => {
|
|
3313
|
+
window.removeEventListener("message", messageListener);
|
|
3314
|
+
iframe.removeEventListener("error", errorListener);
|
|
3315
|
+
};
|
|
3316
|
+
const timeoutId = setTimeout(() => {
|
|
3317
|
+
if (!settled) {
|
|
3318
|
+
settled = true;
|
|
3319
|
+
cleanup();
|
|
3320
|
+
reject(new Error("Timed out waiting for sandbox proxy iframe to be ready"));
|
|
3321
|
+
}
|
|
3322
|
+
}, DEFAULT_SANDBOX_TIMEOUT_MS);
|
|
3323
|
+
const messageListener = (event) => {
|
|
3324
|
+
if (event.source === iframe.contentWindow) {
|
|
3325
|
+
if (event.data?.method === SANDBOX_PROXY_READY_METHOD) {
|
|
3326
|
+
if (!settled) {
|
|
3327
|
+
settled = true;
|
|
3328
|
+
clearTimeout(timeoutId);
|
|
3329
|
+
cleanup();
|
|
3330
|
+
resolve();
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
};
|
|
3335
|
+
const errorListener = () => {
|
|
3336
|
+
if (!settled) {
|
|
3337
|
+
settled = true;
|
|
3338
|
+
clearTimeout(timeoutId);
|
|
3339
|
+
cleanup();
|
|
3340
|
+
reject(new Error("Failed to load sandbox proxy iframe"));
|
|
3341
|
+
}
|
|
3342
|
+
};
|
|
3343
|
+
window.addEventListener("message", messageListener);
|
|
3344
|
+
iframe.addEventListener("error", errorListener);
|
|
3345
|
+
});
|
|
3346
|
+
iframe.src = sandboxProxyUrl.href;
|
|
3347
|
+
return { onReady };
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
// src/client/core/app-host.ts
|
|
3351
|
+
var DEFAULT_MCP_APP_CSP = {
|
|
3352
|
+
"default-src": "'self'",
|
|
3353
|
+
"script-src": "'self' 'unsafe-inline' 'unsafe-eval' https: blob:",
|
|
3354
|
+
"style-src": "'self' 'unsafe-inline' https:",
|
|
3355
|
+
"connect-src": "'self' https: wss:",
|
|
3356
|
+
"img-src": "'self' data: https: blob:",
|
|
3357
|
+
"font-src": "'self' data: https:",
|
|
3358
|
+
"media-src": "'self' https: blob:",
|
|
3359
|
+
"frame-src": "'none'",
|
|
3360
|
+
"object-src": "'none'",
|
|
3361
|
+
"base-uri": "'self'"
|
|
3362
|
+
};
|
|
3363
|
+
var HOST_INFO = APP_HOST_DEFAULTS.HOST_INFO;
|
|
3364
|
+
var MCP_URI_SCHEMES = APP_HOST_DEFAULTS.URI_SCHEMES;
|
|
3221
3365
|
var AppHost = class {
|
|
3222
3366
|
constructor(client, iframe, options) {
|
|
3223
3367
|
this.client = client;
|
|
@@ -3226,10 +3370,12 @@ var AppHost = class {
|
|
|
3226
3370
|
__publicField(this, "sessionId");
|
|
3227
3371
|
__publicField(this, "resourceCache", /* @__PURE__ */ new Map());
|
|
3228
3372
|
__publicField(this, "debug");
|
|
3229
|
-
|
|
3373
|
+
__publicField(this, "sandboxConfig");
|
|
3374
|
+
__publicField(this, "options");
|
|
3230
3375
|
__publicField(this, "onAppMessage");
|
|
3231
|
-
this.
|
|
3232
|
-
this.
|
|
3376
|
+
this.options = options || {};
|
|
3377
|
+
this.debug = this.options.debug ?? false;
|
|
3378
|
+
this.sandboxConfig = this.options.sandbox;
|
|
3233
3379
|
this.bridge = this.initializeBridge();
|
|
3234
3380
|
}
|
|
3235
3381
|
// ============================================
|
|
@@ -3256,19 +3402,35 @@ var AppHost = class {
|
|
|
3256
3402
|
}
|
|
3257
3403
|
}
|
|
3258
3404
|
/**
|
|
3259
|
-
* Launch an MCP App from a URL
|
|
3405
|
+
* Launch an MCP App from a URL, MCP resource URI, or RAW HTML.
|
|
3260
3406
|
* Loads the HTML first, then establishes bridge connection.
|
|
3261
3407
|
*/
|
|
3262
|
-
async launch(
|
|
3408
|
+
async launch(source, sessionId) {
|
|
3263
3409
|
if (sessionId) this.sessionId = sessionId;
|
|
3264
3410
|
const initializedPromise = this.onAppReady();
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3411
|
+
let htmlToRender = source.html;
|
|
3412
|
+
if (!htmlToRender && source.uri) {
|
|
3413
|
+
if (this.isMcpUri(source.uri)) {
|
|
3414
|
+
htmlToRender = await this.readMcpAppHtml(source.uri);
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
if (!htmlToRender && source.uri && !this.isMcpUri(source.uri)) {
|
|
3418
|
+
this.iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-downloads");
|
|
3419
|
+
this.iframe.src = source.uri;
|
|
3420
|
+
await this.onIframeReady();
|
|
3421
|
+
await this.connectBridge();
|
|
3422
|
+
} else if (htmlToRender) {
|
|
3423
|
+
if (!this.sandboxConfig) {
|
|
3424
|
+
throw new Error("Sandbox configuration requires a proxy URL to render HTML safely.");
|
|
3425
|
+
}
|
|
3426
|
+
await this.launchSandboxedHtml(htmlToRender, this.sandboxConfig);
|
|
3427
|
+
await this.connectBridge();
|
|
3428
|
+
this.log("Sending HTML resource to sandbox proxy (MCP Apps notification)");
|
|
3429
|
+
await this.bridge.sendSandboxResourceReady({
|
|
3430
|
+
html: htmlToRender,
|
|
3431
|
+
csp: this.sandboxConfig.csp
|
|
3432
|
+
});
|
|
3269
3433
|
}
|
|
3270
|
-
await this.onIframeReady();
|
|
3271
|
-
await this.connectBridge();
|
|
3272
3434
|
this.log("Waiting for app initialization");
|
|
3273
3435
|
await Promise.race([
|
|
3274
3436
|
initializedPromise,
|
|
@@ -3279,6 +3441,19 @@ var AppHost = class {
|
|
|
3279
3441
|
]);
|
|
3280
3442
|
this.log("App launched and ready");
|
|
3281
3443
|
}
|
|
3444
|
+
// Set host context manually
|
|
3445
|
+
setHostContext(context) {
|
|
3446
|
+
this.options.hostContext = context;
|
|
3447
|
+
if (this.bridge) {
|
|
3448
|
+
this.bridge.setHostContext(context);
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
// Send streaming inputs manually
|
|
3452
|
+
sendToolInputPartial(params) {
|
|
3453
|
+
if (this.bridge) {
|
|
3454
|
+
this.bridge.sendToolInputPartial(params);
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3282
3457
|
/**
|
|
3283
3458
|
* Wait for app to signal initialization complete
|
|
3284
3459
|
*/
|
|
@@ -3329,14 +3504,17 @@ var AppHost = class {
|
|
|
3329
3504
|
this.log("Sending tool cancellation to app");
|
|
3330
3505
|
this.bridge.sendToolCancelled({ reason });
|
|
3331
3506
|
}
|
|
3507
|
+
/**
|
|
3508
|
+
* Tell the guest UI the resource is being torn down (unload / cleanup).
|
|
3509
|
+
* Forwards to {@link AppBridge.teardownResource} on `@modelcontextprotocol/ext-apps/app-bridge`.
|
|
3510
|
+
*/
|
|
3511
|
+
teardownResource(params = {}) {
|
|
3512
|
+
this.log("Sending resource teardown to app");
|
|
3513
|
+
this.bridge.teardownResource(params);
|
|
3514
|
+
}
|
|
3332
3515
|
// ============================================
|
|
3333
3516
|
// Private: Initialization
|
|
3334
3517
|
// ============================================
|
|
3335
|
-
configureSandbox() {
|
|
3336
|
-
if (this.iframe.sandbox.value !== SANDBOX_PERMISSIONS) {
|
|
3337
|
-
this.iframe.sandbox.value = SANDBOX_PERMISSIONS;
|
|
3338
|
-
}
|
|
3339
|
-
}
|
|
3340
3518
|
initializeBridge() {
|
|
3341
3519
|
const bridge = new AppBridge(
|
|
3342
3520
|
null,
|
|
@@ -3345,12 +3523,10 @@ var AppHost = class {
|
|
|
3345
3523
|
openLinks: {},
|
|
3346
3524
|
serverTools: {},
|
|
3347
3525
|
logging: {},
|
|
3348
|
-
// Declare support for model context updates
|
|
3349
3526
|
updateModelContext: { text: {} }
|
|
3350
3527
|
},
|
|
3351
3528
|
{
|
|
3352
|
-
|
|
3353
|
-
hostContext: {
|
|
3529
|
+
hostContext: this.options.hostContext || {
|
|
3354
3530
|
theme: "dark",
|
|
3355
3531
|
platform: "web",
|
|
3356
3532
|
containerDimensions: { maxHeight: 6e3 },
|
|
@@ -3359,19 +3535,56 @@ var AppHost = class {
|
|
|
3359
3535
|
}
|
|
3360
3536
|
}
|
|
3361
3537
|
);
|
|
3538
|
+
bridge.fallbackRequestHandler = this.options.onFallbackRequest;
|
|
3362
3539
|
bridge.oncalltool = (params) => this.handleToolCall(params);
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3540
|
+
if (this.options.onReadResource) {
|
|
3541
|
+
bridge.onreadresource = async (params) => {
|
|
3542
|
+
const resp = await this.options.onReadResource(params.uri);
|
|
3543
|
+
return {
|
|
3544
|
+
contents: resp.contents.map((c) => ({
|
|
3545
|
+
uri: params.uri,
|
|
3546
|
+
text: c.text,
|
|
3547
|
+
blob: c.blob
|
|
3548
|
+
}))
|
|
3549
|
+
};
|
|
3550
|
+
};
|
|
3551
|
+
}
|
|
3552
|
+
bridge.onopenlink = async (params, extra) => {
|
|
3553
|
+
if (this.options.onOpenLink) {
|
|
3554
|
+
return await this.options.onOpenLink(params, extra);
|
|
3555
|
+
}
|
|
3556
|
+
return this.handleOpenLink(params);
|
|
3557
|
+
};
|
|
3558
|
+
bridge.onmessage = async (params, extra) => {
|
|
3559
|
+
if (this.options.onMessage) {
|
|
3560
|
+
return await this.options.onMessage(params, extra);
|
|
3561
|
+
}
|
|
3562
|
+
return this.handleMessage(params);
|
|
3563
|
+
};
|
|
3564
|
+
bridge.onloggingmessage = (params) => {
|
|
3565
|
+
this.log(`App log [${params.level}]: ${params.data}`);
|
|
3566
|
+
if (this.options.onLoggingMessage) {
|
|
3567
|
+
this.options.onLoggingMessage(params);
|
|
3568
|
+
}
|
|
3569
|
+
};
|
|
3366
3570
|
bridge.onupdatemodelcontext = async () => ({});
|
|
3367
|
-
bridge.onsizechange = async (
|
|
3368
|
-
|
|
3369
|
-
if (
|
|
3571
|
+
bridge.onsizechange = async (params) => {
|
|
3572
|
+
const { width, height } = params;
|
|
3573
|
+
if (height !== void 0 && height > 0) {
|
|
3574
|
+
this.iframe.style.height = `${height}px`;
|
|
3575
|
+
}
|
|
3576
|
+
if (width !== void 0 && width > 0) this.iframe.style.minWidth = `min(${width}px, 100%)`;
|
|
3577
|
+
if (this.options.onSizeChanged) {
|
|
3578
|
+
this.options.onSizeChanged(params);
|
|
3579
|
+
}
|
|
3370
3580
|
return {};
|
|
3371
3581
|
};
|
|
3372
|
-
bridge.onrequestdisplaymode = async (params) =>
|
|
3373
|
-
|
|
3374
|
-
|
|
3582
|
+
bridge.onrequestdisplaymode = async (params, extra) => {
|
|
3583
|
+
if (this.options.onRequestDisplayMode) {
|
|
3584
|
+
return await this.options.onRequestDisplayMode(params, extra);
|
|
3585
|
+
}
|
|
3586
|
+
return { mode: params.mode === "fullscreen" ? "fullscreen" : "inline" };
|
|
3587
|
+
};
|
|
3375
3588
|
return bridge;
|
|
3376
3589
|
}
|
|
3377
3590
|
async connectBridge() {
|
|
@@ -3385,6 +3598,9 @@ var AppHost = class {
|
|
|
3385
3598
|
this.log("Bridge connected successfully");
|
|
3386
3599
|
} catch (error) {
|
|
3387
3600
|
this.log("Bridge connection failed", "error");
|
|
3601
|
+
if (this.options.onError) {
|
|
3602
|
+
this.options.onError(error instanceof Error ? error : new Error(String(error)));
|
|
3603
|
+
}
|
|
3388
3604
|
throw error;
|
|
3389
3605
|
}
|
|
3390
3606
|
}
|
|
@@ -3392,8 +3608,11 @@ var AppHost = class {
|
|
|
3392
3608
|
// Private: Bridge Event Handlers
|
|
3393
3609
|
// ============================================
|
|
3394
3610
|
async handleToolCall(params) {
|
|
3395
|
-
if (
|
|
3396
|
-
|
|
3611
|
+
if (this.options.onCallTool) {
|
|
3612
|
+
return await this.options.onCallTool(params);
|
|
3613
|
+
}
|
|
3614
|
+
if (!this.client || !this.client.isConnected()) {
|
|
3615
|
+
throw new Error("Client disconnected or not provided");
|
|
3397
3616
|
}
|
|
3398
3617
|
const sessionId = await this.getSessionId();
|
|
3399
3618
|
if (!sessionId) {
|
|
@@ -3417,13 +3636,19 @@ var AppHost = class {
|
|
|
3417
3636
|
// ============================================
|
|
3418
3637
|
// Private: Resource Loading
|
|
3419
3638
|
// ============================================
|
|
3420
|
-
async
|
|
3421
|
-
|
|
3422
|
-
|
|
3639
|
+
async launchSandboxedHtml(html, config) {
|
|
3640
|
+
const sandboxUrlString = config.url instanceof URL ? config.url.href : config.url;
|
|
3641
|
+
const url = new URL(sandboxUrlString, globalThis.location?.href);
|
|
3642
|
+
if (config.csp && Object.keys(config.csp).length > 0) {
|
|
3643
|
+
url.searchParams.set("csp", JSON.stringify(config.csp));
|
|
3423
3644
|
}
|
|
3645
|
+
const { onReady } = await setupSandboxProxyIframe(this.iframe, url);
|
|
3646
|
+
await onReady;
|
|
3647
|
+
}
|
|
3648
|
+
async readMcpAppHtml(uri) {
|
|
3424
3649
|
const sessionId = await this.getSessionId();
|
|
3425
|
-
if (!sessionId) {
|
|
3426
|
-
throw new Error("No active session");
|
|
3650
|
+
if (!sessionId && !this.options.onReadResource) {
|
|
3651
|
+
throw new Error("No active session.");
|
|
3427
3652
|
}
|
|
3428
3653
|
const response = await this.fetchResourceWithCache(sessionId, uri);
|
|
3429
3654
|
if (!response?.contents?.length) {
|
|
@@ -3434,10 +3659,18 @@ var AppHost = class {
|
|
|
3434
3659
|
if (!html) {
|
|
3435
3660
|
throw new Error(`Invalid content in resource: ${uri}`);
|
|
3436
3661
|
}
|
|
3437
|
-
|
|
3438
|
-
this.iframe.src = URL.createObjectURL(blob);
|
|
3662
|
+
return html;
|
|
3439
3663
|
}
|
|
3440
3664
|
async fetchResourceWithCache(sessionId, uri) {
|
|
3665
|
+
if (this.options.onReadResource) {
|
|
3666
|
+
return await this.options.onReadResource(uri);
|
|
3667
|
+
}
|
|
3668
|
+
if (!sessionId) {
|
|
3669
|
+
throw new Error("No active session");
|
|
3670
|
+
}
|
|
3671
|
+
if (!this.client) {
|
|
3672
|
+
throw new Error("No client to read resource from");
|
|
3673
|
+
}
|
|
3441
3674
|
if (this.hasClientCache()) {
|
|
3442
3675
|
return this.client.getOrFetchResource(sessionId, uri);
|
|
3443
3676
|
}
|
|
@@ -3450,8 +3683,11 @@ var AppHost = class {
|
|
|
3450
3683
|
}
|
|
3451
3684
|
async preloadResource(uri) {
|
|
3452
3685
|
try {
|
|
3686
|
+
if (this.options.onReadResource) {
|
|
3687
|
+
return await this.options.onReadResource(uri);
|
|
3688
|
+
}
|
|
3453
3689
|
const sessionId = await this.getSessionId();
|
|
3454
|
-
if (!sessionId) return null;
|
|
3690
|
+
if (!sessionId || !this.client) return null;
|
|
3455
3691
|
return await this.client.readResource(sessionId, uri);
|
|
3456
3692
|
} catch (error) {
|
|
3457
3693
|
this.log(`Preload failed for ${uri}`, "warn");
|
|
@@ -3463,6 +3699,7 @@ var AppHost = class {
|
|
|
3463
3699
|
// ============================================
|
|
3464
3700
|
async getSessionId() {
|
|
3465
3701
|
if (this.sessionId) return this.sessionId;
|
|
3702
|
+
if (!this.client) return void 0;
|
|
3466
3703
|
const result = await this.client.getSessions();
|
|
3467
3704
|
return result.sessions?.[0]?.sessionId;
|
|
3468
3705
|
}
|
|
@@ -3470,6 +3707,7 @@ var AppHost = class {
|
|
|
3470
3707
|
return MCP_URI_SCHEMES.some((scheme) => url.startsWith(scheme));
|
|
3471
3708
|
}
|
|
3472
3709
|
hasClientCache() {
|
|
3710
|
+
if (!this.client) return false;
|
|
3473
3711
|
return "getOrFetchResource" in this.client && typeof this.client.getOrFetchResource === "function";
|
|
3474
3712
|
}
|
|
3475
3713
|
extractUiResourceUri(tool) {
|
|
@@ -3533,6 +3771,6 @@ function findToolByName(connections, toolName) {
|
|
|
3533
3771
|
return void 0;
|
|
3534
3772
|
}
|
|
3535
3773
|
|
|
3536
|
-
export { AppHost, AuthenticationError, ConfigurationError, ConnectionError, DEFAULT_CLIENT_NAME, DEFAULT_CLIENT_URI, DEFAULT_HEARTBEAT_INTERVAL_MS, DEFAULT_LOGO_URI, DEFAULT_POLICY_URI, DisposableStore, Emitter, InvalidStateError, MCPClient, MCP_CLIENT_NAME, MCP_CLIENT_VERSION, McpError, MultiSessionClient, NotConnectedError, REDIS_KEY_PREFIX, RpcErrorCodes, SESSION_TTL_SECONDS, SOFTWARE_ID, SOFTWARE_VERSION, SSEClient, SSEConnectionManager, STATE_EXPIRATION_MS, SessionNotFoundError, SessionValidationError, StorageOAuthClientProvider, TOKEN_EXPIRY_BUFFER_MS, ToolExecutionError, UnauthorizedError, createNextMcpHandler, createSSEHandler, findToolByName, getToolUiResourceUri, isCallToolSuccess, isConnectAuthRequired, isConnectError, isConnectSuccess, isListToolsSuccess, sanitizeServerLabel, storage };
|
|
3774
|
+
export { APP_HOST_DEFAULTS, AppHost, AuthenticationError, ConfigurationError, ConnectionError, DEFAULT_CLIENT_NAME, DEFAULT_CLIENT_URI, DEFAULT_HEARTBEAT_INTERVAL_MS, DEFAULT_LOGO_URI, DEFAULT_MCP_APP_CSP, DEFAULT_POLICY_URI, DisposableStore, Emitter, InvalidStateError, MCPClient, MCP_CLIENT_NAME, MCP_CLIENT_VERSION, McpError, MultiSessionClient, NotConnectedError, REDIS_KEY_PREFIX, RpcErrorCodes, SANDBOX_PROXY_READY_METHOD, SANDBOX_RESOURCE_READY_METHOD, SESSION_TTL_SECONDS, SOFTWARE_ID, SOFTWARE_VERSION, SSEClient, SSEConnectionManager, STATE_EXPIRATION_MS, SessionNotFoundError, SessionValidationError, StorageOAuthClientProvider, TOKEN_EXPIRY_BUFFER_MS, ToolExecutionError, UnauthorizedError, createNextMcpHandler, createSSEHandler, findToolByName, getToolUiResourceUri, isCallToolSuccess, isConnectAuthRequired, isConnectError, isConnectSuccess, isListToolsSuccess, sanitizeServerLabel, storage };
|
|
3537
3775
|
//# sourceMappingURL=index.mjs.map
|
|
3538
3776
|
//# sourceMappingURL=index.mjs.map
|