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