@apollo/client-ai-apps 0.6.4 → 0.7.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 (272) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/CONTRIBUTING.md +195 -0
  3. package/README.md +74 -0
  4. package/dist/core/AbstractApolloClient.d.ts +33 -0
  5. package/dist/core/AbstractApolloClient.d.ts.map +1 -0
  6. package/dist/core/AbstractApolloClient.js +129 -0
  7. package/dist/core/AbstractApolloClient.js.map +1 -0
  8. package/dist/core/ApolloClient.d.ts +3 -7
  9. package/dist/core/ApolloClient.d.ts.map +1 -1
  10. package/dist/core/ApolloClient.js +5 -4
  11. package/dist/core/ApolloClient.js.map +1 -1
  12. package/dist/core/McpAppManager.d.ts +42 -0
  13. package/dist/core/McpAppManager.d.ts.map +1 -0
  14. package/dist/core/McpAppManager.js +56 -0
  15. package/dist/core/McpAppManager.js.map +1 -0
  16. package/dist/core/typeRegistration.d.ts +0 -14
  17. package/dist/core/typeRegistration.d.ts.map +1 -1
  18. package/dist/core/typeRegistration.js.map +1 -1
  19. package/dist/core/types.d.ts +2 -1
  20. package/dist/core/types.d.ts.map +1 -1
  21. package/dist/core/types.js.map +1 -1
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/index.mcp.d.ts +0 -1
  26. package/dist/index.mcp.d.ts.map +1 -1
  27. package/dist/index.mcp.js +0 -1
  28. package/dist/index.mcp.js.map +1 -1
  29. package/dist/index.openai.d.ts +0 -1
  30. package/dist/index.openai.d.ts.map +1 -1
  31. package/dist/index.openai.js +0 -1
  32. package/dist/index.openai.js.map +1 -1
  33. package/dist/link/ToolCallLink.d.ts +6 -1
  34. package/dist/link/ToolCallLink.d.ts.map +1 -1
  35. package/dist/link/ToolCallLink.js +17 -4
  36. package/dist/link/ToolCallLink.js.map +1 -1
  37. package/dist/link/ToolHydrationLink.d.ts +21 -0
  38. package/dist/link/ToolHydrationLink.d.ts.map +1 -0
  39. package/dist/link/ToolHydrationLink.js +57 -0
  40. package/dist/link/ToolHydrationLink.js.map +1 -0
  41. package/dist/mcp/core/ApolloClient.d.ts +3 -20
  42. package/dist/mcp/core/ApolloClient.d.ts.map +1 -1
  43. package/dist/mcp/core/ApolloClient.js +20 -98
  44. package/dist/mcp/core/ApolloClient.js.map +1 -1
  45. package/dist/mcp/index.d.ts +0 -1
  46. package/dist/mcp/index.d.ts.map +1 -1
  47. package/dist/mcp/index.js +0 -1
  48. package/dist/mcp/index.js.map +1 -1
  49. package/dist/openai/core/ApolloClient.d.ts +3 -20
  50. package/dist/openai/core/ApolloClient.d.ts.map +1 -1
  51. package/dist/openai/core/ApolloClient.js +36 -98
  52. package/dist/openai/core/ApolloClient.js.map +1 -1
  53. package/dist/openai/index.d.ts +0 -1
  54. package/dist/openai/index.d.ts.map +1 -1
  55. package/dist/openai/index.js +0 -1
  56. package/dist/openai/index.js.map +1 -1
  57. package/dist/openai/react/index.d.ts +0 -7
  58. package/dist/openai/react/index.d.ts.map +1 -1
  59. package/dist/openai/react/index.js +0 -7
  60. package/dist/openai/react/index.js.map +1 -1
  61. package/dist/react/ApolloProvider.d.ts.map +1 -1
  62. package/dist/react/ApolloProvider.js +1 -1
  63. package/dist/react/ApolloProvider.js.map +1 -1
  64. package/dist/{mcp/react/hooks → react}/createHydrationUtils.d.ts +1 -1
  65. package/dist/react/createHydrationUtils.d.ts.map +1 -0
  66. package/dist/{mcp/react/hooks → react}/createHydrationUtils.js +7 -9
  67. package/dist/react/createHydrationUtils.js.map +1 -0
  68. package/dist/react/hooks/internal/useApolloClient.d.ts +3 -0
  69. package/dist/react/hooks/internal/useApolloClient.d.ts.map +1 -0
  70. package/dist/{mcp/react/hooks → react/hooks/internal}/useApolloClient.js +3 -3
  71. package/dist/react/hooks/internal/useApolloClient.js.map +1 -0
  72. package/dist/react/hooks/useApp.d.ts.map +1 -0
  73. package/dist/react/hooks/useApp.js +5 -0
  74. package/dist/react/hooks/useApp.js.map +1 -0
  75. package/dist/react/hooks/useHostContext.d.ts.map +1 -0
  76. package/dist/{openai/react → react}/hooks/useHostContext.js +1 -1
  77. package/dist/react/hooks/useHostContext.js.map +1 -0
  78. package/dist/react/hooks/useToolInfo.d.ts +3 -0
  79. package/dist/react/hooks/useToolInfo.d.ts.map +1 -0
  80. package/dist/react/hooks/useToolInfo.js +5 -0
  81. package/dist/react/hooks/useToolInfo.js.map +1 -0
  82. package/dist/react/hooks/useToolMetadata.d.ts +2 -0
  83. package/dist/react/hooks/useToolMetadata.d.ts.map +1 -0
  84. package/dist/react/hooks/useToolMetadata.js +5 -0
  85. package/dist/react/hooks/useToolMetadata.js.map +1 -0
  86. package/dist/react/index.d.ts +5 -16
  87. package/dist/react/index.d.ts.map +1 -1
  88. package/dist/react/index.js +5 -19
  89. package/dist/react/index.js.map +1 -1
  90. package/dist/types/application-manifest.d.ts +1 -0
  91. package/dist/types/application-manifest.d.ts.map +1 -1
  92. package/dist/types/application-manifest.js.map +1 -1
  93. package/dist/utilities/connectToHost.d.ts +3 -0
  94. package/dist/utilities/connectToHost.d.ts.map +1 -0
  95. package/dist/utilities/connectToHost.js +11 -0
  96. package/dist/utilities/connectToHost.js.map +1 -0
  97. package/dist/utilities/index.d.ts +1 -0
  98. package/dist/utilities/index.d.ts.map +1 -1
  99. package/dist/utilities/index.js +1 -0
  100. package/dist/utilities/index.js.map +1 -1
  101. package/dist/vite/apolloClientAiApps.d.ts.map +1 -1
  102. package/dist/vite/apolloClientAiApps.js +2 -0
  103. package/dist/vite/apolloClientAiApps.js.map +1 -1
  104. package/package.json +5 -22
  105. package/src/core/AbstractApolloClient.ts +217 -0
  106. package/src/core/ApolloClient.ts +8 -10
  107. package/src/core/McpAppManager.ts +106 -0
  108. package/src/core/typeRegistration.ts +0 -15
  109. package/src/core/types.ts +2 -1
  110. package/src/index.mcp.ts +0 -1
  111. package/src/index.openai.ts +0 -1
  112. package/src/index.ts +1 -6
  113. package/src/link/ToolCallLink.ts +27 -5
  114. package/src/link/ToolHydrationLink.ts +90 -0
  115. package/src/link/__tests__/ToolCallLink.test.ts +99 -0
  116. package/src/mcp/core/ApolloClient.ts +32 -165
  117. package/src/mcp/core/__tests__/ApolloClient.test.ts +571 -71
  118. package/src/mcp/index.ts +0 -1
  119. package/src/openai/core/ApolloClient.ts +48 -161
  120. package/src/openai/core/__tests__/ApolloClient.test.ts +916 -118
  121. package/src/openai/index.ts +0 -1
  122. package/src/openai/react/index.ts +0 -7
  123. package/src/react/ApolloProvider.tsx +1 -6
  124. package/src/react/__tests__/ApolloProvider/mcp.test.tsx +66 -29
  125. package/src/react/__tests__/ApolloProvider/openai.test.tsx +16 -41
  126. package/src/react/__tests__/createHydrationUtils.test.tsx +1260 -0
  127. package/src/{mcp/react/hooks → react}/createHydrationUtils.ts +7 -10
  128. package/src/react/hooks/__tests__/useApp.test.tsx +46 -0
  129. package/src/react/hooks/__tests__/useHostContext.test.tsx +99 -0
  130. package/src/react/hooks/__tests__/useToolInfo.test.tsx +98 -0
  131. package/src/react/hooks/__tests__/useToolMetadata.test.tsx +58 -0
  132. package/src/{mcp/react/hooks → react/hooks/internal}/useApolloClient.ts +3 -3
  133. package/src/{mcp/react → react}/hooks/useApp.ts +1 -1
  134. package/src/{openai/react → react}/hooks/useHostContext.ts +1 -1
  135. package/src/react/hooks/useToolInfo.ts +6 -0
  136. package/src/react/hooks/useToolMetadata.ts +5 -0
  137. package/src/react/index.ts +5 -36
  138. package/src/testing/internal/graphql/parseManifestOperation.ts +87 -0
  139. package/src/testing/internal/index.ts +3 -0
  140. package/src/testing/internal/matchers/index.ts +1 -0
  141. package/src/testing/internal/matchers/toEmitAnything.ts +43 -0
  142. package/src/testing/internal/matchers/types.ts +1 -0
  143. package/src/testing/internal/mcp/mockMcpHost.ts +25 -4
  144. package/src/testing/internal/tests/eachHostEnv.ts +22 -0
  145. package/src/testing/internal/utilities/createHostEnv.ts +117 -0
  146. package/src/types/application-manifest.ts +1 -0
  147. package/src/utilities/connectToHost.ts +13 -0
  148. package/src/utilities/index.ts +1 -0
  149. package/src/vite/__tests__/apolloClientAiApps.test.ts +56 -0
  150. package/src/vite/apolloClientAiApps.ts +5 -0
  151. package/tsconfig.vite.json +1 -1
  152. package/vitest.config.ts +13 -0
  153. package/dist/mcp/core/McpAppManager.d.ts +0 -30
  154. package/dist/mcp/core/McpAppManager.d.ts.map +0 -1
  155. package/dist/mcp/core/McpAppManager.js +0 -82
  156. package/dist/mcp/core/McpAppManager.js.map +0 -1
  157. package/dist/mcp/link/ToolCallLink.d.ts +0 -28
  158. package/dist/mcp/link/ToolCallLink.d.ts.map +0 -1
  159. package/dist/mcp/link/ToolCallLink.js +0 -35
  160. package/dist/mcp/link/ToolCallLink.js.map +0 -1
  161. package/dist/mcp/react/hooks/createHydrationUtils.d.ts.map +0 -1
  162. package/dist/mcp/react/hooks/createHydrationUtils.js.map +0 -1
  163. package/dist/mcp/react/hooks/useApolloClient.d.ts +0 -3
  164. package/dist/mcp/react/hooks/useApolloClient.d.ts.map +0 -1
  165. package/dist/mcp/react/hooks/useApolloClient.js.map +0 -1
  166. package/dist/mcp/react/hooks/useApp.d.ts.map +0 -1
  167. package/dist/mcp/react/hooks/useApp.js +0 -5
  168. package/dist/mcp/react/hooks/useApp.js.map +0 -1
  169. package/dist/mcp/react/hooks/useHostContext.d.ts.map +0 -1
  170. package/dist/mcp/react/hooks/useHostContext.js +0 -7
  171. package/dist/mcp/react/hooks/useHostContext.js.map +0 -1
  172. package/dist/mcp/react/hooks/useToolInfo.d.ts +0 -3
  173. package/dist/mcp/react/hooks/useToolInfo.d.ts.map +0 -1
  174. package/dist/mcp/react/hooks/useToolInfo.js +0 -10
  175. package/dist/mcp/react/hooks/useToolInfo.js.map +0 -1
  176. package/dist/mcp/react/hooks/useToolInput.d.ts +0 -7
  177. package/dist/mcp/react/hooks/useToolInput.d.ts.map +0 -1
  178. package/dist/mcp/react/hooks/useToolInput.js +0 -9
  179. package/dist/mcp/react/hooks/useToolInput.js.map +0 -1
  180. package/dist/mcp/react/hooks/useToolMetadata.d.ts +0 -2
  181. package/dist/mcp/react/hooks/useToolMetadata.d.ts.map +0 -1
  182. package/dist/mcp/react/hooks/useToolMetadata.js +0 -5
  183. package/dist/mcp/react/hooks/useToolMetadata.js.map +0 -1
  184. package/dist/mcp/react/hooks/useToolName.d.ts +0 -7
  185. package/dist/mcp/react/hooks/useToolName.d.ts.map +0 -1
  186. package/dist/mcp/react/hooks/useToolName.js +0 -9
  187. package/dist/mcp/react/hooks/useToolName.js.map +0 -1
  188. package/dist/mcp/react/index.d.ts +0 -8
  189. package/dist/mcp/react/index.d.ts.map +0 -1
  190. package/dist/mcp/react/index.js +0 -8
  191. package/dist/mcp/react/index.js.map +0 -1
  192. package/dist/openai/core/McpAppManager.d.ts +0 -29
  193. package/dist/openai/core/McpAppManager.d.ts.map +0 -1
  194. package/dist/openai/core/McpAppManager.js +0 -91
  195. package/dist/openai/core/McpAppManager.js.map +0 -1
  196. package/dist/openai/link/ToolCallLink.d.ts +0 -28
  197. package/dist/openai/link/ToolCallLink.d.ts.map +0 -1
  198. package/dist/openai/link/ToolCallLink.js +0 -35
  199. package/dist/openai/link/ToolCallLink.js.map +0 -1
  200. package/dist/openai/react/hooks/createHydrationUtils.d.ts +0 -15
  201. package/dist/openai/react/hooks/createHydrationUtils.d.ts.map +0 -1
  202. package/dist/openai/react/hooks/createHydrationUtils.js +0 -113
  203. package/dist/openai/react/hooks/createHydrationUtils.js.map +0 -1
  204. package/dist/openai/react/hooks/useApp.d.ts +0 -2
  205. package/dist/openai/react/hooks/useApp.d.ts.map +0 -1
  206. package/dist/openai/react/hooks/useApp.js +0 -5
  207. package/dist/openai/react/hooks/useApp.js.map +0 -1
  208. package/dist/openai/react/hooks/useHostContext.d.ts +0 -2
  209. package/dist/openai/react/hooks/useHostContext.d.ts.map +0 -1
  210. package/dist/openai/react/hooks/useHostContext.js.map +0 -1
  211. package/dist/openai/react/hooks/useToolInfo.d.ts +0 -3
  212. package/dist/openai/react/hooks/useToolInfo.d.ts.map +0 -1
  213. package/dist/openai/react/hooks/useToolInfo.js +0 -10
  214. package/dist/openai/react/hooks/useToolInfo.js.map +0 -1
  215. package/dist/openai/react/hooks/useToolInput.d.ts +0 -7
  216. package/dist/openai/react/hooks/useToolInput.d.ts.map +0 -1
  217. package/dist/openai/react/hooks/useToolInput.js +0 -9
  218. package/dist/openai/react/hooks/useToolInput.js.map +0 -1
  219. package/dist/openai/react/hooks/useToolMetadata.d.ts +0 -2
  220. package/dist/openai/react/hooks/useToolMetadata.d.ts.map +0 -1
  221. package/dist/openai/react/hooks/useToolMetadata.js +0 -5
  222. package/dist/openai/react/hooks/useToolMetadata.js.map +0 -1
  223. package/dist/openai/react/hooks/useToolName.d.ts +0 -7
  224. package/dist/openai/react/hooks/useToolName.d.ts.map +0 -1
  225. package/dist/openai/react/hooks/useToolName.js +0 -9
  226. package/dist/openai/react/hooks/useToolName.js.map +0 -1
  227. package/dist/react/index.mcp.d.ts +0 -3
  228. package/dist/react/index.mcp.d.ts.map +0 -1
  229. package/dist/react/index.mcp.js +0 -3
  230. package/dist/react/index.mcp.js.map +0 -1
  231. package/dist/react/index.openai.d.ts +0 -3
  232. package/dist/react/index.openai.d.ts.map +0 -1
  233. package/dist/react/index.openai.js +0 -3
  234. package/dist/react/index.openai.js.map +0 -1
  235. package/dist/react/missingHook.d.ts +0 -2
  236. package/dist/react/missingHook.d.ts.map +0 -1
  237. package/dist/react/missingHook.js +0 -6
  238. package/dist/react/missingHook.js.map +0 -1
  239. package/src/mcp/core/McpAppManager.ts +0 -129
  240. package/src/mcp/link/ToolCallLink.ts +0 -40
  241. package/src/mcp/link/__tests__/ToolCallLink.test.ts +0 -62
  242. package/src/mcp/react/hooks/__tests__/createHydrationUtils.test.tsx +0 -1228
  243. package/src/mcp/react/hooks/__tests__/useApp.test.tsx +0 -46
  244. package/src/mcp/react/hooks/__tests__/useHostContext.test.tsx +0 -95
  245. package/src/mcp/react/hooks/__tests__/useToolInfo.test.tsx +0 -53
  246. package/src/mcp/react/hooks/__tests__/useToolInput.test.tsx +0 -50
  247. package/src/mcp/react/hooks/__tests__/useToolMetadata.test.tsx +0 -53
  248. package/src/mcp/react/hooks/__tests__/useToolName.test.tsx +0 -50
  249. package/src/mcp/react/hooks/useHostContext.ts +0 -14
  250. package/src/mcp/react/hooks/useToolInfo.ts +0 -13
  251. package/src/mcp/react/hooks/useToolInput.ts +0 -10
  252. package/src/mcp/react/hooks/useToolMetadata.ts +0 -5
  253. package/src/mcp/react/hooks/useToolName.ts +0 -10
  254. package/src/mcp/react/index.ts +0 -7
  255. package/src/openai/core/McpAppManager.ts +0 -139
  256. package/src/openai/link/ToolCallLink.ts +0 -40
  257. package/src/openai/react/hooks/__tests__/createHydrationUtils.test.tsx +0 -1333
  258. package/src/openai/react/hooks/__tests__/useToolInfo.test.tsx +0 -92
  259. package/src/openai/react/hooks/__tests__/useToolInput.test.tsx +0 -85
  260. package/src/openai/react/hooks/__tests__/useToolMetadata.test.tsx +0 -86
  261. package/src/openai/react/hooks/__tests__/useToolName.test.tsx +0 -50
  262. package/src/openai/react/hooks/createHydrationUtils.ts +0 -182
  263. package/src/openai/react/hooks/useApp.ts +0 -5
  264. package/src/openai/react/hooks/useToolInfo.ts +0 -13
  265. package/src/openai/react/hooks/useToolInput.ts +0 -10
  266. package/src/openai/react/hooks/useToolMetadata.ts +0 -5
  267. package/src/openai/react/hooks/useToolName.ts +0 -10
  268. package/src/react/index.mcp.ts +0 -10
  269. package/src/react/index.openai.ts +0 -10
  270. package/src/react/missingHook.ts +0 -9
  271. /package/dist/{mcp/react → react}/hooks/useApp.d.ts +0 -0
  272. /package/dist/{mcp/react → react}/hooks/useHostContext.d.ts +0 -0
@@ -1,46 +0,0 @@
1
- import { test, expect } from "vitest";
2
- import {
3
- disableActEnvironment,
4
- renderHookToSnapshotStream,
5
- } from "@testing-library/react-render-stream";
6
- import { Suspense } from "react";
7
- import { InMemoryCache } from "@apollo/client";
8
- import { App } from "@modelcontextprotocol/ext-apps";
9
-
10
- import { useApp } from "../useApp.js";
11
- import { ApolloClient } from "../../../core/ApolloClient.js";
12
- import {
13
- mockApplicationManifest,
14
- mockMcpHost,
15
- spyOnConsole,
16
- } from "../../../../testing/internal/index.js";
17
- import { ApolloProvider } from "../../../../react/ApolloProvider.js";
18
-
19
- test("returns app instance created by ApolloClient", async () => {
20
- using _ = spyOnConsole("debug");
21
- const client = new ApolloClient({
22
- cache: new InMemoryCache(),
23
- manifest: mockApplicationManifest(),
24
- });
25
-
26
- using host = await mockMcpHost();
27
- host.onCleanup(() => client.stop());
28
-
29
- host.sendToolInput({ arguments: {} });
30
- host.sendToolResult({
31
- _meta: { toolName: "Test" },
32
- content: [],
33
- structuredContent: {},
34
- });
35
-
36
- using _disabledAct = disableActEnvironment();
37
- const { takeSnapshot } = await renderHookToSnapshotStream(() => useApp(), {
38
- wrapper: ({ children }) => (
39
- <Suspense>
40
- <ApolloProvider client={client}>{children}</ApolloProvider>
41
- </Suspense>
42
- ),
43
- });
44
-
45
- await expect(takeSnapshot()).resolves.toBeInstanceOf(App);
46
- });
@@ -1,95 +0,0 @@
1
- import { expect, test } from "vitest";
2
- import { InMemoryCache } from "@apollo/client";
3
- import {
4
- graphqlToolResult,
5
- minimalHostContextWithToolName,
6
- mockApplicationManifest,
7
- mockMcpHost,
8
- spyOnConsole,
9
- } from "../../../../testing/internal/index.js";
10
- import { ApolloClient } from "../../../core/ApolloClient.js";
11
- import {
12
- disableActEnvironment,
13
- renderHookToSnapshotStream,
14
- } from "@testing-library/react-render-stream";
15
- import { useHostContext } from "../useHostContext.js";
16
- import { ApolloProvider } from "../../../../react/ApolloProvider.js";
17
-
18
- test("returns the host context from the host", async () => {
19
- using _ = spyOnConsole("debug");
20
- const client = new ApolloClient({
21
- cache: new InMemoryCache(),
22
- manifest: mockApplicationManifest(),
23
- });
24
-
25
- using host = await mockMcpHost({
26
- hostContext: {
27
- ...minimalHostContextWithToolName("GetProduct"),
28
- theme: "light",
29
- },
30
- });
31
- host.onCleanup(() => client.stop());
32
-
33
- host.sendToolInput({ arguments: {} });
34
- host.sendToolResult(graphqlToolResult({ data: { product: null } }));
35
-
36
- using _disabledAct = disableActEnvironment();
37
- const { takeSnapshot } = await renderHookToSnapshotStream(
38
- () => useHostContext(),
39
- {
40
- wrapper: ({ children }) => (
41
- <ApolloProvider client={client}>{children}</ApolloProvider>
42
- ),
43
- }
44
- );
45
-
46
- await expect(takeSnapshot()).resolves.toStrictEqual({
47
- ...minimalHostContextWithToolName("GetProduct"),
48
- theme: "light",
49
- });
50
-
51
- await expect(takeSnapshot).not.toRerender();
52
- });
53
-
54
- test("rerenders when the host context changes", async () => {
55
- using _ = spyOnConsole("debug");
56
- const client = new ApolloClient({
57
- cache: new InMemoryCache(),
58
- manifest: mockApplicationManifest(),
59
- });
60
-
61
- using host = await mockMcpHost({
62
- hostContext: {
63
- ...minimalHostContextWithToolName("GetProduct"),
64
- theme: "light",
65
- },
66
- });
67
- host.onCleanup(() => client.stop());
68
-
69
- host.sendToolInput({ arguments: {} });
70
- host.sendToolResult(graphqlToolResult({ data: { product: null } }));
71
-
72
- using _disabledAct = disableActEnvironment();
73
- const { takeSnapshot } = await renderHookToSnapshotStream(
74
- () => useHostContext(),
75
- {
76
- wrapper: ({ children }) => (
77
- <ApolloProvider client={client}>{children}</ApolloProvider>
78
- ),
79
- }
80
- );
81
-
82
- await expect(takeSnapshot()).resolves.toStrictEqual({
83
- ...minimalHostContextWithToolName("GetProduct"),
84
- theme: "light",
85
- });
86
-
87
- host.sendHostContextChanged({ theme: "dark" });
88
-
89
- await expect(takeSnapshot()).resolves.toStrictEqual({
90
- ...minimalHostContextWithToolName("GetProduct"),
91
- theme: "dark",
92
- });
93
-
94
- await expect(takeSnapshot).not.toRerender();
95
- });
@@ -1,53 +0,0 @@
1
- import { test, expect } from "vitest";
2
- import {
3
- disableActEnvironment,
4
- renderHookToSnapshotStream,
5
- } from "@testing-library/react-render-stream";
6
- import { Suspense } from "react";
7
- import { InMemoryCache } from "@apollo/client";
8
-
9
- import { useToolInfo } from "../useToolInfo.js";
10
- import { ApolloClient } from "../../../core/ApolloClient.js";
11
- import {
12
- graphqlToolResult,
13
- minimalHostContextWithToolName,
14
- mockApplicationManifest,
15
- mockMcpHost,
16
- spyOnConsole,
17
- } from "../../../../testing/internal/index.js";
18
- import { ApolloProvider } from "../../../../react/ApolloProvider.js";
19
-
20
- test("returns tool name and input combined", async () => {
21
- using _ = spyOnConsole("debug");
22
- const client = new ApolloClient({
23
- cache: new InMemoryCache(),
24
- manifest: mockApplicationManifest(),
25
- });
26
-
27
- using host = await mockMcpHost({
28
- hostContext: minimalHostContextWithToolName("GetProduct"),
29
- });
30
- host.onCleanup(() => client.stop());
31
-
32
- host.sendToolInput({ arguments: { id: "1" } });
33
- host.sendToolResult(graphqlToolResult({ data: { product: null } }));
34
-
35
- using _disabledAct = disableActEnvironment();
36
- const { takeSnapshot } = await renderHookToSnapshotStream(
37
- () => useToolInfo(),
38
- {
39
- wrapper: ({ children }) => (
40
- <Suspense>
41
- <ApolloProvider client={client}>{children}</ApolloProvider>
42
- </Suspense>
43
- ),
44
- }
45
- );
46
-
47
- await expect(takeSnapshot()).resolves.toEqual({
48
- toolName: "GetProduct",
49
- toolInput: { id: "1" },
50
- });
51
-
52
- await expect(takeSnapshot).not.toRerender();
53
- });
@@ -1,50 +0,0 @@
1
- import { test, expect } from "vitest";
2
- import {
3
- disableActEnvironment,
4
- renderHookToSnapshotStream,
5
- } from "@testing-library/react-render-stream";
6
- import { Suspense } from "react";
7
- import { InMemoryCache } from "@apollo/client";
8
-
9
- import { useToolInput } from "../useToolInput.js";
10
- import { ApolloClient } from "../../../core/ApolloClient.js";
11
- import {
12
- graphqlToolResult,
13
- minimalHostContextWithToolName,
14
- mockApplicationManifest,
15
- mockMcpHost,
16
- spyOnConsole,
17
- } from "../../../../testing/internal/index.js";
18
- import { ApolloProvider } from "../../../../react/ApolloProvider.js";
19
-
20
- test("returns the tool input from the MCP host", async () => {
21
- using _ = spyOnConsole("debug");
22
- const client = new ApolloClient({
23
- cache: new InMemoryCache(),
24
- manifest: mockApplicationManifest(),
25
- });
26
-
27
- using host = await mockMcpHost({
28
- hostContext: minimalHostContextWithToolName("GetProduct"),
29
- });
30
- host.onCleanup(() => client.stop());
31
-
32
- host.sendToolInput({ arguments: { id: "1" } });
33
- host.sendToolResult(graphqlToolResult({ data: { product: null } }));
34
-
35
- using _disabledAct = disableActEnvironment();
36
- const { takeSnapshot } = await renderHookToSnapshotStream(
37
- () => useToolInput(),
38
- {
39
- wrapper: ({ children }) => (
40
- <Suspense>
41
- <ApolloProvider client={client}>{children}</ApolloProvider>
42
- </Suspense>
43
- ),
44
- }
45
- );
46
-
47
- await expect(takeSnapshot()).resolves.toEqual({ id: "1" });
48
-
49
- await expect(takeSnapshot).not.toRerender();
50
- });
@@ -1,53 +0,0 @@
1
- import { test, expect } from "vitest";
2
- import {
3
- disableActEnvironment,
4
- renderHookToSnapshotStream,
5
- } from "@testing-library/react-render-stream";
6
- import { Suspense } from "react";
7
- import { InMemoryCache } from "@apollo/client";
8
-
9
- import { useToolMetadata } from "../useToolMetadata.js";
10
- import { ApolloClient } from "../../../core/ApolloClient.js";
11
- import {
12
- mockApplicationManifest,
13
- mockMcpHost,
14
- spyOnConsole,
15
- } from "../../../../testing/internal/index.js";
16
- import { ApolloProvider } from "../../../../react/ApolloProvider.js";
17
-
18
- test("returns the tool metadata from the MCP host", async () => {
19
- using _ = spyOnConsole("debug");
20
- const client = new ApolloClient({
21
- cache: new InMemoryCache(),
22
- manifest: mockApplicationManifest(),
23
- });
24
-
25
- using host = await mockMcpHost();
26
- host.onCleanup(() => client.stop());
27
-
28
- host.sendToolInput({ arguments: {} });
29
- host.sendToolResult({
30
- _meta: { toolName: "TestTool", customField: "customValue" },
31
- content: [],
32
- structuredContent: {},
33
- });
34
-
35
- using _disabledAct = disableActEnvironment();
36
- const { takeSnapshot } = await renderHookToSnapshotStream(
37
- () => useToolMetadata(),
38
- {
39
- wrapper: ({ children }) => (
40
- <Suspense>
41
- <ApolloProvider client={client}>{children}</ApolloProvider>
42
- </Suspense>
43
- ),
44
- }
45
- );
46
-
47
- await expect(takeSnapshot()).resolves.toEqual({
48
- toolName: "TestTool",
49
- customField: "customValue",
50
- });
51
-
52
- await expect(takeSnapshot).not.toRerender();
53
- });
@@ -1,50 +0,0 @@
1
- import { test, expect } from "vitest";
2
- import {
3
- disableActEnvironment,
4
- renderHookToSnapshotStream,
5
- } from "@testing-library/react-render-stream";
6
- import { Suspense } from "react";
7
- import { InMemoryCache } from "@apollo/client";
8
-
9
- import { useToolName } from "../useToolName.js";
10
- import { ApolloClient } from "../../../core/ApolloClient.js";
11
- import {
12
- graphqlToolResult,
13
- minimalHostContextWithToolName,
14
- mockApplicationManifest,
15
- mockMcpHost,
16
- spyOnConsole,
17
- } from "../../../../testing/internal/index.js";
18
- import { ApolloProvider } from "../../../../react/ApolloProvider.js";
19
-
20
- test("returns the tool name from the MCP host", async () => {
21
- using _ = spyOnConsole("debug");
22
- const client = new ApolloClient({
23
- cache: new InMemoryCache(),
24
- manifest: mockApplicationManifest(),
25
- });
26
-
27
- using host = await mockMcpHost({
28
- hostContext: minimalHostContextWithToolName("GetProduct"),
29
- });
30
- host.onCleanup(() => client.stop());
31
-
32
- host.sendToolInput({ arguments: {} });
33
- host.sendToolResult(graphqlToolResult({ data: { product: null } }));
34
-
35
- using _disabledAct = disableActEnvironment();
36
- const { takeSnapshot } = await renderHookToSnapshotStream(
37
- () => useToolName(),
38
- {
39
- wrapper: ({ children }) => (
40
- <Suspense>
41
- <ApolloProvider client={client}>{children}</ApolloProvider>
42
- </Suspense>
43
- ),
44
- }
45
- );
46
-
47
- await expect(takeSnapshot()).resolves.toBe("GetProduct");
48
-
49
- await expect(takeSnapshot).not.toRerender();
50
- });
@@ -1,14 +0,0 @@
1
- import { useCallback, useSyncExternalStore } from "react";
2
- import { useApolloClient } from "./useApolloClient";
3
-
4
- export function useHostContext() {
5
- const appManager = useApolloClient()["appManager"];
6
-
7
- return useSyncExternalStore(
8
- useCallback(
9
- (update) => appManager.onHostContextChanged(update),
10
- [appManager]
11
- ),
12
- () => appManager.app.getHostContext()
13
- );
14
- }
@@ -1,13 +0,0 @@
1
- import type { ToolInfo } from "../../../core/typeRegistration.js";
2
- import { useApolloClient } from "./useApolloClient.js";
3
-
4
- export function useToolInfo(): ToolInfo | undefined {
5
- const appManager = useApolloClient()["appManager"];
6
- const toolName = appManager.toolName;
7
-
8
- if (!toolName) {
9
- return;
10
- }
11
-
12
- return { toolName, toolInput: appManager.toolInput };
13
- }
@@ -1,10 +0,0 @@
1
- import type { ToolInput } from "../../../core/typeRegistration.js";
2
- import { useApolloClient } from "./useApolloClient.js";
3
-
4
- /**
5
- * @deprecated Please use the `useToolInfo` hook. `useToolInput` will be removed
6
- * in the next major version.
7
- */
8
- export function useToolInput(): ToolInput | undefined {
9
- return useApolloClient()["appManager"].toolInput;
10
- }
@@ -1,5 +0,0 @@
1
- import { useApolloClient } from "./useApolloClient.js";
2
-
3
- export function useToolMetadata() {
4
- return useApolloClient()["appManager"].toolMetadata;
5
- }
@@ -1,10 +0,0 @@
1
- import type { ToolName } from "../../../core/typeRegistration.js";
2
- import { useApolloClient } from "./useApolloClient.js";
3
-
4
- /**
5
- * @deprecated Please use the `useToolInfo` hook. `useToolName` will be removed
6
- * in the next major version.
7
- */
8
- export function useToolName(): ToolName | undefined {
9
- return useApolloClient()["appManager"].toolName;
10
- }
@@ -1,7 +0,0 @@
1
- export { useApp } from "./hooks/useApp.js";
2
- export { useHostContext } from "./hooks/useHostContext.js";
3
- export { useToolName } from "./hooks/useToolName.js";
4
- export { useToolMetadata } from "./hooks/useToolMetadata.js";
5
- export { useToolInput } from "./hooks/useToolInput.js";
6
- export { useToolInfo } from "./hooks/useToolInfo.js";
7
- export { createHydrationUtils } from "./hooks/createHydrationUtils.js";
@@ -1,139 +0,0 @@
1
- import {
2
- App,
3
- PostMessageTransport,
4
- type McpUiHostContextChangedNotification,
5
- } from "@modelcontextprotocol/ext-apps";
6
- import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
7
- import type { ApplicationManifest } from "../../types/application-manifest";
8
- import type { FormattedExecutionResult } from "graphql";
9
- import type { DocumentNode, OperationVariables } from "@apollo/client";
10
- import { print } from "@apollo/client/utilities";
11
- import { cacheAsync, promiseWithResolvers } from "../../utilities";
12
- import type { ApolloMcpServerApps } from "../../core/types";
13
-
14
- type ExecuteQueryCallToolResult = Omit<CallToolResult, "structuredContent"> & {
15
- structuredContent: FormattedExecutionResult;
16
- };
17
-
18
- /** @internal */
19
- export class McpAppManager {
20
- readonly app: App;
21
-
22
- #toolName: string | undefined;
23
- #toolMetadata: Record<string, unknown> | null = null;
24
- #toolInput: Record<string, unknown> | undefined;
25
-
26
- #hostContextCallbacks = new Set<
27
- (params: McpUiHostContextChangedNotification["params"]) => void
28
- >();
29
-
30
- constructor(manifest: ApplicationManifest) {
31
- this.app = new App({ name: manifest.name, version: manifest.appVersion });
32
- }
33
-
34
- get toolName() {
35
- return this.#toolName;
36
- }
37
-
38
- get toolMetadata() {
39
- return this.#toolMetadata;
40
- }
41
-
42
- get toolInput() {
43
- return this.#toolInput;
44
- }
45
-
46
- onHostContextChanged(
47
- cb: (params: McpUiHostContextChangedNotification["params"]) => void
48
- ) {
49
- this.#hostContextCallbacks.add(cb);
50
- return () => {
51
- this.#hostContextCallbacks.delete(cb);
52
- };
53
- }
54
-
55
- connect = cacheAsync(async () => {
56
- let toolResult = promiseWithResolvers<ApolloMcpServerApps.CallToolResult>();
57
-
58
- this.app.ontoolresult = (params) => {
59
- toolResult.resolve(
60
- params as unknown as ApolloMcpServerApps.CallToolResult
61
- );
62
- };
63
-
64
- this.app.onhostcontextchanged = (params) => {
65
- this.#hostContextCallbacks.forEach((cb) => cb(params));
66
- };
67
-
68
- await this.connectToHost();
69
-
70
- // After a page refresh, OpenAI does not re-send `ui/notifications/tool-result`.
71
- // Instead, the tool result is available immediately via `window.openai.toolOutput`.
72
- // If it's already set, resolve the promise now rather than waiting for the
73
- // notification that will never arrive.
74
- if (window.openai.toolOutput !== null) {
75
- toolResult.resolve({
76
- structuredContent: window.openai.toolOutput,
77
- content: [],
78
- });
79
- }
80
-
81
- const { structuredContent } = await toolResult.promise;
82
-
83
- this.#toolName = this.app.getHostContext()?.toolInfo?.tool.name;
84
-
85
- // OpenAI is not consistent about sending `ui/notifications/tool-input`.
86
- // Sometimes it doesn't send at all, other times it sends more than once
87
- // before we get the tool result (which should always happen and at most
88
- // once according to the spec). Rather than relying on the
89
- // `ui/notifications/tool-input` notification to set the tool input value,
90
- // we read from `window.openai.toolInput so that we have the most recent
91
- // set value.
92
- //
93
- // When OpenAI fixes this issue and sends `ui/notifications/tool-input`
94
- // consistently according to the MCP Apps specification, this can be
95
- // reverted to use the `app.ontoolinput` callback.
96
- this.#toolInput = window.openai.toolInput;
97
-
98
- // OpenAI doesn't provide access to `_meta`, so we need to use
99
- // window.openai.toolResponseMetadata directly
100
- this.#toolMetadata = window.openai.toolResponseMetadata;
101
-
102
- return {
103
- ...structuredContent,
104
- toolName: this.toolName,
105
- args: this.toolInput,
106
- };
107
- });
108
-
109
- close() {
110
- return this.app.close();
111
- }
112
-
113
- async executeQuery({
114
- query,
115
- variables,
116
- }: {
117
- query: DocumentNode;
118
- variables: OperationVariables | undefined;
119
- }) {
120
- const result = (await this.app.callServerTool({
121
- name: "execute",
122
- arguments: { query: print(query), variables },
123
- })) as ExecuteQueryCallToolResult;
124
-
125
- return result.structuredContent;
126
- }
127
-
128
- private async connectToHost() {
129
- try {
130
- return await this.app.connect(
131
- new PostMessageTransport(window.parent, window.parent)
132
- );
133
- } catch (e) {
134
- const error = e instanceof Error ? e : new Error("Failed to connect");
135
-
136
- throw error;
137
- }
138
- }
139
- }
@@ -1,40 +0,0 @@
1
- import { ApolloLink, Observable } from "@apollo/client";
2
- import { from } from "rxjs";
3
- import type { ApolloClient as OpenAiApolloClient } from "../core/ApolloClient";
4
-
5
- /**
6
- * A terminating link that sends a GraphQL request through an agent tool call.
7
- * When providing a custom link chain to `ApolloClient`, `ApolloClient` will
8
- * validate that the terminating link is an instance of this link.
9
- *
10
- * @example Providing a custom link chain
11
- *
12
- * ```ts
13
- * import { ApolloLink } from "@apollo/client";
14
- * import { ApolloClient, ToolCallLink } from "@apollo/client-ai-apps";
15
- *
16
- * const link = ApolloLink.from([
17
- * ...otherLinks,
18
- * new ToolCallLink()
19
- * ]);
20
- *
21
- * const client = new ApolloClient({
22
- * link,
23
- * // ...
24
- * });
25
- * ```
26
- */
27
- export class ToolCallLink extends ApolloLink {
28
- readonly name = "ToolCallLink";
29
-
30
- request(operation: ApolloLink.Operation): Observable<ApolloLink.Result> {
31
- const client = operation.client as OpenAiApolloClient;
32
-
33
- return from(
34
- client["appManager"].executeQuery({
35
- query: operation.query,
36
- variables: operation.variables,
37
- })
38
- );
39
- }
40
- }