@mcp-ts/sdk 1.3.10 → 1.5.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 +20 -27
- package/dist/adapters/agui-adapter.d.mts +16 -0
- package/dist/adapters/agui-adapter.d.ts +16 -0
- package/dist/adapters/agui-adapter.js +185 -0
- package/dist/adapters/agui-adapter.js.map +1 -1
- package/dist/adapters/agui-adapter.mjs +185 -0
- package/dist/adapters/agui-adapter.mjs.map +1 -1
- package/dist/adapters/agui-middleware.d.mts +2 -0
- package/dist/adapters/agui-middleware.d.ts +2 -0
- package/dist/adapters/agui-middleware.js.map +1 -1
- package/dist/adapters/agui-middleware.mjs.map +1 -1
- package/dist/adapters/ai-adapter.d.mts +21 -0
- package/dist/adapters/ai-adapter.d.ts +21 -0
- package/dist/adapters/ai-adapter.js +175 -0
- package/dist/adapters/ai-adapter.js.map +1 -1
- package/dist/adapters/ai-adapter.mjs +175 -0
- package/dist/adapters/ai-adapter.mjs.map +1 -1
- package/dist/adapters/langchain-adapter.d.mts +16 -0
- package/dist/adapters/langchain-adapter.d.ts +16 -0
- package/dist/adapters/langchain-adapter.js +179 -0
- package/dist/adapters/langchain-adapter.js.map +1 -1
- package/dist/adapters/langchain-adapter.mjs +179 -0
- package/dist/adapters/langchain-adapter.mjs.map +1 -1
- package/dist/client/index.d.mts +4 -190
- package/dist/client/index.d.ts +4 -190
- 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 +31 -17
- package/dist/client/react.d.ts +31 -17
- package/dist/client/react.js +447 -103
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +443 -105
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.d.mts +5 -4
- package/dist/client/vue.d.ts +5 -4
- 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-DcYfpY3H.d.mts +295 -0
- package/dist/index-GfC_eNEv.d.ts +295 -0
- package/dist/index.d.mts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +1120 -59
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1097 -60
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.mts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +18 -5
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +18 -5
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.d.mts +86 -4
- package/dist/shared/index.d.ts +86 -4
- package/dist/shared/index.js +874 -0
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs +865 -1
- package/dist/shared/index.mjs.map +1 -1
- package/dist/tool-router-Bo8qZbsD.d.ts +325 -0
- package/dist/tool-router-XnWVxPzv.d.mts +325 -0
- package/dist/{types-CW6lghof.d.mts → types-CfCoIsWI.d.mts} +27 -1
- package/dist/{types-CW6lghof.d.ts → types-CfCoIsWI.d.ts} +27 -1
- package/package.json +15 -12
- package/src/adapters/agui-adapter.ts +79 -0
- package/src/adapters/ai-adapter.ts +75 -0
- package/src/adapters/langchain-adapter.ts +75 -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 +3 -0
- package/src/client/react/use-app-host.ts +8 -15
- package/src/client/react/use-mcp-apps.tsx +262 -49
- package/src/client/react/use-mcp.ts +23 -12
- package/src/client/utils/app-host-utils.ts +62 -0
- package/src/client/vue/use-mcp.ts +23 -12
- package/src/server/index.ts +2 -0
- package/src/server/mcp/oauth-client.ts +34 -9
- package/src/shared/index.ts +36 -0
- package/src/shared/meta-tools.ts +387 -0
- package/src/shared/schema-compressor.ts +124 -0
- package/src/shared/tool-index.ts +499 -0
- package/src/shared/tool-router.ts +469 -0
- package/src/shared/types.ts +30 -0
package/dist/client/react.js
CHANGED
|
@@ -356,18 +356,28 @@ function useMcp(options) {
|
|
|
356
356
|
);
|
|
357
357
|
}
|
|
358
358
|
case "auth_required": {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
359
|
+
const url2 = (event.authUrl || "").trim();
|
|
360
|
+
if (!url2) {
|
|
361
|
+
onLog?.("error", "OAuth required but authorization URL is missing", { sessionId: event.sessionId });
|
|
362
|
+
return prev.map(
|
|
363
|
+
(c) => c.sessionId === event.sessionId ? {
|
|
364
|
+
...c,
|
|
365
|
+
state: "FAILED",
|
|
366
|
+
error: "OAuth authorization URL not available",
|
|
367
|
+
authUrl: void 0
|
|
368
|
+
} : c
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
onLog?.("info", `OAuth required - redirecting to ${url2}`, { authUrl: url2 });
|
|
372
|
+
if (!suppressAuthRedirectSessionsRef.current.has(event.sessionId)) {
|
|
373
|
+
if (onRedirect) {
|
|
374
|
+
onRedirect(url2);
|
|
375
|
+
} else if (typeof window !== "undefined") {
|
|
376
|
+
window.location.href = url2;
|
|
367
377
|
}
|
|
368
378
|
}
|
|
369
379
|
return prev.map(
|
|
370
|
-
(c) => c.sessionId === event.sessionId ? { ...c, state: "AUTHENTICATING", authUrl:
|
|
380
|
+
(c) => c.sessionId === event.sessionId ? { ...c, state: "AUTHENTICATING", authUrl: url2 } : c
|
|
371
381
|
);
|
|
372
382
|
}
|
|
373
383
|
case "error": {
|
|
@@ -583,22 +593,88 @@ function useMcp(options) {
|
|
|
583
593
|
]
|
|
584
594
|
);
|
|
585
595
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
"
|
|
595
|
-
|
|
596
|
-
"
|
|
597
|
-
|
|
598
|
-
"
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
596
|
+
|
|
597
|
+
// src/client/core/constants.ts
|
|
598
|
+
var SANDBOX_PROXY_READY_METHOD = "ui/notifications/sandbox-proxy-ready";
|
|
599
|
+
var SANDBOX_RESOURCE_READY_METHOD = "ui/notifications/sandbox-resource-ready";
|
|
600
|
+
var APP_HOST_DEFAULTS = {
|
|
601
|
+
/** Default timeout for waiting for the sandbox proxy to be ready (ms). */
|
|
602
|
+
SANDBOX_TIMEOUT_MS: 1e4,
|
|
603
|
+
/** Default host info reported to guest apps. */
|
|
604
|
+
HOST_INFO: { name: "mcp-ts-host", version: "1.0.0" },
|
|
605
|
+
/** Supported MCP App URI schemes. */
|
|
606
|
+
URI_SCHEMES: ["ui://", "mcp-app://"],
|
|
607
|
+
/** Default theme for the host context. */
|
|
608
|
+
THEME: "dark",
|
|
609
|
+
/** Default platform for the host context. */
|
|
610
|
+
PLATFORM: "web",
|
|
611
|
+
/** Default max height for the iframe container (px). */
|
|
612
|
+
MAX_HEIGHT: 6e3
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
// src/client/utils/app-host-utils.ts
|
|
616
|
+
var DEFAULT_SANDBOX_TIMEOUT_MS = APP_HOST_DEFAULTS.SANDBOX_TIMEOUT_MS;
|
|
617
|
+
async function setupSandboxProxyIframe(iframe, sandboxProxyUrl) {
|
|
618
|
+
iframe.style.width = "100%";
|
|
619
|
+
iframe.style.height = "100%";
|
|
620
|
+
iframe.style.border = "none";
|
|
621
|
+
iframe.style.backgroundColor = "transparent";
|
|
622
|
+
iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-downloads");
|
|
623
|
+
const onReady = new Promise((resolve, reject) => {
|
|
624
|
+
let settled = false;
|
|
625
|
+
const cleanup = () => {
|
|
626
|
+
window.removeEventListener("message", messageListener);
|
|
627
|
+
iframe.removeEventListener("error", errorListener);
|
|
628
|
+
};
|
|
629
|
+
const timeoutId = setTimeout(() => {
|
|
630
|
+
if (!settled) {
|
|
631
|
+
settled = true;
|
|
632
|
+
cleanup();
|
|
633
|
+
reject(new Error("Timed out waiting for sandbox proxy iframe to be ready"));
|
|
634
|
+
}
|
|
635
|
+
}, DEFAULT_SANDBOX_TIMEOUT_MS);
|
|
636
|
+
const messageListener = (event) => {
|
|
637
|
+
if (event.source === iframe.contentWindow) {
|
|
638
|
+
if (event.data?.method === SANDBOX_PROXY_READY_METHOD) {
|
|
639
|
+
if (!settled) {
|
|
640
|
+
settled = true;
|
|
641
|
+
clearTimeout(timeoutId);
|
|
642
|
+
cleanup();
|
|
643
|
+
resolve();
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
const errorListener = () => {
|
|
649
|
+
if (!settled) {
|
|
650
|
+
settled = true;
|
|
651
|
+
clearTimeout(timeoutId);
|
|
652
|
+
cleanup();
|
|
653
|
+
reject(new Error("Failed to load sandbox proxy iframe"));
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
window.addEventListener("message", messageListener);
|
|
657
|
+
iframe.addEventListener("error", errorListener);
|
|
658
|
+
});
|
|
659
|
+
iframe.src = sandboxProxyUrl.href;
|
|
660
|
+
return { onReady };
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// src/client/core/app-host.ts
|
|
664
|
+
var DEFAULT_MCP_APP_CSP = {
|
|
665
|
+
"default-src": "'self'",
|
|
666
|
+
"script-src": "'self' 'unsafe-inline' 'unsafe-eval' https: blob:",
|
|
667
|
+
"style-src": "'self' 'unsafe-inline' https:",
|
|
668
|
+
"connect-src": "'self' https: wss:",
|
|
669
|
+
"img-src": "'self' data: https: blob:",
|
|
670
|
+
"font-src": "'self' data: https:",
|
|
671
|
+
"media-src": "'self' https: blob:",
|
|
672
|
+
"frame-src": "'none'",
|
|
673
|
+
"object-src": "'none'",
|
|
674
|
+
"base-uri": "'self'"
|
|
675
|
+
};
|
|
676
|
+
var HOST_INFO = APP_HOST_DEFAULTS.HOST_INFO;
|
|
677
|
+
var MCP_URI_SCHEMES = APP_HOST_DEFAULTS.URI_SCHEMES;
|
|
602
678
|
var AppHost = class {
|
|
603
679
|
constructor(client, iframe, options) {
|
|
604
680
|
this.client = client;
|
|
@@ -607,10 +683,12 @@ var AppHost = class {
|
|
|
607
683
|
__publicField(this, "sessionId");
|
|
608
684
|
__publicField(this, "resourceCache", /* @__PURE__ */ new Map());
|
|
609
685
|
__publicField(this, "debug");
|
|
610
|
-
|
|
686
|
+
__publicField(this, "sandboxConfig");
|
|
687
|
+
__publicField(this, "options");
|
|
611
688
|
__publicField(this, "onAppMessage");
|
|
612
|
-
this.
|
|
613
|
-
this.
|
|
689
|
+
this.options = options || {};
|
|
690
|
+
this.debug = this.options.debug ?? false;
|
|
691
|
+
this.sandboxConfig = this.options.sandbox;
|
|
614
692
|
this.bridge = this.initializeBridge();
|
|
615
693
|
}
|
|
616
694
|
// ============================================
|
|
@@ -637,19 +715,35 @@ var AppHost = class {
|
|
|
637
715
|
}
|
|
638
716
|
}
|
|
639
717
|
/**
|
|
640
|
-
* Launch an MCP App from a URL
|
|
718
|
+
* Launch an MCP App from a URL, MCP resource URI, or RAW HTML.
|
|
641
719
|
* Loads the HTML first, then establishes bridge connection.
|
|
642
720
|
*/
|
|
643
|
-
async launch(
|
|
721
|
+
async launch(source, sessionId) {
|
|
644
722
|
if (sessionId) this.sessionId = sessionId;
|
|
645
723
|
const initializedPromise = this.onAppReady();
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
724
|
+
let htmlToRender = source.html;
|
|
725
|
+
if (!htmlToRender && source.uri) {
|
|
726
|
+
if (this.isMcpUri(source.uri)) {
|
|
727
|
+
htmlToRender = await this.readMcpAppHtml(source.uri);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (!htmlToRender && source.uri && !this.isMcpUri(source.uri)) {
|
|
731
|
+
this.iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-downloads");
|
|
732
|
+
this.iframe.src = source.uri;
|
|
733
|
+
await this.onIframeReady();
|
|
734
|
+
await this.connectBridge();
|
|
735
|
+
} else if (htmlToRender) {
|
|
736
|
+
if (!this.sandboxConfig) {
|
|
737
|
+
throw new Error("Sandbox configuration requires a proxy URL to render HTML safely.");
|
|
738
|
+
}
|
|
739
|
+
await this.launchSandboxedHtml(htmlToRender, this.sandboxConfig);
|
|
740
|
+
await this.connectBridge();
|
|
741
|
+
this.log("Sending HTML resource to sandbox proxy (MCP Apps notification)");
|
|
742
|
+
await this.bridge.sendSandboxResourceReady({
|
|
743
|
+
html: htmlToRender,
|
|
744
|
+
csp: this.sandboxConfig.csp
|
|
745
|
+
});
|
|
650
746
|
}
|
|
651
|
-
await this.onIframeReady();
|
|
652
|
-
await this.connectBridge();
|
|
653
747
|
this.log("Waiting for app initialization");
|
|
654
748
|
await Promise.race([
|
|
655
749
|
initializedPromise,
|
|
@@ -660,6 +754,19 @@ var AppHost = class {
|
|
|
660
754
|
]);
|
|
661
755
|
this.log("App launched and ready");
|
|
662
756
|
}
|
|
757
|
+
// Set host context manually
|
|
758
|
+
setHostContext(context) {
|
|
759
|
+
this.options.hostContext = context;
|
|
760
|
+
if (this.bridge) {
|
|
761
|
+
this.bridge.setHostContext(context);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
// Send streaming inputs manually
|
|
765
|
+
sendToolInputPartial(params) {
|
|
766
|
+
if (this.bridge) {
|
|
767
|
+
this.bridge.sendToolInputPartial(params);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
663
770
|
/**
|
|
664
771
|
* Wait for app to signal initialization complete
|
|
665
772
|
*/
|
|
@@ -710,14 +817,17 @@ var AppHost = class {
|
|
|
710
817
|
this.log("Sending tool cancellation to app");
|
|
711
818
|
this.bridge.sendToolCancelled({ reason });
|
|
712
819
|
}
|
|
820
|
+
/**
|
|
821
|
+
* Tell the guest UI the resource is being torn down (unload / cleanup).
|
|
822
|
+
* Forwards to {@link AppBridge.teardownResource} on `@modelcontextprotocol/ext-apps/app-bridge`.
|
|
823
|
+
*/
|
|
824
|
+
teardownResource(params = {}) {
|
|
825
|
+
this.log("Sending resource teardown to app");
|
|
826
|
+
this.bridge.teardownResource(params);
|
|
827
|
+
}
|
|
713
828
|
// ============================================
|
|
714
829
|
// Private: Initialization
|
|
715
830
|
// ============================================
|
|
716
|
-
configureSandbox() {
|
|
717
|
-
if (this.iframe.sandbox.value !== SANDBOX_PERMISSIONS) {
|
|
718
|
-
this.iframe.sandbox.value = SANDBOX_PERMISSIONS;
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
831
|
initializeBridge() {
|
|
722
832
|
const bridge = new appBridge.AppBridge(
|
|
723
833
|
null,
|
|
@@ -726,12 +836,10 @@ var AppHost = class {
|
|
|
726
836
|
openLinks: {},
|
|
727
837
|
serverTools: {},
|
|
728
838
|
logging: {},
|
|
729
|
-
// Declare support for model context updates
|
|
730
839
|
updateModelContext: { text: {} }
|
|
731
840
|
},
|
|
732
841
|
{
|
|
733
|
-
|
|
734
|
-
hostContext: {
|
|
842
|
+
hostContext: this.options.hostContext || {
|
|
735
843
|
theme: "dark",
|
|
736
844
|
platform: "web",
|
|
737
845
|
containerDimensions: { maxHeight: 6e3 },
|
|
@@ -740,19 +848,56 @@ var AppHost = class {
|
|
|
740
848
|
}
|
|
741
849
|
}
|
|
742
850
|
);
|
|
851
|
+
bridge.fallbackRequestHandler = this.options.onFallbackRequest;
|
|
743
852
|
bridge.oncalltool = (params) => this.handleToolCall(params);
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
853
|
+
if (this.options.onReadResource) {
|
|
854
|
+
bridge.onreadresource = async (params) => {
|
|
855
|
+
const resp = await this.options.onReadResource(params.uri);
|
|
856
|
+
return {
|
|
857
|
+
contents: resp.contents.map((c) => ({
|
|
858
|
+
uri: params.uri,
|
|
859
|
+
text: c.text,
|
|
860
|
+
blob: c.blob
|
|
861
|
+
}))
|
|
862
|
+
};
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
bridge.onopenlink = async (params, extra) => {
|
|
866
|
+
if (this.options.onOpenLink) {
|
|
867
|
+
return await this.options.onOpenLink(params, extra);
|
|
868
|
+
}
|
|
869
|
+
return this.handleOpenLink(params);
|
|
870
|
+
};
|
|
871
|
+
bridge.onmessage = async (params, extra) => {
|
|
872
|
+
if (this.options.onMessage) {
|
|
873
|
+
return await this.options.onMessage(params, extra);
|
|
874
|
+
}
|
|
875
|
+
return this.handleMessage(params);
|
|
876
|
+
};
|
|
877
|
+
bridge.onloggingmessage = (params) => {
|
|
878
|
+
this.log(`App log [${params.level}]: ${params.data}`);
|
|
879
|
+
if (this.options.onLoggingMessage) {
|
|
880
|
+
this.options.onLoggingMessage(params);
|
|
881
|
+
}
|
|
882
|
+
};
|
|
747
883
|
bridge.onupdatemodelcontext = async () => ({});
|
|
748
|
-
bridge.onsizechange = async (
|
|
749
|
-
|
|
750
|
-
if (
|
|
884
|
+
bridge.onsizechange = async (params) => {
|
|
885
|
+
const { width, height } = params;
|
|
886
|
+
if (height !== void 0 && height > 0) {
|
|
887
|
+
this.iframe.style.height = `${height}px`;
|
|
888
|
+
}
|
|
889
|
+
if (width !== void 0 && width > 0) this.iframe.style.minWidth = `min(${width}px, 100%)`;
|
|
890
|
+
if (this.options.onSizeChanged) {
|
|
891
|
+
this.options.onSizeChanged(params);
|
|
892
|
+
}
|
|
751
893
|
return {};
|
|
752
894
|
};
|
|
753
|
-
bridge.onrequestdisplaymode = async (params) =>
|
|
754
|
-
|
|
755
|
-
|
|
895
|
+
bridge.onrequestdisplaymode = async (params, extra) => {
|
|
896
|
+
if (this.options.onRequestDisplayMode) {
|
|
897
|
+
return await this.options.onRequestDisplayMode(params, extra);
|
|
898
|
+
}
|
|
899
|
+
return { mode: params.mode === "fullscreen" ? "fullscreen" : "inline" };
|
|
900
|
+
};
|
|
756
901
|
return bridge;
|
|
757
902
|
}
|
|
758
903
|
async connectBridge() {
|
|
@@ -766,6 +911,9 @@ var AppHost = class {
|
|
|
766
911
|
this.log("Bridge connected successfully");
|
|
767
912
|
} catch (error) {
|
|
768
913
|
this.log("Bridge connection failed", "error");
|
|
914
|
+
if (this.options.onError) {
|
|
915
|
+
this.options.onError(error instanceof Error ? error : new Error(String(error)));
|
|
916
|
+
}
|
|
769
917
|
throw error;
|
|
770
918
|
}
|
|
771
919
|
}
|
|
@@ -773,8 +921,11 @@ var AppHost = class {
|
|
|
773
921
|
// Private: Bridge Event Handlers
|
|
774
922
|
// ============================================
|
|
775
923
|
async handleToolCall(params) {
|
|
776
|
-
if (
|
|
777
|
-
|
|
924
|
+
if (this.options.onCallTool) {
|
|
925
|
+
return await this.options.onCallTool(params);
|
|
926
|
+
}
|
|
927
|
+
if (!this.client || !this.client.isConnected()) {
|
|
928
|
+
throw new Error("Client disconnected or not provided");
|
|
778
929
|
}
|
|
779
930
|
const sessionId = await this.getSessionId();
|
|
780
931
|
if (!sessionId) {
|
|
@@ -798,13 +949,19 @@ var AppHost = class {
|
|
|
798
949
|
// ============================================
|
|
799
950
|
// Private: Resource Loading
|
|
800
951
|
// ============================================
|
|
801
|
-
async
|
|
802
|
-
|
|
803
|
-
|
|
952
|
+
async launchSandboxedHtml(html, config) {
|
|
953
|
+
const sandboxUrlString = config.url instanceof URL ? config.url.href : config.url;
|
|
954
|
+
const url = new URL(sandboxUrlString, globalThis.location?.href);
|
|
955
|
+
if (config.csp && Object.keys(config.csp).length > 0) {
|
|
956
|
+
url.searchParams.set("csp", JSON.stringify(config.csp));
|
|
804
957
|
}
|
|
958
|
+
const { onReady } = await setupSandboxProxyIframe(this.iframe, url);
|
|
959
|
+
await onReady;
|
|
960
|
+
}
|
|
961
|
+
async readMcpAppHtml(uri) {
|
|
805
962
|
const sessionId = await this.getSessionId();
|
|
806
|
-
if (!sessionId) {
|
|
807
|
-
throw new Error("No active session");
|
|
963
|
+
if (!sessionId && !this.options.onReadResource) {
|
|
964
|
+
throw new Error("No active session.");
|
|
808
965
|
}
|
|
809
966
|
const response = await this.fetchResourceWithCache(sessionId, uri);
|
|
810
967
|
if (!response?.contents?.length) {
|
|
@@ -815,10 +972,18 @@ var AppHost = class {
|
|
|
815
972
|
if (!html) {
|
|
816
973
|
throw new Error(`Invalid content in resource: ${uri}`);
|
|
817
974
|
}
|
|
818
|
-
|
|
819
|
-
this.iframe.src = URL.createObjectURL(blob);
|
|
975
|
+
return html;
|
|
820
976
|
}
|
|
821
977
|
async fetchResourceWithCache(sessionId, uri) {
|
|
978
|
+
if (this.options.onReadResource) {
|
|
979
|
+
return await this.options.onReadResource(uri);
|
|
980
|
+
}
|
|
981
|
+
if (!sessionId) {
|
|
982
|
+
throw new Error("No active session");
|
|
983
|
+
}
|
|
984
|
+
if (!this.client) {
|
|
985
|
+
throw new Error("No client to read resource from");
|
|
986
|
+
}
|
|
822
987
|
if (this.hasClientCache()) {
|
|
823
988
|
return this.client.getOrFetchResource(sessionId, uri);
|
|
824
989
|
}
|
|
@@ -831,8 +996,11 @@ var AppHost = class {
|
|
|
831
996
|
}
|
|
832
997
|
async preloadResource(uri) {
|
|
833
998
|
try {
|
|
999
|
+
if (this.options.onReadResource) {
|
|
1000
|
+
return await this.options.onReadResource(uri);
|
|
1001
|
+
}
|
|
834
1002
|
const sessionId = await this.getSessionId();
|
|
835
|
-
if (!sessionId) return null;
|
|
1003
|
+
if (!sessionId || !this.client) return null;
|
|
836
1004
|
return await this.client.readResource(sessionId, uri);
|
|
837
1005
|
} catch (error) {
|
|
838
1006
|
this.log(`Preload failed for ${uri}`, "warn");
|
|
@@ -844,6 +1012,7 @@ var AppHost = class {
|
|
|
844
1012
|
// ============================================
|
|
845
1013
|
async getSessionId() {
|
|
846
1014
|
if (this.sessionId) return this.sessionId;
|
|
1015
|
+
if (!this.client) return void 0;
|
|
847
1016
|
const result = await this.client.getSessions();
|
|
848
1017
|
return result.sessions?.[0]?.sessionId;
|
|
849
1018
|
}
|
|
@@ -851,6 +1020,7 @@ var AppHost = class {
|
|
|
851
1020
|
return MCP_URI_SCHEMES.some((scheme) => url.startsWith(scheme));
|
|
852
1021
|
}
|
|
853
1022
|
hasClientCache() {
|
|
1023
|
+
if (!this.client) return false;
|
|
854
1024
|
return "getOrFetchResource" in this.client && typeof this.client.getOrFetchResource === "function";
|
|
855
1025
|
}
|
|
856
1026
|
extractUiResourceUri(tool) {
|
|
@@ -894,10 +1064,7 @@ function useAppHost(client, iframeRef, options) {
|
|
|
894
1064
|
initializingRef.current = true;
|
|
895
1065
|
const initHost = async () => {
|
|
896
1066
|
try {
|
|
897
|
-
const appHost = new AppHost(client, iframeRef.current);
|
|
898
|
-
appHost.onAppMessage = (params) => {
|
|
899
|
-
onMessageRef.current?.(params);
|
|
900
|
-
};
|
|
1067
|
+
const appHost = new AppHost(client, iframeRef.current, options);
|
|
901
1068
|
setHost(appHost);
|
|
902
1069
|
await appHost.start();
|
|
903
1070
|
} catch (err) {
|
|
@@ -913,44 +1080,163 @@ function useAppHost(client, iframeRef, options) {
|
|
|
913
1080
|
}, [client, iframeRef]);
|
|
914
1081
|
return { host, error };
|
|
915
1082
|
}
|
|
916
|
-
|
|
1083
|
+
|
|
1084
|
+
// src/shared/meta-tools.ts
|
|
1085
|
+
function resolveMetaToolProxy(toolName, args) {
|
|
1086
|
+
if (toolName === "mcp_execute_tool") {
|
|
1087
|
+
const innerName = args?.toolName;
|
|
1088
|
+
const innerArgs = args?.args;
|
|
1089
|
+
return {
|
|
1090
|
+
toolName: typeof innerName === "string" && innerName ? innerName : toolName,
|
|
1091
|
+
args: innerArgs && typeof innerArgs === "object" && !Array.isArray(innerArgs) ? innerArgs : {}
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
const match = toolName.match(/(?:tool_[^_]+_)?(.+)$/);
|
|
1095
|
+
const resolvedName = match?.[1] ?? toolName;
|
|
1096
|
+
return { toolName: resolvedName, args: args ?? {} };
|
|
1097
|
+
}
|
|
1098
|
+
var McpAppViewInner = react.forwardRef(function McpAppView({
|
|
917
1099
|
clientRef,
|
|
918
1100
|
name,
|
|
1101
|
+
toolResourceUri,
|
|
1102
|
+
html,
|
|
919
1103
|
input,
|
|
920
1104
|
result,
|
|
921
|
-
status,
|
|
922
|
-
|
|
923
|
-
|
|
1105
|
+
status = "idle",
|
|
1106
|
+
toolInputPartial,
|
|
1107
|
+
toolCancelled,
|
|
1108
|
+
sandbox,
|
|
1109
|
+
hostContext,
|
|
1110
|
+
onCallTool,
|
|
1111
|
+
onReadResource,
|
|
1112
|
+
onFallbackRequest,
|
|
1113
|
+
onMessage,
|
|
1114
|
+
onOpenLink,
|
|
1115
|
+
onLoggingMessage,
|
|
1116
|
+
onSizeChanged,
|
|
1117
|
+
onError: onHostError,
|
|
1118
|
+
className,
|
|
1119
|
+
loader
|
|
1120
|
+
}, ref) {
|
|
924
1121
|
const mcpClient = clientRef.current;
|
|
925
|
-
const
|
|
1122
|
+
const { toolName: resolvedToolName, args: resolvedInput } = resolveMetaToolProxy(name, input);
|
|
1123
|
+
const metadata = getMcpAppMetadata(mcpClient, resolvedToolName, resolvedInput);
|
|
926
1124
|
const sseClient = mcpClient?.sseClient ?? null;
|
|
927
|
-
const resourceUri = metadata?.resourceUri;
|
|
1125
|
+
const resourceUri = toolResourceUri || metadata?.resourceUri;
|
|
928
1126
|
const appSessionId = metadata?.sessionId;
|
|
929
1127
|
const iframeRef = react.useRef(null);
|
|
930
|
-
const
|
|
1128
|
+
const containerRef = react.useRef(null);
|
|
1129
|
+
const preFullscreenHeightRef = react.useRef(null);
|
|
1130
|
+
const displayModeRef = react.useRef("inline");
|
|
1131
|
+
const [displayMode, setDisplayMode] = react.useState("inline");
|
|
1132
|
+
const setDisplayModeWithRef = (mode) => {
|
|
1133
|
+
displayModeRef.current = mode;
|
|
1134
|
+
setDisplayMode(mode);
|
|
1135
|
+
};
|
|
1136
|
+
const { host, error: hostError } = useAppHost(sseClient, iframeRef, {
|
|
1137
|
+
sandbox,
|
|
1138
|
+
hostContext,
|
|
1139
|
+
onCallTool,
|
|
1140
|
+
onReadResource,
|
|
1141
|
+
onFallbackRequest,
|
|
1142
|
+
onMessage,
|
|
1143
|
+
onOpenLink,
|
|
1144
|
+
onLoggingMessage,
|
|
1145
|
+
// Intercept onSizeChanged: when exiting fullscreen, ignore guest resize events
|
|
1146
|
+
// that arrive with the shrunken viewport dimensions, and restore the pre-fullscreen height.
|
|
1147
|
+
onSizeChanged: (params) => {
|
|
1148
|
+
if (displayModeRef.current === "inline" && preFullscreenHeightRef.current !== null) {
|
|
1149
|
+
const savedHeight = preFullscreenHeightRef.current;
|
|
1150
|
+
preFullscreenHeightRef.current = null;
|
|
1151
|
+
if (iframeRef.current) {
|
|
1152
|
+
iframeRef.current.style.height = `${savedHeight}px`;
|
|
1153
|
+
}
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
onSizeChanged?.(params);
|
|
1157
|
+
},
|
|
1158
|
+
onError: onHostError,
|
|
1159
|
+
onRequestDisplayMode: async (params) => {
|
|
1160
|
+
if (params.mode === "fullscreen") {
|
|
1161
|
+
if (iframeRef.current) {
|
|
1162
|
+
const h = iframeRef.current.getBoundingClientRect().height;
|
|
1163
|
+
if (h > 0) preFullscreenHeightRef.current = h;
|
|
1164
|
+
}
|
|
1165
|
+
try {
|
|
1166
|
+
if (containerRef.current?.requestFullscreen) {
|
|
1167
|
+
await containerRef.current.requestFullscreen();
|
|
1168
|
+
} else if (containerRef.current?.webkitRequestFullscreen) {
|
|
1169
|
+
await containerRef.current.webkitRequestFullscreen();
|
|
1170
|
+
}
|
|
1171
|
+
setDisplayModeWithRef("fullscreen");
|
|
1172
|
+
} catch (err) {
|
|
1173
|
+
console.warn("[McpAppHost] requestFullscreen failed:", err);
|
|
1174
|
+
preFullscreenHeightRef.current = null;
|
|
1175
|
+
return { mode: "inline" };
|
|
1176
|
+
}
|
|
1177
|
+
} else if (params.mode === "inline") {
|
|
1178
|
+
restoreHeightAfterFullscreen();
|
|
1179
|
+
try {
|
|
1180
|
+
if (document.fullscreenElement) {
|
|
1181
|
+
await document.exitFullscreen();
|
|
1182
|
+
}
|
|
1183
|
+
} catch (err) {
|
|
1184
|
+
}
|
|
1185
|
+
setDisplayModeWithRef("inline");
|
|
1186
|
+
}
|
|
1187
|
+
return { mode: params.mode };
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
react.useImperativeHandle(
|
|
1191
|
+
ref,
|
|
1192
|
+
() => ({
|
|
1193
|
+
teardownResource: (params) => {
|
|
1194
|
+
host?.teardownResource(params ?? {});
|
|
1195
|
+
}
|
|
1196
|
+
}),
|
|
1197
|
+
[host]
|
|
1198
|
+
);
|
|
931
1199
|
const [isLaunched, setIsLaunched] = react.useState(false);
|
|
932
1200
|
const [error, setError] = react.useState(null);
|
|
933
1201
|
const sentInputRef = react.useRef(false);
|
|
934
1202
|
const sentResultRef = react.useRef(false);
|
|
935
|
-
const lastInputRef = react.useRef(
|
|
1203
|
+
const lastInputRef = react.useRef(resolvedInput);
|
|
936
1204
|
const lastResultRef = react.useRef(result);
|
|
937
1205
|
const lastStatusRef = react.useRef(status);
|
|
938
1206
|
react.useEffect(() => {
|
|
939
1207
|
setIsLaunched(false);
|
|
940
1208
|
setError(null);
|
|
941
1209
|
}, [resourceUri, appSessionId]);
|
|
1210
|
+
const restoreHeightAfterFullscreen = () => {
|
|
1211
|
+
const savedHeight = preFullscreenHeightRef.current;
|
|
1212
|
+
if (savedHeight && iframeRef.current) {
|
|
1213
|
+
iframeRef.current.style.height = `${savedHeight}px`;
|
|
1214
|
+
}
|
|
1215
|
+
preFullscreenHeightRef.current = null;
|
|
1216
|
+
};
|
|
1217
|
+
react.useEffect(() => {
|
|
1218
|
+
const onFullscreenChange = () => {
|
|
1219
|
+
const isFullscreen = !!document.fullscreenElement;
|
|
1220
|
+
if (!isFullscreen && displayModeRef.current === "fullscreen") {
|
|
1221
|
+
restoreHeightAfterFullscreen();
|
|
1222
|
+
setDisplayModeWithRef("inline");
|
|
1223
|
+
}
|
|
1224
|
+
};
|
|
1225
|
+
document.addEventListener("fullscreenchange", onFullscreenChange);
|
|
1226
|
+
return () => document.removeEventListener("fullscreenchange", onFullscreenChange);
|
|
1227
|
+
}, []);
|
|
942
1228
|
react.useEffect(() => {
|
|
943
|
-
if (!host || !resourceUri
|
|
944
|
-
host.launch(resourceUri, appSessionId).then(() => setIsLaunched(true)).catch((err) => setError(err instanceof Error ? err : new Error(String(err))));
|
|
945
|
-
}, [host, resourceUri, appSessionId]);
|
|
1229
|
+
if (!host || !resourceUri && !html) return;
|
|
1230
|
+
host.launch({ uri: resourceUri, html }, appSessionId).then(() => setIsLaunched(true)).catch((err) => setError(err instanceof Error ? err : new Error(String(err))));
|
|
1231
|
+
}, [host, resourceUri, html, appSessionId]);
|
|
946
1232
|
react.useEffect(() => {
|
|
947
|
-
if (!host || !isLaunched || !resourceUri || !appSessionId || !
|
|
948
|
-
if (!sentInputRef.current || JSON.stringify(
|
|
1233
|
+
if (!host || !isLaunched || !resourceUri || !appSessionId || !resolvedInput) return;
|
|
1234
|
+
if (!sentInputRef.current || JSON.stringify(resolvedInput) !== JSON.stringify(lastInputRef.current)) {
|
|
949
1235
|
sentInputRef.current = true;
|
|
950
|
-
lastInputRef.current =
|
|
951
|
-
host.sendToolInput(
|
|
1236
|
+
lastInputRef.current = resolvedInput;
|
|
1237
|
+
host.sendToolInput(resolvedInput);
|
|
952
1238
|
}
|
|
953
|
-
}, [host, isLaunched,
|
|
1239
|
+
}, [host, isLaunched, resolvedInput, resourceUri, appSessionId, resolvedToolName]);
|
|
954
1240
|
react.useEffect(() => {
|
|
955
1241
|
if (!host || !isLaunched || !resourceUri || !appSessionId || result === void 0) return;
|
|
956
1242
|
if (status !== "complete") return;
|
|
@@ -960,7 +1246,7 @@ var McpAppView = react.memo(function McpAppView2({
|
|
|
960
1246
|
const formattedResult = typeof result === "string" ? { content: [{ type: "text", text: result }] } : result;
|
|
961
1247
|
host.sendToolResult(formattedResult);
|
|
962
1248
|
}
|
|
963
|
-
}, [host, isLaunched, result, status, resourceUri, appSessionId,
|
|
1249
|
+
}, [host, isLaunched, result, status, resourceUri, appSessionId, resolvedToolName]);
|
|
964
1250
|
react.useEffect(() => {
|
|
965
1251
|
if (status === "executing" && lastStatusRef.current !== "executing") {
|
|
966
1252
|
sentInputRef.current = false;
|
|
@@ -968,7 +1254,26 @@ var McpAppView = react.memo(function McpAppView2({
|
|
|
968
1254
|
}
|
|
969
1255
|
lastStatusRef.current = status;
|
|
970
1256
|
}, [status]);
|
|
971
|
-
|
|
1257
|
+
react.useEffect(() => {
|
|
1258
|
+
if (!host) return;
|
|
1259
|
+
const mergedCtx = {
|
|
1260
|
+
theme: APP_HOST_DEFAULTS.THEME,
|
|
1261
|
+
platform: APP_HOST_DEFAULTS.PLATFORM,
|
|
1262
|
+
containerDimensions: { maxHeight: APP_HOST_DEFAULTS.MAX_HEIGHT },
|
|
1263
|
+
availableDisplayModes: ["inline", "fullscreen"],
|
|
1264
|
+
...hostContext || {},
|
|
1265
|
+
displayMode
|
|
1266
|
+
// always override with our authoritative state
|
|
1267
|
+
};
|
|
1268
|
+
host.setHostContext(mergedCtx);
|
|
1269
|
+
}, [host, hostContext, displayMode]);
|
|
1270
|
+
react.useEffect(() => {
|
|
1271
|
+
if (host && toolInputPartial) host.sendToolInputPartial(toolInputPartial);
|
|
1272
|
+
}, [host, toolInputPartial]);
|
|
1273
|
+
react.useEffect(() => {
|
|
1274
|
+
if (host && toolCancelled) host.sendToolCancelled("User cancelled");
|
|
1275
|
+
}, [host, toolCancelled]);
|
|
1276
|
+
if (!metadata && !html && !toolResourceUri) {
|
|
972
1277
|
return null;
|
|
973
1278
|
}
|
|
974
1279
|
const displayError = error || hostError;
|
|
@@ -978,43 +1283,76 @@ var McpAppView = react.memo(function McpAppView2({
|
|
|
978
1283
|
displayError.message || String(displayError)
|
|
979
1284
|
] });
|
|
980
1285
|
}
|
|
981
|
-
|
|
1286
|
+
const opacityClass = isLaunched ? "opacity-100" : "opacity-0";
|
|
1287
|
+
let containerClass = `w-full border border-gray-700 rounded bg-transparent my-2 relative ${className || ""}`;
|
|
1288
|
+
let iframeClass = `w-full transition-opacity duration-300 ${opacityClass}`;
|
|
1289
|
+
if (displayMode === "fullscreen") {
|
|
1290
|
+
containerClass = `w-full h-full bg-black m-0 p-0 flex flex-col relative`;
|
|
1291
|
+
iframeClass = `w-full flex-1 transition-opacity duration-300 ${opacityClass}`;
|
|
1292
|
+
}
|
|
1293
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: containerClass, children: [
|
|
1294
|
+
displayMode === "fullscreen" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-0 right-0 p-2 z-[100000] w-full bg-gradient-to-b from-black/80 to-transparent flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1295
|
+
"button",
|
|
1296
|
+
{
|
|
1297
|
+
title: "Exit Fullscreen",
|
|
1298
|
+
onClick: () => {
|
|
1299
|
+
restoreHeightAfterFullscreen();
|
|
1300
|
+
if (document.fullscreenElement) document.exitFullscreen();
|
|
1301
|
+
setDisplayModeWithRef("inline");
|
|
1302
|
+
},
|
|
1303
|
+
className: "px-4 py-2 bg-gray-800 hover:bg-gray-700 text-white rounded-md shadow flex items-center gap-2 border border-gray-600 transition-colors",
|
|
1304
|
+
children: [
|
|
1305
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3" }) }),
|
|
1306
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: "Exit" })
|
|
1307
|
+
]
|
|
1308
|
+
}
|
|
1309
|
+
) }),
|
|
982
1310
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
983
1311
|
"iframe",
|
|
984
1312
|
{
|
|
985
1313
|
ref: iframeRef,
|
|
986
1314
|
sandbox: "allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-downloads",
|
|
987
|
-
|
|
988
|
-
|
|
1315
|
+
allow: "fullscreen",
|
|
1316
|
+
className: iframeClass,
|
|
989
1317
|
title: "MCP App"
|
|
990
1318
|
}
|
|
991
1319
|
),
|
|
992
|
-
!isLaunched && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-
|
|
1320
|
+
!isLaunched && loader && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-transparent flex items-center justify-center pointer-events-none z-10", children: loader })
|
|
993
1321
|
] });
|
|
994
1322
|
});
|
|
1323
|
+
var McpAppView2 = react.memo(McpAppViewInner);
|
|
1324
|
+
McpAppView2.displayName = "McpAppView";
|
|
1325
|
+
var McpAppRenderer = react.memo(
|
|
1326
|
+
react.forwardRef(function McpAppRenderer2({ client, ...props }, ref) {
|
|
1327
|
+
const clientRef = react.useRef(client || null);
|
|
1328
|
+
clientRef.current = client || null;
|
|
1329
|
+
return /* @__PURE__ */ jsxRuntime.jsx(McpAppView2, { ref, clientRef, ...props });
|
|
1330
|
+
})
|
|
1331
|
+
);
|
|
995
1332
|
function useMcpApps(mcpClient) {
|
|
996
|
-
const clientRef = react.useRef(mcpClient);
|
|
997
|
-
clientRef.current = mcpClient;
|
|
998
1333
|
const getAppMetadata = react.useCallback(
|
|
999
|
-
(toolName) => getMcpAppMetadata(
|
|
1000
|
-
[]
|
|
1334
|
+
(toolName) => getMcpAppMetadata(mcpClient, toolName),
|
|
1335
|
+
[mcpClient]
|
|
1001
1336
|
);
|
|
1002
|
-
const
|
|
1003
|
-
const Renderer = react.
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1337
|
+
const BoundMcpAppRenderer = react.useMemo(() => {
|
|
1338
|
+
const Renderer = react.forwardRef(
|
|
1339
|
+
function BoundMcpAppRenderer2(props, ref) {
|
|
1340
|
+
return /* @__PURE__ */ jsxRuntime.jsx(McpAppRenderer, { ref, client: mcpClient, ...props });
|
|
1341
|
+
}
|
|
1342
|
+
);
|
|
1343
|
+
Renderer.displayName = "BoundMcpAppRenderer";
|
|
1344
|
+
return react.memo(Renderer);
|
|
1345
|
+
}, [mcpClient]);
|
|
1346
|
+
return { getAppMetadata, McpAppRenderer: BoundMcpAppRenderer };
|
|
1010
1347
|
}
|
|
1011
1348
|
function extractToolName(fullName) {
|
|
1012
1349
|
const match = fullName.match(/(?:tool_[^_]+_)?(.+)$/);
|
|
1013
1350
|
return match?.[1] || fullName;
|
|
1014
1351
|
}
|
|
1015
|
-
function getMcpAppMetadata(mcpClient, toolName) {
|
|
1352
|
+
function getMcpAppMetadata(mcpClient, toolName, input) {
|
|
1016
1353
|
if (!mcpClient) return void 0;
|
|
1017
|
-
const
|
|
1354
|
+
const { toolName: proxyToolName } = resolveMetaToolProxy(toolName, input);
|
|
1355
|
+
const extractedName = extractToolName(proxyToolName);
|
|
1018
1356
|
for (const conn of mcpClient.connections) {
|
|
1019
1357
|
for (const tool of conn.tools) {
|
|
1020
1358
|
const candidateName = extractToolName(tool.name);
|
|
@@ -1031,8 +1369,14 @@ function getMcpAppMetadata(mcpClient, toolName) {
|
|
|
1031
1369
|
return void 0;
|
|
1032
1370
|
}
|
|
1033
1371
|
|
|
1372
|
+
exports.APP_HOST_DEFAULTS = APP_HOST_DEFAULTS;
|
|
1034
1373
|
exports.AppHost = AppHost;
|
|
1374
|
+
exports.DEFAULT_MCP_APP_CSP = DEFAULT_MCP_APP_CSP;
|
|
1375
|
+
exports.McpAppRenderer = McpAppRenderer;
|
|
1376
|
+
exports.SANDBOX_PROXY_READY_METHOD = SANDBOX_PROXY_READY_METHOD;
|
|
1377
|
+
exports.SANDBOX_RESOURCE_READY_METHOD = SANDBOX_RESOURCE_READY_METHOD;
|
|
1035
1378
|
exports.SSEClient = SSEClient;
|
|
1379
|
+
exports.getMcpAppMetadata = getMcpAppMetadata;
|
|
1036
1380
|
exports.useAppHost = useAppHost;
|
|
1037
1381
|
exports.useMcp = useMcp;
|
|
1038
1382
|
exports.useMcpApps = useMcpApps;
|