@mcp-ts/sdk 1.2.0 → 1.3.1

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.
@@ -3,6 +3,7 @@
3
3
  var react = require('react');
4
4
  var nanoid = require('nanoid');
5
5
  var appBridge = require('@modelcontextprotocol/ext-apps/app-bridge');
6
+ var jsxRuntime = require('react/jsx-runtime');
6
7
 
7
8
  var __defProp = Object.defineProperty;
8
9
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -928,312 +929,106 @@ function useAppHost(client, iframeRef, options) {
928
929
  }, [client, iframeRef]);
929
930
  return { host, error };
930
931
  }
931
- function useMcpAppIframe({
932
- resourceUri,
933
- sessionId,
934
- toolInput,
935
- toolResult,
936
- toolStatus,
937
- sseClient
932
+ var McpAppRenderer = react.memo(function McpAppRenderer2({
933
+ metadata,
934
+ input,
935
+ result,
936
+ status,
937
+ sseClient,
938
+ className
938
939
  }) {
939
940
  const iframeRef = react.useRef(null);
940
941
  const { host, error: hostError } = useAppHost(sseClient, iframeRef);
941
942
  const [isLaunched, setIsLaunched] = react.useState(false);
942
943
  const [error, setError] = react.useState(null);
943
- const launchAttemptedRef = react.useRef(false);
944
- const toolInputSentRef = react.useRef(false);
945
- const toolResultSentRef = react.useRef(false);
944
+ const sentInputRef = react.useRef(false);
945
+ const sentResultRef = react.useRef(false);
946
+ const lastInputRef = react.useRef(input);
947
+ const lastResultRef = react.useRef(result);
948
+ const lastStatusRef = react.useRef(status);
946
949
  react.useEffect(() => {
947
- if (hostError) {
948
- setError(hostError);
949
- }
950
- }, [hostError]);
951
- react.useEffect(() => {
952
- if (!host || !resourceUri || !sessionId || launchAttemptedRef.current) return;
953
- launchAttemptedRef.current = true;
954
- host.launch(resourceUri, sessionId).then(() => {
955
- setIsLaunched(true);
956
- }).catch((err) => {
957
- const error2 = err instanceof Error ? err : new Error(String(err));
958
- setError(error2);
959
- });
960
- }, [host, resourceUri, sessionId]);
950
+ if (!host || !metadata.resourceUri || !metadata.sessionId) return;
951
+ host.launch(metadata.resourceUri, metadata.sessionId).then(() => setIsLaunched(true)).catch((err) => setError(err instanceof Error ? err : new Error(String(err))));
952
+ }, [host, metadata.resourceUri, metadata.sessionId]);
961
953
  react.useEffect(() => {
962
- if (!host || !isLaunched || !toolInput || toolInputSentRef.current) return;
963
- toolInputSentRef.current = true;
964
- host.sendToolInput(toolInput);
965
- }, [host, isLaunched, toolInput]);
966
- react.useEffect(() => {
967
- if (!host || !isLaunched || toolResult === void 0 || toolResultSentRef.current) return;
968
- if (toolStatus !== "complete") return;
969
- toolResultSentRef.current = true;
970
- const formattedResult = typeof toolResult === "string" ? { content: [{ type: "text", text: toolResult }] } : toolResult;
971
- host.sendToolResult(formattedResult);
972
- }, [host, isLaunched, toolResult, toolStatus]);
973
- return {
974
- iframeRef,
975
- isLaunched,
976
- error
977
- };
978
- }
979
-
980
- // src/client/react/agui-subscriber.ts
981
- function createMcpAppSubscriber(config) {
982
- const eventName = config.eventName || "mcp-apps-ui";
983
- const subscriber = {
984
- // Listen for custom MCP app events from middleware
985
- onCustomEvent: ({ event }) => {
986
- if (event.name === eventName && config.onMcpApp) {
987
- const payload = event.value;
988
- config.onMcpApp(payload);
989
- }
990
- },
991
- // Listen for tool call lifecycle events
992
- onToolCallStartEvent: (params) => {
993
- if (config.onToolCall && params.event.toolCallName) {
994
- config.onToolCall({
995
- toolCallId: params.event.toolCallId || "",
996
- toolName: params.event.toolCallName,
997
- status: "start"
998
- });
999
- }
1000
- },
1001
- onToolCallArgsEvent: (params) => {
1002
- if (config.onToolCall) {
1003
- const args = params.partialToolCallArgs;
1004
- config.onToolCall({
1005
- toolCallId: params.event.toolCallId || "",
1006
- toolName: params.toolCallName || "",
1007
- // toolCallName is in params, not event
1008
- args,
1009
- status: "args"
1010
- });
1011
- }
1012
- },
1013
- onToolCallEndEvent: (params) => {
1014
- if (config.onToolCall && params.event.toolCallId) {
1015
- config.onToolCall({
1016
- toolCallId: params.event.toolCallId,
1017
- toolName: params.toolCallName || "",
1018
- // toolCallName is in params, not event
1019
- status: "end"
1020
- });
1021
- }
1022
- },
1023
- onToolCallResultEvent: (params) => {
1024
- if (config.onToolCall && params.event.toolCallId) {
1025
- config.onToolCall({
1026
- toolCallId: params.event.toolCallId,
1027
- toolName: "",
1028
- // Not available in result event
1029
- result: params.event.content,
1030
- // content contains the result
1031
- status: "result"
1032
- });
1033
- }
1034
- }
1035
- };
1036
- return subscriber;
1037
- }
1038
- function subscribeMcpAppEvents(agent, config) {
1039
- const subscriber = createMcpAppSubscriber(config);
1040
- const { unsubscribe } = agent.subscribe(subscriber);
1041
- return unsubscribe;
1042
- }
1043
- var McpAppEventManager = class {
1044
- constructor() {
1045
- __publicField(this, "events", /* @__PURE__ */ new Map());
1046
- __publicField(this, "toolCalls", /* @__PURE__ */ new Map());
1047
- __publicField(this, "listeners", /* @__PURE__ */ new Set());
1048
- __publicField(this, "unsubscribe");
1049
- __publicField(this, "cachedSnapshot", {});
1050
- }
1051
- /**
1052
- * Attach to an AG-UI agent
1053
- */
1054
- attach(agent) {
1055
- if (this.unsubscribe) {
1056
- this.unsubscribe();
954
+ if (!host || !isLaunched || !input) return;
955
+ if (!sentInputRef.current || JSON.stringify(input) !== JSON.stringify(lastInputRef.current)) {
956
+ sentInputRef.current = true;
957
+ lastInputRef.current = input;
958
+ host.sendToolInput(input);
1057
959
  }
1058
- this.unsubscribe = subscribeMcpAppEvents(agent, {
1059
- onMcpApp: (event) => {
1060
- this.events.set(event.toolName, event);
1061
- this.cachedSnapshot = Object.fromEntries(this.events);
1062
- this.notify();
1063
- },
1064
- onToolCall: (event) => {
1065
- if (event.toolCallId) {
1066
- this.toolCalls.set(event.toolCallId, event);
1067
- this.notify();
1068
- }
1069
- }
1070
- });
1071
- }
1072
- /**
1073
- * Detach from the current agent
1074
- */
1075
- detach() {
1076
- if (this.unsubscribe) {
1077
- this.unsubscribe();
1078
- this.unsubscribe = void 0;
1079
- }
1080
- }
1081
- /**
1082
- * Get MCP app event for a specific tool
1083
- */
1084
- getEvent(toolName) {
1085
- return this.events.get(toolName);
1086
- }
1087
- /**
1088
- * Get all MCP app events (cached for useSyncExternalStore)
1089
- */
1090
- getAllEvents() {
1091
- return this.cachedSnapshot;
1092
- }
1093
- /**
1094
- * Get tool call event by ID
1095
- */
1096
- getToolCall(toolCallId) {
1097
- return this.toolCalls.get(toolCallId);
1098
- }
1099
- /**
1100
- * Subscribe to event changes
1101
- */
1102
- subscribe(listener) {
1103
- this.listeners.add(listener);
1104
- return () => this.listeners.delete(listener);
1105
- }
1106
- /**
1107
- * Clear all events
1108
- */
1109
- clear() {
1110
- this.events.clear();
1111
- this.toolCalls.clear();
1112
- this.cachedSnapshot = {};
1113
- this.notify();
1114
- }
1115
- notify() {
1116
- this.listeners.forEach((listener) => listener());
1117
- }
1118
- };
1119
-
1120
- // src/client/react/use-agui-subscriber.ts
1121
- function useAguiSubscriber(agent, config) {
960
+ }, [host, isLaunched, input]);
1122
961
  react.useEffect(() => {
1123
- if (!agent) return;
1124
- const subscriber = createMcpAppSubscriber(config);
1125
- const { unsubscribe } = agent.subscribe(subscriber);
1126
- return () => unsubscribe();
1127
- }, [agent, config]);
1128
- }
1129
- function useMcpApps(agent, mcpClient) {
1130
- const [manager] = react.useState(() => new McpAppEventManager());
962
+ if (!host || !isLaunched || result === void 0) return;
963
+ if (status !== "complete") return;
964
+ if (!sentResultRef.current || JSON.stringify(result) !== JSON.stringify(lastResultRef.current)) {
965
+ sentResultRef.current = true;
966
+ lastResultRef.current = result;
967
+ const formattedResult = typeof result === "string" ? { content: [{ type: "text", text: result }] } : result;
968
+ host.sendToolResult(formattedResult);
969
+ }
970
+ }, [host, isLaunched, result, status]);
1131
971
  react.useEffect(() => {
1132
- if (!agent) {
1133
- manager.detach();
1134
- return;
972
+ if (status === "executing" && lastStatusRef.current !== "executing") {
973
+ sentInputRef.current = false;
974
+ sentResultRef.current = false;
1135
975
  }
1136
- manager.attach(agent);
1137
- return () => manager.detach();
1138
- }, [agent, manager]);
1139
- const agentApps = react.useSyncExternalStore(
1140
- (callback) => manager.subscribe(callback),
1141
- () => manager.getAllEvents(),
1142
- () => ({})
1143
- // Server-side snapshot
1144
- );
1145
- const apps = react.useMemo(() => {
1146
- const combined = {};
1147
- if (mcpClient) {
976
+ lastStatusRef.current = status;
977
+ }, [status]);
978
+ const displayError = error || hostError;
979
+ if (displayError) {
980
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `p-4 bg-red-900/20 border border-red-700 rounded text-red-200 ${className || ""}`, children: [
981
+ "Error: ",
982
+ displayError.message || String(displayError)
983
+ ] });
984
+ }
985
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full border border-gray-700 rounded overflow-hidden bg-white min-h-96 my-2 relative ${className || ""}`, children: [
986
+ /* @__PURE__ */ jsxRuntime.jsx(
987
+ "iframe",
988
+ {
989
+ ref: iframeRef,
990
+ sandbox: "allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-downloads",
991
+ className: "w-full h-full min-h-96",
992
+ style: { height: "auto" },
993
+ title: "MCP App"
994
+ }
995
+ ),
996
+ !isLaunched && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-gray-900/50 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-6 h-6 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" }) })
997
+ ] });
998
+ });
999
+ function useMcpApps(mcpClient) {
1000
+ const getAppMetadata = react.useCallback(
1001
+ (toolName) => {
1002
+ if (!mcpClient) return void 0;
1003
+ const extractedName = extractToolName(toolName);
1148
1004
  for (const conn of mcpClient.connections) {
1149
1005
  for (const tool of conn.tools) {
1150
- const meta = tool._meta;
1151
- if (!meta?.ui) continue;
1152
- const ui = meta.ui;
1153
- if (typeof ui !== "object" || !ui) continue;
1154
- if (ui.visibility && !ui.visibility.includes("app")) continue;
1155
- const resourceUri = typeof ui.resourceUri === "string" ? ui.resourceUri : typeof ui.uri === "string" ? ui.uri : void 0;
1156
- if (resourceUri) {
1157
- combined[tool.name] = {
1158
- toolCallId: "",
1006
+ const candidateName = extractToolName(tool.name);
1007
+ const resourceUri = tool.mcpApp?.resourceUri ?? tool._meta?.ui?.resourceUri ?? tool._meta?.["ui/resourceUri"];
1008
+ if (resourceUri && candidateName === extractedName) {
1009
+ return {
1010
+ toolName: candidateName,
1159
1011
  resourceUri,
1160
- sessionId: conn.sessionId,
1161
- toolName: tool.name
1012
+ sessionId: conn.sessionId
1162
1013
  };
1163
1014
  }
1164
1015
  }
1165
1016
  }
1166
- }
1167
- for (const [toolName, event] of Object.entries(agentApps)) {
1168
- if (combined[toolName]) {
1169
- combined[toolName] = {
1170
- ...combined[toolName],
1171
- toolCallId: event.toolCallId || combined[toolName].toolCallId,
1172
- result: event.result,
1173
- input: event.input,
1174
- status: event.status
1175
- };
1176
- } else {
1177
- combined[toolName] = event;
1178
- }
1179
- }
1180
- return new Proxy(combined, {
1181
- get(target, prop) {
1182
- if (typeof prop !== "string") return void 0;
1183
- if (prop in target) return target[prop];
1184
- const match = prop.match(/^tool_[^_]+_(.+)$/);
1185
- if (match && match[1] in target) {
1186
- return target[match[1]];
1187
- }
1188
- return void 0;
1189
- },
1190
- // Support Object.entries, Object.keys, etc. by returning base names
1191
- ownKeys(target) {
1192
- return Reflect.ownKeys(target);
1193
- },
1194
- getOwnPropertyDescriptor(target, prop) {
1195
- return Reflect.getOwnPropertyDescriptor(target, prop);
1196
- }
1197
- });
1198
- }, [agentApps, mcpClient]);
1199
- return {
1200
- apps,
1201
- // getApp handles both base and prefixed names transparently via the Proxy
1202
- getApp: (toolName) => apps[toolName],
1203
- clear: () => manager.clear()
1204
- };
1017
+ return void 0;
1018
+ },
1019
+ [mcpClient]
1020
+ );
1021
+ return { getAppMetadata, McpAppRenderer };
1205
1022
  }
1206
- function useToolCallEvents(agent) {
1207
- const [toolCalls, setToolCalls] = react.useState({});
1208
- react.useEffect(() => {
1209
- if (!agent) {
1210
- setToolCalls({});
1211
- return;
1212
- }
1213
- const subscriber = createMcpAppSubscriber({
1214
- onToolCall: (event) => {
1215
- setToolCalls((prev) => ({
1216
- ...prev,
1217
- [event.toolCallId]: event
1218
- }));
1219
- }
1220
- });
1221
- const { unsubscribe } = agent.subscribe(subscriber);
1222
- return () => unsubscribe();
1223
- }, [agent]);
1224
- return { toolCalls };
1023
+ function extractToolName(fullName) {
1024
+ const match = fullName.match(/(?:tool_[^_]+_)?(.+)$/);
1025
+ return match?.[1] || fullName;
1225
1026
  }
1226
1027
 
1227
1028
  exports.AppHost = AppHost;
1228
- exports.McpAppEventManager = McpAppEventManager;
1229
1029
  exports.SSEClient = SSEClient;
1230
- exports.createMcpAppSubscriber = createMcpAppSubscriber;
1231
- exports.subscribeMcpAppEvents = subscribeMcpAppEvents;
1232
- exports.useAguiSubscriber = useAguiSubscriber;
1233
1030
  exports.useAppHost = useAppHost;
1234
1031
  exports.useMcp = useMcp;
1235
- exports.useMcpAppIframe = useMcpAppIframe;
1236
1032
  exports.useMcpApps = useMcpApps;
1237
- exports.useToolCallEvents = useToolCallEvents;
1238
1033
  //# sourceMappingURL=react.js.map
1239
1034
  //# sourceMappingURL=react.js.map