@mcp-ts/sdk 1.1.0 → 1.2.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.
Files changed (46) hide show
  1. package/README.md +21 -10
  2. package/dist/adapters/agui-adapter.js +0 -1
  3. package/dist/adapters/agui-adapter.js.map +1 -1
  4. package/dist/adapters/agui-adapter.mjs +0 -1
  5. package/dist/adapters/agui-adapter.mjs.map +1 -1
  6. package/dist/adapters/agui-middleware.d.mts +5 -0
  7. package/dist/adapters/agui-middleware.d.ts +5 -0
  8. package/dist/adapters/agui-middleware.js +12 -23
  9. package/dist/adapters/agui-middleware.js.map +1 -1
  10. package/dist/adapters/agui-middleware.mjs +12 -23
  11. package/dist/adapters/agui-middleware.mjs.map +1 -1
  12. package/dist/client/react.d.mts +351 -3
  13. package/dist/client/react.d.ts +351 -3
  14. package/dist/client/react.js +308 -6
  15. package/dist/client/react.js.map +1 -1
  16. package/dist/client/react.mjs +302 -7
  17. package/dist/client/react.mjs.map +1 -1
  18. package/dist/client/vue.js +1 -1
  19. package/dist/client/vue.js.map +1 -1
  20. package/dist/client/vue.mjs +1 -1
  21. package/dist/client/vue.mjs.map +1 -1
  22. package/dist/index.d.mts +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +19 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/index.mjs +18 -1
  27. package/dist/index.mjs.map +1 -1
  28. package/dist/shared/index.d.mts +38 -2
  29. package/dist/shared/index.d.ts +38 -2
  30. package/dist/shared/index.js +19 -0
  31. package/dist/shared/index.js.map +1 -1
  32. package/dist/shared/index.mjs +18 -1
  33. package/dist/shared/index.mjs.map +1 -1
  34. package/package.json +1 -1
  35. package/src/adapters/agui-adapter.ts +2 -4
  36. package/src/adapters/agui-middleware.ts +15 -27
  37. package/src/client/react/agui-subscriber.ts +275 -0
  38. package/src/client/react/index.ts +23 -4
  39. package/src/client/react/use-agui-subscriber.ts +270 -0
  40. package/src/client/react/{use-mcp-app.ts → use-app-host.ts} +2 -2
  41. package/src/client/react/use-mcp-app-iframe.ts +164 -0
  42. package/src/client/react/{useMcp.ts → use-mcp.ts} +2 -2
  43. package/src/client/vue/index.ts +1 -1
  44. package/src/shared/index.ts +6 -1
  45. package/src/shared/tool-utils.ts +61 -0
  46. /package/src/client/vue/{useMcp.ts → use-mcp.ts} +0 -0
@@ -329,7 +329,7 @@ var SSEClient = class {
329
329
  }
330
330
  };
331
331
 
332
- // src/client/react/useMcp.ts
332
+ // src/client/react/use-mcp.ts
333
333
  function useMcp(options) {
334
334
  const {
335
335
  url,
@@ -595,7 +595,7 @@ function useMcp(options) {
595
595
  getPrompt,
596
596
  listResources,
597
597
  readResource,
598
- client: clientRef.current
598
+ sseClient: clientRef.current
599
599
  };
600
600
  }
601
601
  var HOST_INFO = { name: "mcp-ts-host", version: "1.0.0" };
@@ -895,8 +895,8 @@ var AppHost = class {
895
895
  }
896
896
  };
897
897
 
898
- // src/client/react/use-mcp-app.ts
899
- function useMcpApp(client, iframeRef, options) {
898
+ // src/client/react/use-app-host.ts
899
+ function useAppHost(client, iframeRef, options) {
900
900
  const [host, setHost] = react.useState(null);
901
901
  const [error, setError] = react.useState(null);
902
902
  const initializingRef = react.useRef(false);
@@ -916,7 +916,7 @@ function useMcpApp(client, iframeRef, options) {
916
916
  setHost(appHost);
917
917
  await appHost.start();
918
918
  } catch (err) {
919
- console.error("[useMcpApp] Failed to initialize AppHost:", err);
919
+ console.error("[useAppHost] Failed to initialize AppHost:", err);
920
920
  setError(err instanceof Error ? err : new Error(String(err)));
921
921
  }
922
922
  };
@@ -928,10 +928,312 @@ function useMcpApp(client, iframeRef, options) {
928
928
  }, [client, iframeRef]);
929
929
  return { host, error };
930
930
  }
931
+ function useMcpAppIframe({
932
+ resourceUri,
933
+ sessionId,
934
+ toolInput,
935
+ toolResult,
936
+ toolStatus,
937
+ sseClient
938
+ }) {
939
+ const iframeRef = react.useRef(null);
940
+ const { host, error: hostError } = useAppHost(sseClient, iframeRef);
941
+ const [isLaunched, setIsLaunched] = react.useState(false);
942
+ const [error, setError] = react.useState(null);
943
+ const launchAttemptedRef = react.useRef(false);
944
+ const toolInputSentRef = react.useRef(false);
945
+ const toolResultSentRef = react.useRef(false);
946
+ 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]);
961
+ 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();
1057
+ }
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) {
1122
+ 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());
1131
+ react.useEffect(() => {
1132
+ if (!agent) {
1133
+ manager.detach();
1134
+ return;
1135
+ }
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) {
1148
+ for (const conn of mcpClient.connections) {
1149
+ 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: "",
1159
+ resourceUri,
1160
+ sessionId: conn.sessionId,
1161
+ toolName: tool.name
1162
+ };
1163
+ }
1164
+ }
1165
+ }
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
+ };
1205
+ }
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 };
1225
+ }
931
1226
 
932
1227
  exports.AppHost = AppHost;
1228
+ exports.McpAppEventManager = McpAppEventManager;
933
1229
  exports.SSEClient = SSEClient;
1230
+ exports.createMcpAppSubscriber = createMcpAppSubscriber;
1231
+ exports.subscribeMcpAppEvents = subscribeMcpAppEvents;
1232
+ exports.useAguiSubscriber = useAguiSubscriber;
1233
+ exports.useAppHost = useAppHost;
934
1234
  exports.useMcp = useMcp;
935
- exports.useMcpApp = useMcpApp;
1235
+ exports.useMcpAppIframe = useMcpAppIframe;
1236
+ exports.useMcpApps = useMcpApps;
1237
+ exports.useToolCallEvents = useToolCallEvents;
936
1238
  //# sourceMappingURL=react.js.map
937
1239
  //# sourceMappingURL=react.js.map