@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
@@ -1,4 +1,4 @@
1
- import { useRef, useState, useEffect, useCallback } from 'react';
1
+ import { useRef, useState, useEffect, useCallback, useSyncExternalStore, useMemo } from 'react';
2
2
  import { nanoid } from 'nanoid';
3
3
  import { AppBridge, PostMessageTransport } from '@modelcontextprotocol/ext-apps/app-bridge';
4
4
 
@@ -327,7 +327,7 @@ var SSEClient = class {
327
327
  }
328
328
  };
329
329
 
330
- // src/client/react/useMcp.ts
330
+ // src/client/react/use-mcp.ts
331
331
  function useMcp(options) {
332
332
  const {
333
333
  url,
@@ -593,7 +593,7 @@ function useMcp(options) {
593
593
  getPrompt,
594
594
  listResources,
595
595
  readResource,
596
- client: clientRef.current
596
+ sseClient: clientRef.current
597
597
  };
598
598
  }
599
599
  var HOST_INFO = { name: "mcp-ts-host", version: "1.0.0" };
@@ -893,8 +893,8 @@ var AppHost = class {
893
893
  }
894
894
  };
895
895
 
896
- // src/client/react/use-mcp-app.ts
897
- function useMcpApp(client, iframeRef, options) {
896
+ // src/client/react/use-app-host.ts
897
+ function useAppHost(client, iframeRef, options) {
898
898
  const [host, setHost] = useState(null);
899
899
  const [error, setError] = useState(null);
900
900
  const initializingRef = useRef(false);
@@ -914,7 +914,7 @@ function useMcpApp(client, iframeRef, options) {
914
914
  setHost(appHost);
915
915
  await appHost.start();
916
916
  } catch (err) {
917
- console.error("[useMcpApp] Failed to initialize AppHost:", err);
917
+ console.error("[useAppHost] Failed to initialize AppHost:", err);
918
918
  setError(err instanceof Error ? err : new Error(String(err)));
919
919
  }
920
920
  };
@@ -926,7 +926,302 @@ function useMcpApp(client, iframeRef, options) {
926
926
  }, [client, iframeRef]);
927
927
  return { host, error };
928
928
  }
929
+ function useMcpAppIframe({
930
+ resourceUri,
931
+ sessionId,
932
+ toolInput,
933
+ toolResult,
934
+ toolStatus,
935
+ sseClient
936
+ }) {
937
+ const iframeRef = useRef(null);
938
+ const { host, error: hostError } = useAppHost(sseClient, iframeRef);
939
+ const [isLaunched, setIsLaunched] = useState(false);
940
+ const [error, setError] = useState(null);
941
+ const launchAttemptedRef = useRef(false);
942
+ const toolInputSentRef = useRef(false);
943
+ const toolResultSentRef = useRef(false);
944
+ useEffect(() => {
945
+ if (hostError) {
946
+ setError(hostError);
947
+ }
948
+ }, [hostError]);
949
+ useEffect(() => {
950
+ if (!host || !resourceUri || !sessionId || launchAttemptedRef.current) return;
951
+ launchAttemptedRef.current = true;
952
+ host.launch(resourceUri, sessionId).then(() => {
953
+ setIsLaunched(true);
954
+ }).catch((err) => {
955
+ const error2 = err instanceof Error ? err : new Error(String(err));
956
+ setError(error2);
957
+ });
958
+ }, [host, resourceUri, sessionId]);
959
+ useEffect(() => {
960
+ if (!host || !isLaunched || !toolInput || toolInputSentRef.current) return;
961
+ toolInputSentRef.current = true;
962
+ host.sendToolInput(toolInput);
963
+ }, [host, isLaunched, toolInput]);
964
+ useEffect(() => {
965
+ if (!host || !isLaunched || toolResult === void 0 || toolResultSentRef.current) return;
966
+ if (toolStatus !== "complete") return;
967
+ toolResultSentRef.current = true;
968
+ const formattedResult = typeof toolResult === "string" ? { content: [{ type: "text", text: toolResult }] } : toolResult;
969
+ host.sendToolResult(formattedResult);
970
+ }, [host, isLaunched, toolResult, toolStatus]);
971
+ return {
972
+ iframeRef,
973
+ isLaunched,
974
+ error
975
+ };
976
+ }
977
+
978
+ // src/client/react/agui-subscriber.ts
979
+ function createMcpAppSubscriber(config) {
980
+ const eventName = config.eventName || "mcp-apps-ui";
981
+ const subscriber = {
982
+ // Listen for custom MCP app events from middleware
983
+ onCustomEvent: ({ event }) => {
984
+ if (event.name === eventName && config.onMcpApp) {
985
+ const payload = event.value;
986
+ config.onMcpApp(payload);
987
+ }
988
+ },
989
+ // Listen for tool call lifecycle events
990
+ onToolCallStartEvent: (params) => {
991
+ if (config.onToolCall && params.event.toolCallName) {
992
+ config.onToolCall({
993
+ toolCallId: params.event.toolCallId || "",
994
+ toolName: params.event.toolCallName,
995
+ status: "start"
996
+ });
997
+ }
998
+ },
999
+ onToolCallArgsEvent: (params) => {
1000
+ if (config.onToolCall) {
1001
+ const args = params.partialToolCallArgs;
1002
+ config.onToolCall({
1003
+ toolCallId: params.event.toolCallId || "",
1004
+ toolName: params.toolCallName || "",
1005
+ // toolCallName is in params, not event
1006
+ args,
1007
+ status: "args"
1008
+ });
1009
+ }
1010
+ },
1011
+ onToolCallEndEvent: (params) => {
1012
+ if (config.onToolCall && params.event.toolCallId) {
1013
+ config.onToolCall({
1014
+ toolCallId: params.event.toolCallId,
1015
+ toolName: params.toolCallName || "",
1016
+ // toolCallName is in params, not event
1017
+ status: "end"
1018
+ });
1019
+ }
1020
+ },
1021
+ onToolCallResultEvent: (params) => {
1022
+ if (config.onToolCall && params.event.toolCallId) {
1023
+ config.onToolCall({
1024
+ toolCallId: params.event.toolCallId,
1025
+ toolName: "",
1026
+ // Not available in result event
1027
+ result: params.event.content,
1028
+ // content contains the result
1029
+ status: "result"
1030
+ });
1031
+ }
1032
+ }
1033
+ };
1034
+ return subscriber;
1035
+ }
1036
+ function subscribeMcpAppEvents(agent, config) {
1037
+ const subscriber = createMcpAppSubscriber(config);
1038
+ const { unsubscribe } = agent.subscribe(subscriber);
1039
+ return unsubscribe;
1040
+ }
1041
+ var McpAppEventManager = class {
1042
+ constructor() {
1043
+ __publicField(this, "events", /* @__PURE__ */ new Map());
1044
+ __publicField(this, "toolCalls", /* @__PURE__ */ new Map());
1045
+ __publicField(this, "listeners", /* @__PURE__ */ new Set());
1046
+ __publicField(this, "unsubscribe");
1047
+ __publicField(this, "cachedSnapshot", {});
1048
+ }
1049
+ /**
1050
+ * Attach to an AG-UI agent
1051
+ */
1052
+ attach(agent) {
1053
+ if (this.unsubscribe) {
1054
+ this.unsubscribe();
1055
+ }
1056
+ this.unsubscribe = subscribeMcpAppEvents(agent, {
1057
+ onMcpApp: (event) => {
1058
+ this.events.set(event.toolName, event);
1059
+ this.cachedSnapshot = Object.fromEntries(this.events);
1060
+ this.notify();
1061
+ },
1062
+ onToolCall: (event) => {
1063
+ if (event.toolCallId) {
1064
+ this.toolCalls.set(event.toolCallId, event);
1065
+ this.notify();
1066
+ }
1067
+ }
1068
+ });
1069
+ }
1070
+ /**
1071
+ * Detach from the current agent
1072
+ */
1073
+ detach() {
1074
+ if (this.unsubscribe) {
1075
+ this.unsubscribe();
1076
+ this.unsubscribe = void 0;
1077
+ }
1078
+ }
1079
+ /**
1080
+ * Get MCP app event for a specific tool
1081
+ */
1082
+ getEvent(toolName) {
1083
+ return this.events.get(toolName);
1084
+ }
1085
+ /**
1086
+ * Get all MCP app events (cached for useSyncExternalStore)
1087
+ */
1088
+ getAllEvents() {
1089
+ return this.cachedSnapshot;
1090
+ }
1091
+ /**
1092
+ * Get tool call event by ID
1093
+ */
1094
+ getToolCall(toolCallId) {
1095
+ return this.toolCalls.get(toolCallId);
1096
+ }
1097
+ /**
1098
+ * Subscribe to event changes
1099
+ */
1100
+ subscribe(listener) {
1101
+ this.listeners.add(listener);
1102
+ return () => this.listeners.delete(listener);
1103
+ }
1104
+ /**
1105
+ * Clear all events
1106
+ */
1107
+ clear() {
1108
+ this.events.clear();
1109
+ this.toolCalls.clear();
1110
+ this.cachedSnapshot = {};
1111
+ this.notify();
1112
+ }
1113
+ notify() {
1114
+ this.listeners.forEach((listener) => listener());
1115
+ }
1116
+ };
1117
+
1118
+ // src/client/react/use-agui-subscriber.ts
1119
+ function useAguiSubscriber(agent, config) {
1120
+ useEffect(() => {
1121
+ if (!agent) return;
1122
+ const subscriber = createMcpAppSubscriber(config);
1123
+ const { unsubscribe } = agent.subscribe(subscriber);
1124
+ return () => unsubscribe();
1125
+ }, [agent, config]);
1126
+ }
1127
+ function useMcpApps(agent, mcpClient) {
1128
+ const [manager] = useState(() => new McpAppEventManager());
1129
+ useEffect(() => {
1130
+ if (!agent) {
1131
+ manager.detach();
1132
+ return;
1133
+ }
1134
+ manager.attach(agent);
1135
+ return () => manager.detach();
1136
+ }, [agent, manager]);
1137
+ const agentApps = useSyncExternalStore(
1138
+ (callback) => manager.subscribe(callback),
1139
+ () => manager.getAllEvents(),
1140
+ () => ({})
1141
+ // Server-side snapshot
1142
+ );
1143
+ const apps = useMemo(() => {
1144
+ const combined = {};
1145
+ if (mcpClient) {
1146
+ for (const conn of mcpClient.connections) {
1147
+ for (const tool of conn.tools) {
1148
+ const meta = tool._meta;
1149
+ if (!meta?.ui) continue;
1150
+ const ui = meta.ui;
1151
+ if (typeof ui !== "object" || !ui) continue;
1152
+ if (ui.visibility && !ui.visibility.includes("app")) continue;
1153
+ const resourceUri = typeof ui.resourceUri === "string" ? ui.resourceUri : typeof ui.uri === "string" ? ui.uri : void 0;
1154
+ if (resourceUri) {
1155
+ combined[tool.name] = {
1156
+ toolCallId: "",
1157
+ resourceUri,
1158
+ sessionId: conn.sessionId,
1159
+ toolName: tool.name
1160
+ };
1161
+ }
1162
+ }
1163
+ }
1164
+ }
1165
+ for (const [toolName, event] of Object.entries(agentApps)) {
1166
+ if (combined[toolName]) {
1167
+ combined[toolName] = {
1168
+ ...combined[toolName],
1169
+ toolCallId: event.toolCallId || combined[toolName].toolCallId,
1170
+ result: event.result,
1171
+ input: event.input,
1172
+ status: event.status
1173
+ };
1174
+ } else {
1175
+ combined[toolName] = event;
1176
+ }
1177
+ }
1178
+ return new Proxy(combined, {
1179
+ get(target, prop) {
1180
+ if (typeof prop !== "string") return void 0;
1181
+ if (prop in target) return target[prop];
1182
+ const match = prop.match(/^tool_[^_]+_(.+)$/);
1183
+ if (match && match[1] in target) {
1184
+ return target[match[1]];
1185
+ }
1186
+ return void 0;
1187
+ },
1188
+ // Support Object.entries, Object.keys, etc. by returning base names
1189
+ ownKeys(target) {
1190
+ return Reflect.ownKeys(target);
1191
+ },
1192
+ getOwnPropertyDescriptor(target, prop) {
1193
+ return Reflect.getOwnPropertyDescriptor(target, prop);
1194
+ }
1195
+ });
1196
+ }, [agentApps, mcpClient]);
1197
+ return {
1198
+ apps,
1199
+ // getApp handles both base and prefixed names transparently via the Proxy
1200
+ getApp: (toolName) => apps[toolName],
1201
+ clear: () => manager.clear()
1202
+ };
1203
+ }
1204
+ function useToolCallEvents(agent) {
1205
+ const [toolCalls, setToolCalls] = useState({});
1206
+ useEffect(() => {
1207
+ if (!agent) {
1208
+ setToolCalls({});
1209
+ return;
1210
+ }
1211
+ const subscriber = createMcpAppSubscriber({
1212
+ onToolCall: (event) => {
1213
+ setToolCalls((prev) => ({
1214
+ ...prev,
1215
+ [event.toolCallId]: event
1216
+ }));
1217
+ }
1218
+ });
1219
+ const { unsubscribe } = agent.subscribe(subscriber);
1220
+ return () => unsubscribe();
1221
+ }, [agent]);
1222
+ return { toolCalls };
1223
+ }
929
1224
 
930
- export { AppHost, SSEClient, useMcp, useMcpApp };
1225
+ export { AppHost, McpAppEventManager, SSEClient, createMcpAppSubscriber, subscribeMcpAppEvents, useAguiSubscriber, useAppHost, useMcp, useMcpAppIframe, useMcpApps, useToolCallEvents };
931
1226
  //# sourceMappingURL=react.mjs.map
932
1227
  //# sourceMappingURL=react.mjs.map