@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.js
CHANGED
|
@@ -8,6 +8,7 @@ var auth_js = require('@modelcontextprotocol/sdk/client/auth.js');
|
|
|
8
8
|
var types_js = require('@modelcontextprotocol/sdk/types.js');
|
|
9
9
|
var fs2 = require('fs');
|
|
10
10
|
var path = require('path');
|
|
11
|
+
var crypto = require('crypto');
|
|
11
12
|
var appBridge = require('@modelcontextprotocol/ext-apps/app-bridge');
|
|
12
13
|
|
|
13
14
|
function _interopNamespace(e) {
|
|
@@ -771,6 +772,77 @@ var SqliteStorage = class {
|
|
|
771
772
|
|
|
772
773
|
// src/server/storage/supabase-backend.ts
|
|
773
774
|
init_cjs_shims();
|
|
775
|
+
|
|
776
|
+
// src/server/storage/crypto.ts
|
|
777
|
+
init_cjs_shims();
|
|
778
|
+
var ALGORITHM = "aes-256-gcm";
|
|
779
|
+
var IV_LENGTH = 12;
|
|
780
|
+
var ENCRYPTION_PREFIX = "enc:1:";
|
|
781
|
+
var warningLogged = false;
|
|
782
|
+
function getKey() {
|
|
783
|
+
const keyString = process.env.STORAGE_ENCRYPTION_KEY;
|
|
784
|
+
if (!keyString) return null;
|
|
785
|
+
if (keyString.length === 64) {
|
|
786
|
+
return Buffer.from(keyString, "hex");
|
|
787
|
+
} else {
|
|
788
|
+
const keyBuffer = Buffer.alloc(32);
|
|
789
|
+
keyBuffer.write(keyString, 0, 32, "utf-8");
|
|
790
|
+
return keyBuffer;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
function encryptObject(data) {
|
|
794
|
+
if (data === void 0 || data === null) return data;
|
|
795
|
+
const key = getKey();
|
|
796
|
+
if (!key) {
|
|
797
|
+
if (!warningLogged) {
|
|
798
|
+
console.warn("[mcp-ts][Storage] WARNING: STORAGE_ENCRYPTION_KEY is not set. Saving sensitive data in plain-text.");
|
|
799
|
+
warningLogged = true;
|
|
800
|
+
}
|
|
801
|
+
return data;
|
|
802
|
+
}
|
|
803
|
+
try {
|
|
804
|
+
const text = JSON.stringify(data);
|
|
805
|
+
const iv = crypto.randomBytes(IV_LENGTH);
|
|
806
|
+
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
|
|
807
|
+
let encrypted = cipher.update(text, "utf-8", "hex");
|
|
808
|
+
encrypted += cipher.final("hex");
|
|
809
|
+
const authTag = cipher.getAuthTag().toString("hex");
|
|
810
|
+
return `${ENCRYPTION_PREFIX}${iv.toString("hex")}:${authTag}:${encrypted}`;
|
|
811
|
+
} catch (e) {
|
|
812
|
+
console.error("[mcp-ts][Storage] Encryption failed, falling back to plain-text.", e);
|
|
813
|
+
return data;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
function decryptObject(data) {
|
|
817
|
+
if (data === void 0 || data === null) return data;
|
|
818
|
+
if (typeof data !== "string" || !data.startsWith(ENCRYPTION_PREFIX)) {
|
|
819
|
+
return data;
|
|
820
|
+
}
|
|
821
|
+
const key = getKey();
|
|
822
|
+
if (!key) {
|
|
823
|
+
console.warn("[mcp-ts][Storage] WARNING: Found encrypted data but STORAGE_ENCRYPTION_KEY is missing. Returning raw encrypted string.");
|
|
824
|
+
return data;
|
|
825
|
+
}
|
|
826
|
+
try {
|
|
827
|
+
const parts = data.split(":");
|
|
828
|
+
if (parts.length !== 5) {
|
|
829
|
+
return data;
|
|
830
|
+
}
|
|
831
|
+
const iv = Buffer.from(parts[2], "hex");
|
|
832
|
+
const authTag = Buffer.from(parts[3], "hex");
|
|
833
|
+
const encryptedText = parts[4];
|
|
834
|
+
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
|
|
835
|
+
decipher.setAuthTag(authTag);
|
|
836
|
+
let decrypted = decipher.update(encryptedText, "hex", "utf-8");
|
|
837
|
+
decrypted += decipher.final("utf-8");
|
|
838
|
+
return JSON.parse(decrypted);
|
|
839
|
+
} catch (e) {
|
|
840
|
+
console.error("[mcp-ts][Storage] Decryption failed.", e);
|
|
841
|
+
return data;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// src/server/storage/supabase-backend.ts
|
|
774
846
|
var SupabaseStorageBackend = class {
|
|
775
847
|
constructor(supabase) {
|
|
776
848
|
this.supabase = supabase;
|
|
@@ -801,10 +873,10 @@ var SupabaseStorageBackend = class {
|
|
|
801
873
|
callbackUrl: row.callback_url,
|
|
802
874
|
createdAt: new Date(row.created_at).getTime(),
|
|
803
875
|
identity: row.identity,
|
|
804
|
-
headers: row.headers,
|
|
876
|
+
headers: decryptObject(row.headers),
|
|
805
877
|
active: row.active,
|
|
806
878
|
clientInformation: row.client_information,
|
|
807
|
-
tokens: row.tokens,
|
|
879
|
+
tokens: decryptObject(row.tokens),
|
|
808
880
|
codeVerifier: row.code_verifier,
|
|
809
881
|
clientId: row.client_id
|
|
810
882
|
};
|
|
@@ -825,10 +897,10 @@ var SupabaseStorageBackend = class {
|
|
|
825
897
|
callback_url: session.callbackUrl,
|
|
826
898
|
created_at: new Date(session.createdAt || Date.now()).toISOString(),
|
|
827
899
|
identity,
|
|
828
|
-
headers: session.headers,
|
|
900
|
+
headers: encryptObject(session.headers),
|
|
829
901
|
active: session.active ?? false,
|
|
830
902
|
client_information: session.clientInformation,
|
|
831
|
-
tokens: session.tokens,
|
|
903
|
+
tokens: encryptObject(session.tokens),
|
|
832
904
|
code_verifier: session.codeVerifier,
|
|
833
905
|
client_id: session.clientId,
|
|
834
906
|
expires_at: expiresAt
|
|
@@ -853,9 +925,9 @@ var SupabaseStorageBackend = class {
|
|
|
853
925
|
if ("transportType" in data) updateData.transport_type = data.transportType;
|
|
854
926
|
if ("callbackUrl" in data) updateData.callback_url = data.callbackUrl;
|
|
855
927
|
if ("active" in data) updateData.active = data.active;
|
|
856
|
-
if ("headers" in data) updateData.headers = data.headers;
|
|
928
|
+
if ("headers" in data) updateData.headers = encryptObject(data.headers);
|
|
857
929
|
if ("clientInformation" in data) updateData.client_information = data.clientInformation;
|
|
858
|
-
if ("tokens" in data) updateData.tokens = data.tokens;
|
|
930
|
+
if ("tokens" in data) updateData.tokens = encryptObject(data.tokens);
|
|
859
931
|
if ("codeVerifier" in data) updateData.code_verifier = data.codeVerifier;
|
|
860
932
|
if ("clientId" in data) updateData.client_id = data.clientId;
|
|
861
933
|
const { data: updatedRows, error } = await this.supabase.from("mcp_sessions").update(updateData).eq("identity", identity).eq("session_id", sessionId).select("id");
|
|
@@ -1722,13 +1794,24 @@ var MCPClient = class _MCPClient {
|
|
|
1722
1794
|
}
|
|
1723
1795
|
} catch (error) {
|
|
1724
1796
|
if (error instanceof auth_js.UnauthorizedError || error instanceof Error && error.message.toLowerCase().includes("unauthorized")) {
|
|
1725
|
-
this.emitStateChange("AUTHENTICATING");
|
|
1726
|
-
console.log(`[MCPClient] Saving session ${this.sessionId} with 10min TTL (OAuth pending)`);
|
|
1727
|
-
await this.saveSession(Math.floor(STATE_EXPIRATION_MS / 1e3), false);
|
|
1728
1797
|
let authUrl = "";
|
|
1729
1798
|
if (this.oauthProvider) {
|
|
1730
|
-
authUrl = this.oauthProvider.authUrl || "";
|
|
1799
|
+
authUrl = (this.oauthProvider.authUrl || "").trim();
|
|
1731
1800
|
}
|
|
1801
|
+
if (!authUrl) {
|
|
1802
|
+
const detail = error instanceof Error && error.message.trim().length > 0 ? error.message.trim() : "Unauthorized";
|
|
1803
|
+
const message = detail.toLowerCase() === "unauthorized" ? "OAuth authorization URL not available" : `OAuth authorization URL not available: ${detail}`;
|
|
1804
|
+
this.emitError(message, "auth");
|
|
1805
|
+
this.emitStateChange("FAILED");
|
|
1806
|
+
try {
|
|
1807
|
+
await storage.removeSession(this.identity, this.sessionId);
|
|
1808
|
+
} catch {
|
|
1809
|
+
}
|
|
1810
|
+
throw new Error(message);
|
|
1811
|
+
}
|
|
1812
|
+
this.emitStateChange("AUTHENTICATING");
|
|
1813
|
+
console.log(`[MCPClient] Saving session ${this.sessionId} with 10min TTL (OAuth pending)`);
|
|
1814
|
+
await this.saveSession(Math.floor(STATE_EXPIRATION_MS / 1e3), false);
|
|
1732
1815
|
if (this.serverId) {
|
|
1733
1816
|
this._onConnectionEvent.fire({
|
|
1734
1817
|
type: "auth_required",
|
|
@@ -3281,22 +3364,92 @@ var SSEClient = class {
|
|
|
3281
3364
|
|
|
3282
3365
|
// src/client/core/app-host.ts
|
|
3283
3366
|
init_cjs_shims();
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
"
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3367
|
+
|
|
3368
|
+
// src/client/utils/app-host-utils.ts
|
|
3369
|
+
init_cjs_shims();
|
|
3370
|
+
|
|
3371
|
+
// src/client/core/constants.ts
|
|
3372
|
+
init_cjs_shims();
|
|
3373
|
+
var SANDBOX_PROXY_READY_METHOD = "ui/notifications/sandbox-proxy-ready";
|
|
3374
|
+
var SANDBOX_RESOURCE_READY_METHOD = "ui/notifications/sandbox-resource-ready";
|
|
3375
|
+
var APP_HOST_DEFAULTS = {
|
|
3376
|
+
/** Default timeout for waiting for the sandbox proxy to be ready (ms). */
|
|
3377
|
+
SANDBOX_TIMEOUT_MS: 1e4,
|
|
3378
|
+
/** Default host info reported to guest apps. */
|
|
3379
|
+
HOST_INFO: { name: "mcp-ts-host", version: "1.0.0" },
|
|
3380
|
+
/** Supported MCP App URI schemes. */
|
|
3381
|
+
URI_SCHEMES: ["ui://", "mcp-app://"],
|
|
3382
|
+
/** Default theme for the host context. */
|
|
3383
|
+
THEME: "dark",
|
|
3384
|
+
/** Default platform for the host context. */
|
|
3385
|
+
PLATFORM: "web",
|
|
3386
|
+
/** Default max height for the iframe container (px). */
|
|
3387
|
+
MAX_HEIGHT: 6e3
|
|
3388
|
+
};
|
|
3389
|
+
|
|
3390
|
+
// src/client/utils/app-host-utils.ts
|
|
3391
|
+
var DEFAULT_SANDBOX_TIMEOUT_MS = APP_HOST_DEFAULTS.SANDBOX_TIMEOUT_MS;
|
|
3392
|
+
async function setupSandboxProxyIframe(iframe, sandboxProxyUrl) {
|
|
3393
|
+
iframe.style.width = "100%";
|
|
3394
|
+
iframe.style.height = "100%";
|
|
3395
|
+
iframe.style.border = "none";
|
|
3396
|
+
iframe.style.backgroundColor = "transparent";
|
|
3397
|
+
iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-downloads");
|
|
3398
|
+
const onReady = new Promise((resolve, reject) => {
|
|
3399
|
+
let settled = false;
|
|
3400
|
+
const cleanup = () => {
|
|
3401
|
+
window.removeEventListener("message", messageListener);
|
|
3402
|
+
iframe.removeEventListener("error", errorListener);
|
|
3403
|
+
};
|
|
3404
|
+
const timeoutId = setTimeout(() => {
|
|
3405
|
+
if (!settled) {
|
|
3406
|
+
settled = true;
|
|
3407
|
+
cleanup();
|
|
3408
|
+
reject(new Error("Timed out waiting for sandbox proxy iframe to be ready"));
|
|
3409
|
+
}
|
|
3410
|
+
}, DEFAULT_SANDBOX_TIMEOUT_MS);
|
|
3411
|
+
const messageListener = (event) => {
|
|
3412
|
+
if (event.source === iframe.contentWindow) {
|
|
3413
|
+
if (event.data?.method === SANDBOX_PROXY_READY_METHOD) {
|
|
3414
|
+
if (!settled) {
|
|
3415
|
+
settled = true;
|
|
3416
|
+
clearTimeout(timeoutId);
|
|
3417
|
+
cleanup();
|
|
3418
|
+
resolve();
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
};
|
|
3423
|
+
const errorListener = () => {
|
|
3424
|
+
if (!settled) {
|
|
3425
|
+
settled = true;
|
|
3426
|
+
clearTimeout(timeoutId);
|
|
3427
|
+
cleanup();
|
|
3428
|
+
reject(new Error("Failed to load sandbox proxy iframe"));
|
|
3429
|
+
}
|
|
3430
|
+
};
|
|
3431
|
+
window.addEventListener("message", messageListener);
|
|
3432
|
+
iframe.addEventListener("error", errorListener);
|
|
3433
|
+
});
|
|
3434
|
+
iframe.src = sandboxProxyUrl.href;
|
|
3435
|
+
return { onReady };
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
// src/client/core/app-host.ts
|
|
3439
|
+
var DEFAULT_MCP_APP_CSP = {
|
|
3440
|
+
"default-src": "'self'",
|
|
3441
|
+
"script-src": "'self' 'unsafe-inline' 'unsafe-eval' https: blob:",
|
|
3442
|
+
"style-src": "'self' 'unsafe-inline' https:",
|
|
3443
|
+
"connect-src": "'self' https: wss:",
|
|
3444
|
+
"img-src": "'self' data: https: blob:",
|
|
3445
|
+
"font-src": "'self' data: https:",
|
|
3446
|
+
"media-src": "'self' https: blob:",
|
|
3447
|
+
"frame-src": "'none'",
|
|
3448
|
+
"object-src": "'none'",
|
|
3449
|
+
"base-uri": "'self'"
|
|
3450
|
+
};
|
|
3451
|
+
var HOST_INFO = APP_HOST_DEFAULTS.HOST_INFO;
|
|
3452
|
+
var MCP_URI_SCHEMES = APP_HOST_DEFAULTS.URI_SCHEMES;
|
|
3300
3453
|
var AppHost = class {
|
|
3301
3454
|
constructor(client, iframe, options) {
|
|
3302
3455
|
this.client = client;
|
|
@@ -3305,10 +3458,12 @@ var AppHost = class {
|
|
|
3305
3458
|
__publicField(this, "sessionId");
|
|
3306
3459
|
__publicField(this, "resourceCache", /* @__PURE__ */ new Map());
|
|
3307
3460
|
__publicField(this, "debug");
|
|
3308
|
-
|
|
3461
|
+
__publicField(this, "sandboxConfig");
|
|
3462
|
+
__publicField(this, "options");
|
|
3309
3463
|
__publicField(this, "onAppMessage");
|
|
3310
|
-
this.
|
|
3311
|
-
this.
|
|
3464
|
+
this.options = options || {};
|
|
3465
|
+
this.debug = this.options.debug ?? false;
|
|
3466
|
+
this.sandboxConfig = this.options.sandbox;
|
|
3312
3467
|
this.bridge = this.initializeBridge();
|
|
3313
3468
|
}
|
|
3314
3469
|
// ============================================
|
|
@@ -3335,19 +3490,35 @@ var AppHost = class {
|
|
|
3335
3490
|
}
|
|
3336
3491
|
}
|
|
3337
3492
|
/**
|
|
3338
|
-
* Launch an MCP App from a URL
|
|
3493
|
+
* Launch an MCP App from a URL, MCP resource URI, or RAW HTML.
|
|
3339
3494
|
* Loads the HTML first, then establishes bridge connection.
|
|
3340
3495
|
*/
|
|
3341
|
-
async launch(
|
|
3496
|
+
async launch(source, sessionId) {
|
|
3342
3497
|
if (sessionId) this.sessionId = sessionId;
|
|
3343
3498
|
const initializedPromise = this.onAppReady();
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3499
|
+
let htmlToRender = source.html;
|
|
3500
|
+
if (!htmlToRender && source.uri) {
|
|
3501
|
+
if (this.isMcpUri(source.uri)) {
|
|
3502
|
+
htmlToRender = await this.readMcpAppHtml(source.uri);
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
3505
|
+
if (!htmlToRender && source.uri && !this.isMcpUri(source.uri)) {
|
|
3506
|
+
this.iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-downloads");
|
|
3507
|
+
this.iframe.src = source.uri;
|
|
3508
|
+
await this.onIframeReady();
|
|
3509
|
+
await this.connectBridge();
|
|
3510
|
+
} else if (htmlToRender) {
|
|
3511
|
+
if (!this.sandboxConfig) {
|
|
3512
|
+
throw new Error("Sandbox configuration requires a proxy URL to render HTML safely.");
|
|
3513
|
+
}
|
|
3514
|
+
await this.launchSandboxedHtml(htmlToRender, this.sandboxConfig);
|
|
3515
|
+
await this.connectBridge();
|
|
3516
|
+
this.log("Sending HTML resource to sandbox proxy (MCP Apps notification)");
|
|
3517
|
+
await this.bridge.sendSandboxResourceReady({
|
|
3518
|
+
html: htmlToRender,
|
|
3519
|
+
csp: this.sandboxConfig.csp
|
|
3520
|
+
});
|
|
3348
3521
|
}
|
|
3349
|
-
await this.onIframeReady();
|
|
3350
|
-
await this.connectBridge();
|
|
3351
3522
|
this.log("Waiting for app initialization");
|
|
3352
3523
|
await Promise.race([
|
|
3353
3524
|
initializedPromise,
|
|
@@ -3358,6 +3529,19 @@ var AppHost = class {
|
|
|
3358
3529
|
]);
|
|
3359
3530
|
this.log("App launched and ready");
|
|
3360
3531
|
}
|
|
3532
|
+
// Set host context manually
|
|
3533
|
+
setHostContext(context) {
|
|
3534
|
+
this.options.hostContext = context;
|
|
3535
|
+
if (this.bridge) {
|
|
3536
|
+
this.bridge.setHostContext(context);
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
// Send streaming inputs manually
|
|
3540
|
+
sendToolInputPartial(params) {
|
|
3541
|
+
if (this.bridge) {
|
|
3542
|
+
this.bridge.sendToolInputPartial(params);
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3361
3545
|
/**
|
|
3362
3546
|
* Wait for app to signal initialization complete
|
|
3363
3547
|
*/
|
|
@@ -3408,14 +3592,17 @@ var AppHost = class {
|
|
|
3408
3592
|
this.log("Sending tool cancellation to app");
|
|
3409
3593
|
this.bridge.sendToolCancelled({ reason });
|
|
3410
3594
|
}
|
|
3595
|
+
/**
|
|
3596
|
+
* Tell the guest UI the resource is being torn down (unload / cleanup).
|
|
3597
|
+
* Forwards to {@link AppBridge.teardownResource} on `@modelcontextprotocol/ext-apps/app-bridge`.
|
|
3598
|
+
*/
|
|
3599
|
+
teardownResource(params = {}) {
|
|
3600
|
+
this.log("Sending resource teardown to app");
|
|
3601
|
+
this.bridge.teardownResource(params);
|
|
3602
|
+
}
|
|
3411
3603
|
// ============================================
|
|
3412
3604
|
// Private: Initialization
|
|
3413
3605
|
// ============================================
|
|
3414
|
-
configureSandbox() {
|
|
3415
|
-
if (this.iframe.sandbox.value !== SANDBOX_PERMISSIONS) {
|
|
3416
|
-
this.iframe.sandbox.value = SANDBOX_PERMISSIONS;
|
|
3417
|
-
}
|
|
3418
|
-
}
|
|
3419
3606
|
initializeBridge() {
|
|
3420
3607
|
const bridge = new appBridge.AppBridge(
|
|
3421
3608
|
null,
|
|
@@ -3424,12 +3611,10 @@ var AppHost = class {
|
|
|
3424
3611
|
openLinks: {},
|
|
3425
3612
|
serverTools: {},
|
|
3426
3613
|
logging: {},
|
|
3427
|
-
// Declare support for model context updates
|
|
3428
3614
|
updateModelContext: { text: {} }
|
|
3429
3615
|
},
|
|
3430
3616
|
{
|
|
3431
|
-
|
|
3432
|
-
hostContext: {
|
|
3617
|
+
hostContext: this.options.hostContext || {
|
|
3433
3618
|
theme: "dark",
|
|
3434
3619
|
platform: "web",
|
|
3435
3620
|
containerDimensions: { maxHeight: 6e3 },
|
|
@@ -3438,19 +3623,56 @@ var AppHost = class {
|
|
|
3438
3623
|
}
|
|
3439
3624
|
}
|
|
3440
3625
|
);
|
|
3626
|
+
bridge.fallbackRequestHandler = this.options.onFallbackRequest;
|
|
3441
3627
|
bridge.oncalltool = (params) => this.handleToolCall(params);
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3628
|
+
if (this.options.onReadResource) {
|
|
3629
|
+
bridge.onreadresource = async (params) => {
|
|
3630
|
+
const resp = await this.options.onReadResource(params.uri);
|
|
3631
|
+
return {
|
|
3632
|
+
contents: resp.contents.map((c) => ({
|
|
3633
|
+
uri: params.uri,
|
|
3634
|
+
text: c.text,
|
|
3635
|
+
blob: c.blob
|
|
3636
|
+
}))
|
|
3637
|
+
};
|
|
3638
|
+
};
|
|
3639
|
+
}
|
|
3640
|
+
bridge.onopenlink = async (params, extra) => {
|
|
3641
|
+
if (this.options.onOpenLink) {
|
|
3642
|
+
return await this.options.onOpenLink(params, extra);
|
|
3643
|
+
}
|
|
3644
|
+
return this.handleOpenLink(params);
|
|
3645
|
+
};
|
|
3646
|
+
bridge.onmessage = async (params, extra) => {
|
|
3647
|
+
if (this.options.onMessage) {
|
|
3648
|
+
return await this.options.onMessage(params, extra);
|
|
3649
|
+
}
|
|
3650
|
+
return this.handleMessage(params);
|
|
3651
|
+
};
|
|
3652
|
+
bridge.onloggingmessage = (params) => {
|
|
3653
|
+
this.log(`App log [${params.level}]: ${params.data}`);
|
|
3654
|
+
if (this.options.onLoggingMessage) {
|
|
3655
|
+
this.options.onLoggingMessage(params);
|
|
3656
|
+
}
|
|
3657
|
+
};
|
|
3445
3658
|
bridge.onupdatemodelcontext = async () => ({});
|
|
3446
|
-
bridge.onsizechange = async (
|
|
3447
|
-
|
|
3448
|
-
if (
|
|
3659
|
+
bridge.onsizechange = async (params) => {
|
|
3660
|
+
const { width, height } = params;
|
|
3661
|
+
if (height !== void 0 && height > 0) {
|
|
3662
|
+
this.iframe.style.height = `${height}px`;
|
|
3663
|
+
}
|
|
3664
|
+
if (width !== void 0 && width > 0) this.iframe.style.minWidth = `min(${width}px, 100%)`;
|
|
3665
|
+
if (this.options.onSizeChanged) {
|
|
3666
|
+
this.options.onSizeChanged(params);
|
|
3667
|
+
}
|
|
3449
3668
|
return {};
|
|
3450
3669
|
};
|
|
3451
|
-
bridge.onrequestdisplaymode = async (params) =>
|
|
3452
|
-
|
|
3453
|
-
|
|
3670
|
+
bridge.onrequestdisplaymode = async (params, extra) => {
|
|
3671
|
+
if (this.options.onRequestDisplayMode) {
|
|
3672
|
+
return await this.options.onRequestDisplayMode(params, extra);
|
|
3673
|
+
}
|
|
3674
|
+
return { mode: params.mode === "fullscreen" ? "fullscreen" : "inline" };
|
|
3675
|
+
};
|
|
3454
3676
|
return bridge;
|
|
3455
3677
|
}
|
|
3456
3678
|
async connectBridge() {
|
|
@@ -3464,6 +3686,9 @@ var AppHost = class {
|
|
|
3464
3686
|
this.log("Bridge connected successfully");
|
|
3465
3687
|
} catch (error) {
|
|
3466
3688
|
this.log("Bridge connection failed", "error");
|
|
3689
|
+
if (this.options.onError) {
|
|
3690
|
+
this.options.onError(error instanceof Error ? error : new Error(String(error)));
|
|
3691
|
+
}
|
|
3467
3692
|
throw error;
|
|
3468
3693
|
}
|
|
3469
3694
|
}
|
|
@@ -3471,8 +3696,11 @@ var AppHost = class {
|
|
|
3471
3696
|
// Private: Bridge Event Handlers
|
|
3472
3697
|
// ============================================
|
|
3473
3698
|
async handleToolCall(params) {
|
|
3474
|
-
if (
|
|
3475
|
-
|
|
3699
|
+
if (this.options.onCallTool) {
|
|
3700
|
+
return await this.options.onCallTool(params);
|
|
3701
|
+
}
|
|
3702
|
+
if (!this.client || !this.client.isConnected()) {
|
|
3703
|
+
throw new Error("Client disconnected or not provided");
|
|
3476
3704
|
}
|
|
3477
3705
|
const sessionId = await this.getSessionId();
|
|
3478
3706
|
if (!sessionId) {
|
|
@@ -3496,13 +3724,19 @@ var AppHost = class {
|
|
|
3496
3724
|
// ============================================
|
|
3497
3725
|
// Private: Resource Loading
|
|
3498
3726
|
// ============================================
|
|
3499
|
-
async
|
|
3500
|
-
|
|
3501
|
-
|
|
3727
|
+
async launchSandboxedHtml(html, config) {
|
|
3728
|
+
const sandboxUrlString = config.url instanceof URL ? config.url.href : config.url;
|
|
3729
|
+
const url = new URL(sandboxUrlString, globalThis.location?.href);
|
|
3730
|
+
if (config.csp && Object.keys(config.csp).length > 0) {
|
|
3731
|
+
url.searchParams.set("csp", JSON.stringify(config.csp));
|
|
3502
3732
|
}
|
|
3733
|
+
const { onReady } = await setupSandboxProxyIframe(this.iframe, url);
|
|
3734
|
+
await onReady;
|
|
3735
|
+
}
|
|
3736
|
+
async readMcpAppHtml(uri) {
|
|
3503
3737
|
const sessionId = await this.getSessionId();
|
|
3504
|
-
if (!sessionId) {
|
|
3505
|
-
throw new Error("No active session");
|
|
3738
|
+
if (!sessionId && !this.options.onReadResource) {
|
|
3739
|
+
throw new Error("No active session.");
|
|
3506
3740
|
}
|
|
3507
3741
|
const response = await this.fetchResourceWithCache(sessionId, uri);
|
|
3508
3742
|
if (!response?.contents?.length) {
|
|
@@ -3513,10 +3747,18 @@ var AppHost = class {
|
|
|
3513
3747
|
if (!html) {
|
|
3514
3748
|
throw new Error(`Invalid content in resource: ${uri}`);
|
|
3515
3749
|
}
|
|
3516
|
-
|
|
3517
|
-
this.iframe.src = URL.createObjectURL(blob);
|
|
3750
|
+
return html;
|
|
3518
3751
|
}
|
|
3519
3752
|
async fetchResourceWithCache(sessionId, uri) {
|
|
3753
|
+
if (this.options.onReadResource) {
|
|
3754
|
+
return await this.options.onReadResource(uri);
|
|
3755
|
+
}
|
|
3756
|
+
if (!sessionId) {
|
|
3757
|
+
throw new Error("No active session");
|
|
3758
|
+
}
|
|
3759
|
+
if (!this.client) {
|
|
3760
|
+
throw new Error("No client to read resource from");
|
|
3761
|
+
}
|
|
3520
3762
|
if (this.hasClientCache()) {
|
|
3521
3763
|
return this.client.getOrFetchResource(sessionId, uri);
|
|
3522
3764
|
}
|
|
@@ -3529,8 +3771,11 @@ var AppHost = class {
|
|
|
3529
3771
|
}
|
|
3530
3772
|
async preloadResource(uri) {
|
|
3531
3773
|
try {
|
|
3774
|
+
if (this.options.onReadResource) {
|
|
3775
|
+
return await this.options.onReadResource(uri);
|
|
3776
|
+
}
|
|
3532
3777
|
const sessionId = await this.getSessionId();
|
|
3533
|
-
if (!sessionId) return null;
|
|
3778
|
+
if (!sessionId || !this.client) return null;
|
|
3534
3779
|
return await this.client.readResource(sessionId, uri);
|
|
3535
3780
|
} catch (error) {
|
|
3536
3781
|
this.log(`Preload failed for ${uri}`, "warn");
|
|
@@ -3542,6 +3787,7 @@ var AppHost = class {
|
|
|
3542
3787
|
// ============================================
|
|
3543
3788
|
async getSessionId() {
|
|
3544
3789
|
if (this.sessionId) return this.sessionId;
|
|
3790
|
+
if (!this.client) return void 0;
|
|
3545
3791
|
const result = await this.client.getSessions();
|
|
3546
3792
|
return result.sessions?.[0]?.sessionId;
|
|
3547
3793
|
}
|
|
@@ -3549,6 +3795,7 @@ var AppHost = class {
|
|
|
3549
3795
|
return MCP_URI_SCHEMES.some((scheme) => url.startsWith(scheme));
|
|
3550
3796
|
}
|
|
3551
3797
|
hasClientCache() {
|
|
3798
|
+
if (!this.client) return false;
|
|
3552
3799
|
return "getOrFetchResource" in this.client && typeof this.client.getOrFetchResource === "function";
|
|
3553
3800
|
}
|
|
3554
3801
|
extractUiResourceUri(tool) {
|
|
@@ -3617,6 +3864,7 @@ function findToolByName(connections, toolName) {
|
|
|
3617
3864
|
return void 0;
|
|
3618
3865
|
}
|
|
3619
3866
|
|
|
3867
|
+
exports.APP_HOST_DEFAULTS = APP_HOST_DEFAULTS;
|
|
3620
3868
|
exports.AppHost = AppHost;
|
|
3621
3869
|
exports.AuthenticationError = AuthenticationError;
|
|
3622
3870
|
exports.ConfigurationError = ConfigurationError;
|
|
@@ -3625,6 +3873,7 @@ exports.DEFAULT_CLIENT_NAME = DEFAULT_CLIENT_NAME;
|
|
|
3625
3873
|
exports.DEFAULT_CLIENT_URI = DEFAULT_CLIENT_URI;
|
|
3626
3874
|
exports.DEFAULT_HEARTBEAT_INTERVAL_MS = DEFAULT_HEARTBEAT_INTERVAL_MS;
|
|
3627
3875
|
exports.DEFAULT_LOGO_URI = DEFAULT_LOGO_URI;
|
|
3876
|
+
exports.DEFAULT_MCP_APP_CSP = DEFAULT_MCP_APP_CSP;
|
|
3628
3877
|
exports.DEFAULT_POLICY_URI = DEFAULT_POLICY_URI;
|
|
3629
3878
|
exports.DisposableStore = DisposableStore;
|
|
3630
3879
|
exports.Emitter = Emitter;
|
|
@@ -3637,6 +3886,8 @@ exports.MultiSessionClient = MultiSessionClient;
|
|
|
3637
3886
|
exports.NotConnectedError = NotConnectedError;
|
|
3638
3887
|
exports.REDIS_KEY_PREFIX = REDIS_KEY_PREFIX;
|
|
3639
3888
|
exports.RpcErrorCodes = RpcErrorCodes;
|
|
3889
|
+
exports.SANDBOX_PROXY_READY_METHOD = SANDBOX_PROXY_READY_METHOD;
|
|
3890
|
+
exports.SANDBOX_RESOURCE_READY_METHOD = SANDBOX_RESOURCE_READY_METHOD;
|
|
3640
3891
|
exports.SESSION_TTL_SECONDS = SESSION_TTL_SECONDS;
|
|
3641
3892
|
exports.SOFTWARE_ID = SOFTWARE_ID;
|
|
3642
3893
|
exports.SOFTWARE_VERSION = SOFTWARE_VERSION;
|