@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.
- package/CHANGELOG.md +100 -0
- package/CONTRIBUTING.md +195 -0
- package/README.md +74 -0
- package/dist/core/AbstractApolloClient.d.ts +33 -0
- package/dist/core/AbstractApolloClient.d.ts.map +1 -0
- package/dist/core/AbstractApolloClient.js +129 -0
- package/dist/core/AbstractApolloClient.js.map +1 -0
- package/dist/core/ApolloClient.d.ts +3 -7
- package/dist/core/ApolloClient.d.ts.map +1 -1
- package/dist/core/ApolloClient.js +5 -4
- package/dist/core/ApolloClient.js.map +1 -1
- package/dist/core/McpAppManager.d.ts +42 -0
- package/dist/core/McpAppManager.d.ts.map +1 -0
- package/dist/core/McpAppManager.js +56 -0
- package/dist/core/McpAppManager.js.map +1 -0
- package/dist/core/typeRegistration.d.ts +0 -14
- package/dist/core/typeRegistration.d.ts.map +1 -1
- package/dist/core/typeRegistration.js.map +1 -1
- package/dist/core/types.d.ts +2 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mcp.d.ts +0 -1
- package/dist/index.mcp.d.ts.map +1 -1
- package/dist/index.mcp.js +0 -1
- package/dist/index.mcp.js.map +1 -1
- package/dist/index.openai.d.ts +0 -1
- package/dist/index.openai.d.ts.map +1 -1
- package/dist/index.openai.js +0 -1
- package/dist/index.openai.js.map +1 -1
- package/dist/link/ToolCallLink.d.ts +6 -1
- package/dist/link/ToolCallLink.d.ts.map +1 -1
- package/dist/link/ToolCallLink.js +17 -4
- package/dist/link/ToolCallLink.js.map +1 -1
- package/dist/link/ToolHydrationLink.d.ts +21 -0
- package/dist/link/ToolHydrationLink.d.ts.map +1 -0
- package/dist/link/ToolHydrationLink.js +57 -0
- package/dist/link/ToolHydrationLink.js.map +1 -0
- package/dist/mcp/core/ApolloClient.d.ts +3 -20
- package/dist/mcp/core/ApolloClient.d.ts.map +1 -1
- package/dist/mcp/core/ApolloClient.js +20 -98
- package/dist/mcp/core/ApolloClient.js.map +1 -1
- package/dist/mcp/index.d.ts +0 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +0 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/openai/core/ApolloClient.d.ts +3 -20
- package/dist/openai/core/ApolloClient.d.ts.map +1 -1
- package/dist/openai/core/ApolloClient.js +36 -98
- package/dist/openai/core/ApolloClient.js.map +1 -1
- package/dist/openai/index.d.ts +0 -1
- package/dist/openai/index.d.ts.map +1 -1
- package/dist/openai/index.js +0 -1
- package/dist/openai/index.js.map +1 -1
- package/dist/openai/react/index.d.ts +0 -7
- package/dist/openai/react/index.d.ts.map +1 -1
- package/dist/openai/react/index.js +0 -7
- package/dist/openai/react/index.js.map +1 -1
- package/dist/react/ApolloProvider.d.ts.map +1 -1
- package/dist/react/ApolloProvider.js +1 -1
- package/dist/react/ApolloProvider.js.map +1 -1
- package/dist/{mcp/react/hooks → react}/createHydrationUtils.d.ts +1 -1
- package/dist/react/createHydrationUtils.d.ts.map +1 -0
- package/dist/{mcp/react/hooks → react}/createHydrationUtils.js +7 -9
- package/dist/react/createHydrationUtils.js.map +1 -0
- package/dist/react/hooks/internal/useApolloClient.d.ts +3 -0
- package/dist/react/hooks/internal/useApolloClient.d.ts.map +1 -0
- package/dist/{mcp/react/hooks → react/hooks/internal}/useApolloClient.js +3 -3
- package/dist/react/hooks/internal/useApolloClient.js.map +1 -0
- package/dist/react/hooks/useApp.d.ts.map +1 -0
- package/dist/react/hooks/useApp.js +5 -0
- package/dist/react/hooks/useApp.js.map +1 -0
- package/dist/react/hooks/useHostContext.d.ts.map +1 -0
- package/dist/{openai/react → react}/hooks/useHostContext.js +1 -1
- package/dist/react/hooks/useHostContext.js.map +1 -0
- package/dist/react/hooks/useToolInfo.d.ts +3 -0
- package/dist/react/hooks/useToolInfo.d.ts.map +1 -0
- package/dist/react/hooks/useToolInfo.js +5 -0
- package/dist/react/hooks/useToolInfo.js.map +1 -0
- package/dist/react/hooks/useToolMetadata.d.ts +2 -0
- package/dist/react/hooks/useToolMetadata.d.ts.map +1 -0
- package/dist/react/hooks/useToolMetadata.js +5 -0
- package/dist/react/hooks/useToolMetadata.js.map +1 -0
- package/dist/react/index.d.ts +5 -16
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +5 -19
- package/dist/react/index.js.map +1 -1
- package/dist/types/application-manifest.d.ts +1 -0
- package/dist/types/application-manifest.d.ts.map +1 -1
- package/dist/types/application-manifest.js.map +1 -1
- package/dist/utilities/connectToHost.d.ts +3 -0
- package/dist/utilities/connectToHost.d.ts.map +1 -0
- package/dist/utilities/connectToHost.js +11 -0
- package/dist/utilities/connectToHost.js.map +1 -0
- package/dist/utilities/index.d.ts +1 -0
- package/dist/utilities/index.d.ts.map +1 -1
- package/dist/utilities/index.js +1 -0
- package/dist/utilities/index.js.map +1 -1
- package/dist/vite/apolloClientAiApps.d.ts.map +1 -1
- package/dist/vite/apolloClientAiApps.js +2 -0
- package/dist/vite/apolloClientAiApps.js.map +1 -1
- package/package.json +5 -22
- package/src/core/AbstractApolloClient.ts +217 -0
- package/src/core/ApolloClient.ts +8 -10
- package/src/core/McpAppManager.ts +106 -0
- package/src/core/typeRegistration.ts +0 -15
- package/src/core/types.ts +2 -1
- package/src/index.mcp.ts +0 -1
- package/src/index.openai.ts +0 -1
- package/src/index.ts +1 -6
- package/src/link/ToolCallLink.ts +27 -5
- package/src/link/ToolHydrationLink.ts +90 -0
- package/src/link/__tests__/ToolCallLink.test.ts +99 -0
- package/src/mcp/core/ApolloClient.ts +32 -165
- package/src/mcp/core/__tests__/ApolloClient.test.ts +571 -71
- package/src/mcp/index.ts +0 -1
- package/src/openai/core/ApolloClient.ts +48 -161
- package/src/openai/core/__tests__/ApolloClient.test.ts +916 -118
- package/src/openai/index.ts +0 -1
- package/src/openai/react/index.ts +0 -7
- package/src/react/ApolloProvider.tsx +1 -6
- package/src/react/__tests__/ApolloProvider/mcp.test.tsx +66 -29
- package/src/react/__tests__/ApolloProvider/openai.test.tsx +16 -41
- package/src/react/__tests__/createHydrationUtils.test.tsx +1260 -0
- package/src/{mcp/react/hooks → react}/createHydrationUtils.ts +7 -10
- package/src/react/hooks/__tests__/useApp.test.tsx +46 -0
- package/src/react/hooks/__tests__/useHostContext.test.tsx +99 -0
- package/src/react/hooks/__tests__/useToolInfo.test.tsx +98 -0
- package/src/react/hooks/__tests__/useToolMetadata.test.tsx +58 -0
- package/src/{mcp/react/hooks → react/hooks/internal}/useApolloClient.ts +3 -3
- package/src/{mcp/react → react}/hooks/useApp.ts +1 -1
- package/src/{openai/react → react}/hooks/useHostContext.ts +1 -1
- package/src/react/hooks/useToolInfo.ts +6 -0
- package/src/react/hooks/useToolMetadata.ts +5 -0
- package/src/react/index.ts +5 -36
- package/src/testing/internal/graphql/parseManifestOperation.ts +87 -0
- package/src/testing/internal/index.ts +3 -0
- package/src/testing/internal/matchers/index.ts +1 -0
- package/src/testing/internal/matchers/toEmitAnything.ts +43 -0
- package/src/testing/internal/matchers/types.ts +1 -0
- package/src/testing/internal/mcp/mockMcpHost.ts +25 -4
- package/src/testing/internal/tests/eachHostEnv.ts +22 -0
- package/src/testing/internal/utilities/createHostEnv.ts +117 -0
- package/src/types/application-manifest.ts +1 -0
- package/src/utilities/connectToHost.ts +13 -0
- package/src/utilities/index.ts +1 -0
- package/src/vite/__tests__/apolloClientAiApps.test.ts +56 -0
- package/src/vite/apolloClientAiApps.ts +5 -0
- package/tsconfig.vite.json +1 -1
- package/vitest.config.ts +13 -0
- package/dist/mcp/core/McpAppManager.d.ts +0 -30
- package/dist/mcp/core/McpAppManager.d.ts.map +0 -1
- package/dist/mcp/core/McpAppManager.js +0 -82
- package/dist/mcp/core/McpAppManager.js.map +0 -1
- package/dist/mcp/link/ToolCallLink.d.ts +0 -28
- package/dist/mcp/link/ToolCallLink.d.ts.map +0 -1
- package/dist/mcp/link/ToolCallLink.js +0 -35
- package/dist/mcp/link/ToolCallLink.js.map +0 -1
- package/dist/mcp/react/hooks/createHydrationUtils.d.ts.map +0 -1
- package/dist/mcp/react/hooks/createHydrationUtils.js.map +0 -1
- package/dist/mcp/react/hooks/useApolloClient.d.ts +0 -3
- package/dist/mcp/react/hooks/useApolloClient.d.ts.map +0 -1
- package/dist/mcp/react/hooks/useApolloClient.js.map +0 -1
- package/dist/mcp/react/hooks/useApp.d.ts.map +0 -1
- package/dist/mcp/react/hooks/useApp.js +0 -5
- package/dist/mcp/react/hooks/useApp.js.map +0 -1
- package/dist/mcp/react/hooks/useHostContext.d.ts.map +0 -1
- package/dist/mcp/react/hooks/useHostContext.js +0 -7
- package/dist/mcp/react/hooks/useHostContext.js.map +0 -1
- package/dist/mcp/react/hooks/useToolInfo.d.ts +0 -3
- package/dist/mcp/react/hooks/useToolInfo.d.ts.map +0 -1
- package/dist/mcp/react/hooks/useToolInfo.js +0 -10
- package/dist/mcp/react/hooks/useToolInfo.js.map +0 -1
- package/dist/mcp/react/hooks/useToolInput.d.ts +0 -7
- package/dist/mcp/react/hooks/useToolInput.d.ts.map +0 -1
- package/dist/mcp/react/hooks/useToolInput.js +0 -9
- package/dist/mcp/react/hooks/useToolInput.js.map +0 -1
- package/dist/mcp/react/hooks/useToolMetadata.d.ts +0 -2
- package/dist/mcp/react/hooks/useToolMetadata.d.ts.map +0 -1
- package/dist/mcp/react/hooks/useToolMetadata.js +0 -5
- package/dist/mcp/react/hooks/useToolMetadata.js.map +0 -1
- package/dist/mcp/react/hooks/useToolName.d.ts +0 -7
- package/dist/mcp/react/hooks/useToolName.d.ts.map +0 -1
- package/dist/mcp/react/hooks/useToolName.js +0 -9
- package/dist/mcp/react/hooks/useToolName.js.map +0 -1
- package/dist/mcp/react/index.d.ts +0 -8
- package/dist/mcp/react/index.d.ts.map +0 -1
- package/dist/mcp/react/index.js +0 -8
- package/dist/mcp/react/index.js.map +0 -1
- package/dist/openai/core/McpAppManager.d.ts +0 -29
- package/dist/openai/core/McpAppManager.d.ts.map +0 -1
- package/dist/openai/core/McpAppManager.js +0 -91
- package/dist/openai/core/McpAppManager.js.map +0 -1
- package/dist/openai/link/ToolCallLink.d.ts +0 -28
- package/dist/openai/link/ToolCallLink.d.ts.map +0 -1
- package/dist/openai/link/ToolCallLink.js +0 -35
- package/dist/openai/link/ToolCallLink.js.map +0 -1
- package/dist/openai/react/hooks/createHydrationUtils.d.ts +0 -15
- package/dist/openai/react/hooks/createHydrationUtils.d.ts.map +0 -1
- package/dist/openai/react/hooks/createHydrationUtils.js +0 -113
- package/dist/openai/react/hooks/createHydrationUtils.js.map +0 -1
- package/dist/openai/react/hooks/useApp.d.ts +0 -2
- package/dist/openai/react/hooks/useApp.d.ts.map +0 -1
- package/dist/openai/react/hooks/useApp.js +0 -5
- package/dist/openai/react/hooks/useApp.js.map +0 -1
- package/dist/openai/react/hooks/useHostContext.d.ts +0 -2
- package/dist/openai/react/hooks/useHostContext.d.ts.map +0 -1
- package/dist/openai/react/hooks/useHostContext.js.map +0 -1
- package/dist/openai/react/hooks/useToolInfo.d.ts +0 -3
- package/dist/openai/react/hooks/useToolInfo.d.ts.map +0 -1
- package/dist/openai/react/hooks/useToolInfo.js +0 -10
- package/dist/openai/react/hooks/useToolInfo.js.map +0 -1
- package/dist/openai/react/hooks/useToolInput.d.ts +0 -7
- package/dist/openai/react/hooks/useToolInput.d.ts.map +0 -1
- package/dist/openai/react/hooks/useToolInput.js +0 -9
- package/dist/openai/react/hooks/useToolInput.js.map +0 -1
- package/dist/openai/react/hooks/useToolMetadata.d.ts +0 -2
- package/dist/openai/react/hooks/useToolMetadata.d.ts.map +0 -1
- package/dist/openai/react/hooks/useToolMetadata.js +0 -5
- package/dist/openai/react/hooks/useToolMetadata.js.map +0 -1
- package/dist/openai/react/hooks/useToolName.d.ts +0 -7
- package/dist/openai/react/hooks/useToolName.d.ts.map +0 -1
- package/dist/openai/react/hooks/useToolName.js +0 -9
- package/dist/openai/react/hooks/useToolName.js.map +0 -1
- package/dist/react/index.mcp.d.ts +0 -3
- package/dist/react/index.mcp.d.ts.map +0 -1
- package/dist/react/index.mcp.js +0 -3
- package/dist/react/index.mcp.js.map +0 -1
- package/dist/react/index.openai.d.ts +0 -3
- package/dist/react/index.openai.d.ts.map +0 -1
- package/dist/react/index.openai.js +0 -3
- package/dist/react/index.openai.js.map +0 -1
- package/dist/react/missingHook.d.ts +0 -2
- package/dist/react/missingHook.d.ts.map +0 -1
- package/dist/react/missingHook.js +0 -6
- package/dist/react/missingHook.js.map +0 -1
- package/src/mcp/core/McpAppManager.ts +0 -129
- package/src/mcp/link/ToolCallLink.ts +0 -40
- package/src/mcp/link/__tests__/ToolCallLink.test.ts +0 -62
- package/src/mcp/react/hooks/__tests__/createHydrationUtils.test.tsx +0 -1228
- package/src/mcp/react/hooks/__tests__/useApp.test.tsx +0 -46
- package/src/mcp/react/hooks/__tests__/useHostContext.test.tsx +0 -95
- package/src/mcp/react/hooks/__tests__/useToolInfo.test.tsx +0 -53
- package/src/mcp/react/hooks/__tests__/useToolInput.test.tsx +0 -50
- package/src/mcp/react/hooks/__tests__/useToolMetadata.test.tsx +0 -53
- package/src/mcp/react/hooks/__tests__/useToolName.test.tsx +0 -50
- package/src/mcp/react/hooks/useHostContext.ts +0 -14
- package/src/mcp/react/hooks/useToolInfo.ts +0 -13
- package/src/mcp/react/hooks/useToolInput.ts +0 -10
- package/src/mcp/react/hooks/useToolMetadata.ts +0 -5
- package/src/mcp/react/hooks/useToolName.ts +0 -10
- package/src/mcp/react/index.ts +0 -7
- package/src/openai/core/McpAppManager.ts +0 -139
- package/src/openai/link/ToolCallLink.ts +0 -40
- package/src/openai/react/hooks/__tests__/createHydrationUtils.test.tsx +0 -1333
- package/src/openai/react/hooks/__tests__/useToolInfo.test.tsx +0 -92
- package/src/openai/react/hooks/__tests__/useToolInput.test.tsx +0 -85
- package/src/openai/react/hooks/__tests__/useToolMetadata.test.tsx +0 -86
- package/src/openai/react/hooks/__tests__/useToolName.test.tsx +0 -50
- package/src/openai/react/hooks/createHydrationUtils.ts +0 -182
- package/src/openai/react/hooks/useApp.ts +0 -5
- package/src/openai/react/hooks/useToolInfo.ts +0 -13
- package/src/openai/react/hooks/useToolInput.ts +0 -10
- package/src/openai/react/hooks/useToolMetadata.ts +0 -5
- package/src/openai/react/hooks/useToolName.ts +0 -10
- package/src/react/index.mcp.ts +0 -10
- package/src/react/index.openai.ts +0 -10
- package/src/react/missingHook.ts +0 -9
- /package/dist/{mcp/react → react}/hooks/useApp.d.ts +0 -0
- /package/dist/{mcp/react → react}/hooks/useHostContext.d.ts +0 -0
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import { expect, test, vi, describe } from "vitest";
|
|
2
2
|
import { ApolloClient } from "../ApolloClient.js";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ApolloLink,
|
|
5
|
+
HttpLink,
|
|
6
|
+
InMemoryCache,
|
|
7
|
+
NetworkStatus,
|
|
8
|
+
gql,
|
|
9
|
+
type DocumentNode,
|
|
10
|
+
} from "@apollo/client";
|
|
4
11
|
import { print } from "@apollo/client/utilities";
|
|
5
|
-
import { ToolCallLink } from "
|
|
12
|
+
import { ToolCallLink } from "../../../link/ToolCallLink.js";
|
|
6
13
|
import {
|
|
7
14
|
graphqlToolResult,
|
|
8
15
|
minimalHostContextWithToolName,
|
|
9
16
|
mockApplicationManifest,
|
|
10
17
|
mockMcpHost,
|
|
18
|
+
ObservableStream,
|
|
19
|
+
parseManifestOperation,
|
|
11
20
|
spyOnConsole,
|
|
12
21
|
} from "../../../testing/internal/index.js";
|
|
13
22
|
|
|
@@ -15,7 +24,8 @@ test("writes tool result data to cache", async () => {
|
|
|
15
24
|
using _ = spyOnConsole("debug");
|
|
16
25
|
|
|
17
26
|
const query = gql`
|
|
18
|
-
query Product($id: ID!)
|
|
27
|
+
query Product($id: ID!)
|
|
28
|
+
@tool(name: "GetProduct", description: "Get a product") {
|
|
19
29
|
product(id: $id) {
|
|
20
30
|
id
|
|
21
31
|
title
|
|
@@ -24,27 +34,8 @@ test("writes tool result data to cache", async () => {
|
|
|
24
34
|
}
|
|
25
35
|
`;
|
|
26
36
|
|
|
27
|
-
const client =
|
|
28
|
-
|
|
29
|
-
manifest: mockApplicationManifest({
|
|
30
|
-
operations: [
|
|
31
|
-
{
|
|
32
|
-
id: "1",
|
|
33
|
-
name: "Product",
|
|
34
|
-
body: print(query),
|
|
35
|
-
type: "query",
|
|
36
|
-
prefetch: false,
|
|
37
|
-
variables: { id: "ID" },
|
|
38
|
-
tools: [{ name: "GetProduct", description: "Get a product" }],
|
|
39
|
-
},
|
|
40
|
-
],
|
|
41
|
-
}),
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
using host = await mockMcpHost({
|
|
45
|
-
hostContext: minimalHostContextWithToolName("GetProduct"),
|
|
46
|
-
});
|
|
47
|
-
host.onCleanup(() => client.stop());
|
|
37
|
+
const { client, host } = await setup({ query });
|
|
38
|
+
using _host = host;
|
|
48
39
|
|
|
49
40
|
host.sendToolResult({
|
|
50
41
|
content: [],
|
|
@@ -60,6 +51,12 @@ test("writes tool result data to cache", async () => {
|
|
|
60
51
|
|
|
61
52
|
await client.connect();
|
|
62
53
|
|
|
54
|
+
await expect(
|
|
55
|
+
client.query({ query, variables: { id: "1" } })
|
|
56
|
+
).resolves.toStrictEqual({
|
|
57
|
+
data: { product: { id: "1", title: "Pen", __typename: "Product" } },
|
|
58
|
+
});
|
|
59
|
+
|
|
63
60
|
expect(client.extract()).toEqual({
|
|
64
61
|
"Product:1": {
|
|
65
62
|
__typename: "Product",
|
|
@@ -140,11 +137,11 @@ test("writes prefetch data to cache", async () => {
|
|
|
140
137
|
});
|
|
141
138
|
});
|
|
142
139
|
|
|
143
|
-
test("writes prefetch
|
|
140
|
+
test("writes prefetch response data to cache when both are provided", async () => {
|
|
144
141
|
using _ = spyOnConsole("debug");
|
|
145
142
|
|
|
146
143
|
const prefetchQuery = gql`
|
|
147
|
-
query TopProducts {
|
|
144
|
+
query TopProducts @tool(description: "Shows top products") @prefetch {
|
|
148
145
|
topProducts {
|
|
149
146
|
id
|
|
150
147
|
title
|
|
@@ -154,7 +151,7 @@ test("writes prefetch and tool response data to cache when both are provided", a
|
|
|
154
151
|
`;
|
|
155
152
|
|
|
156
153
|
const query = gql`
|
|
157
|
-
query Product($id: ID!) {
|
|
154
|
+
query Product($id: ID!) @tool(description: "Get a product by id") {
|
|
158
155
|
product(id: $id) {
|
|
159
156
|
id
|
|
160
157
|
title
|
|
@@ -167,25 +164,8 @@ test("writes prefetch and tool response data to cache when both are provided", a
|
|
|
167
164
|
cache: new InMemoryCache(),
|
|
168
165
|
manifest: mockApplicationManifest({
|
|
169
166
|
operations: [
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
name: "TopProducts",
|
|
173
|
-
body: print(prefetchQuery),
|
|
174
|
-
type: "query",
|
|
175
|
-
prefetch: true,
|
|
176
|
-
prefetchID: "__anonymous",
|
|
177
|
-
variables: {},
|
|
178
|
-
tools: [{ name: "TopProducts", description: "Shows top products" }],
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
id: "2",
|
|
182
|
-
name: "Product",
|
|
183
|
-
body: print(query),
|
|
184
|
-
type: "query",
|
|
185
|
-
prefetch: false,
|
|
186
|
-
variables: { id: "ID" },
|
|
187
|
-
tools: [{ name: "Product", description: "Get a product by id" }],
|
|
188
|
-
},
|
|
167
|
+
parseManifestOperation(prefetchQuery),
|
|
168
|
+
parseManifestOperation(query),
|
|
189
169
|
],
|
|
190
170
|
}),
|
|
191
171
|
});
|
|
@@ -196,7 +176,6 @@ test("writes prefetch and tool response data to cache when both are provided", a
|
|
|
196
176
|
host.onCleanup(() => client.stop());
|
|
197
177
|
|
|
198
178
|
host.sendToolResult({
|
|
199
|
-
content: [],
|
|
200
179
|
structuredContent: {
|
|
201
180
|
prefetch: {
|
|
202
181
|
__anonymous: {
|
|
@@ -216,6 +195,12 @@ test("writes prefetch and tool response data to cache when both are provided", a
|
|
|
216
195
|
|
|
217
196
|
await client.connect();
|
|
218
197
|
|
|
198
|
+
await expect(
|
|
199
|
+
client.query({ query, variables: { id: "2" } })
|
|
200
|
+
).resolves.toStrictEqual({
|
|
201
|
+
data: { product: { __typename: "Product", id: "2", title: "iPad" } },
|
|
202
|
+
});
|
|
203
|
+
|
|
219
204
|
expect(client.extract()).toEqual({
|
|
220
205
|
"Product:1": {
|
|
221
206
|
__typename: "Product",
|
|
@@ -239,7 +224,8 @@ test("excludes extra tool input variables not defined in the operation", async (
|
|
|
239
224
|
using _ = spyOnConsole("debug");
|
|
240
225
|
|
|
241
226
|
const query = gql`
|
|
242
|
-
query Product($id: ID!)
|
|
227
|
+
query Product($id: ID!)
|
|
228
|
+
@tool(name: "GetProduct", description: "Get a product") {
|
|
243
229
|
product(id: $id) {
|
|
244
230
|
id
|
|
245
231
|
title
|
|
@@ -248,30 +234,10 @@ test("excludes extra tool input variables not defined in the operation", async (
|
|
|
248
234
|
}
|
|
249
235
|
`;
|
|
250
236
|
|
|
251
|
-
const client =
|
|
252
|
-
|
|
253
|
-
manifest: mockApplicationManifest({
|
|
254
|
-
operations: [
|
|
255
|
-
{
|
|
256
|
-
id: "1",
|
|
257
|
-
name: "Product",
|
|
258
|
-
body: print(query),
|
|
259
|
-
type: "query",
|
|
260
|
-
prefetch: false,
|
|
261
|
-
variables: { id: "ID" },
|
|
262
|
-
tools: [{ name: "GetProduct", description: "Get a product" }],
|
|
263
|
-
},
|
|
264
|
-
],
|
|
265
|
-
}),
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
using host = await mockMcpHost({
|
|
269
|
-
hostContext: minimalHostContextWithToolName("GetProduct"),
|
|
270
|
-
});
|
|
271
|
-
host.onCleanup(() => client.stop());
|
|
237
|
+
const { client, host } = await setup({ query });
|
|
238
|
+
using _host = host;
|
|
272
239
|
|
|
273
240
|
host.sendToolResult({
|
|
274
|
-
content: [],
|
|
275
241
|
structuredContent: {
|
|
276
242
|
result: {
|
|
277
243
|
data: {
|
|
@@ -284,7 +250,13 @@ test("excludes extra tool input variables not defined in the operation", async (
|
|
|
284
250
|
|
|
285
251
|
await client.connect();
|
|
286
252
|
|
|
287
|
-
expect(
|
|
253
|
+
await expect(
|
|
254
|
+
client.query({ query, variables: { id: "1" } })
|
|
255
|
+
).resolves.toStrictEqual({
|
|
256
|
+
data: { product: { id: "1", title: "Pen", __typename: "Product" } },
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
expect(client.extract()).toStrictEqual({
|
|
288
260
|
"Product:1": {
|
|
289
261
|
__typename: "Product",
|
|
290
262
|
id: "1",
|
|
@@ -464,6 +436,511 @@ test("creates a default ToolCallLink when no link is provided", () => {
|
|
|
464
436
|
}).not.toThrow();
|
|
465
437
|
});
|
|
466
438
|
|
|
439
|
+
test("reads result data from _meta.structuredContent", async () => {
|
|
440
|
+
using _ = spyOnConsole("debug");
|
|
441
|
+
|
|
442
|
+
const query = gql`
|
|
443
|
+
query Product($id: ID!)
|
|
444
|
+
@tool(name: "GetProduct", description: "Get a product") {
|
|
445
|
+
product(id: $id) @private {
|
|
446
|
+
id
|
|
447
|
+
title
|
|
448
|
+
__typename
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
`;
|
|
452
|
+
|
|
453
|
+
const { client, host } = await setup({ query });
|
|
454
|
+
using _host = host;
|
|
455
|
+
|
|
456
|
+
host.sendToolResult({
|
|
457
|
+
structuredContent: {},
|
|
458
|
+
_meta: {
|
|
459
|
+
toolName: "GetProduct",
|
|
460
|
+
structuredContent: {
|
|
461
|
+
result: {
|
|
462
|
+
data: {
|
|
463
|
+
product: { id: "1", title: "Pen", __typename: "Product" },
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
});
|
|
469
|
+
host.sendToolInput({ arguments: { id: "1" } });
|
|
470
|
+
|
|
471
|
+
await client.connect();
|
|
472
|
+
|
|
473
|
+
await expect(
|
|
474
|
+
client.query({ query, variables: { id: "1" } })
|
|
475
|
+
).resolves.toStrictEqual({
|
|
476
|
+
data: { product: { id: "1", title: "Pen", __typename: "Product" } },
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
expect(client.extract()).toEqual({
|
|
480
|
+
"Product:1": {
|
|
481
|
+
__typename: "Product",
|
|
482
|
+
id: "1",
|
|
483
|
+
title: "Pen",
|
|
484
|
+
},
|
|
485
|
+
ROOT_QUERY: {
|
|
486
|
+
__typename: "Query",
|
|
487
|
+
'product({"id":"1"})@private': {
|
|
488
|
+
__ref: "Product:1",
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
test("merges prefetch from structuredContent and result from _meta.structuredContent", async () => {
|
|
495
|
+
using _ = spyOnConsole("debug");
|
|
496
|
+
|
|
497
|
+
const prefetchQuery = gql`
|
|
498
|
+
query TopProducts @tool(description: "Shows top products") @prefetch {
|
|
499
|
+
topProducts {
|
|
500
|
+
id
|
|
501
|
+
title
|
|
502
|
+
__typename
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
`;
|
|
506
|
+
|
|
507
|
+
const query = gql`
|
|
508
|
+
query Product($id: ID!)
|
|
509
|
+
@tool(name: "GetProduct", description: "Get a product") {
|
|
510
|
+
product(id: $id) @private {
|
|
511
|
+
id
|
|
512
|
+
title
|
|
513
|
+
__typename
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
`;
|
|
517
|
+
|
|
518
|
+
const client = new ApolloClient({
|
|
519
|
+
cache: new InMemoryCache(),
|
|
520
|
+
manifest: mockApplicationManifest({
|
|
521
|
+
operations: [
|
|
522
|
+
parseManifestOperation(prefetchQuery),
|
|
523
|
+
parseManifestOperation(query),
|
|
524
|
+
],
|
|
525
|
+
}),
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
using host = await mockMcpHost({
|
|
529
|
+
hostContext: minimalHostContextWithToolName("GetProduct"),
|
|
530
|
+
});
|
|
531
|
+
host.onCleanup(() => client.stop());
|
|
532
|
+
|
|
533
|
+
host.sendToolResult({
|
|
534
|
+
structuredContent: {
|
|
535
|
+
prefetch: {
|
|
536
|
+
__anonymous: {
|
|
537
|
+
data: {
|
|
538
|
+
topProducts: [{ id: "1", title: "iPhone", __typename: "Product" }],
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
_meta: {
|
|
544
|
+
toolName: "GetProduct",
|
|
545
|
+
structuredContent: {
|
|
546
|
+
result: {
|
|
547
|
+
data: {
|
|
548
|
+
product: { id: "2", title: "iPad", __typename: "Product" },
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
},
|
|
553
|
+
});
|
|
554
|
+
host.sendToolInput({ arguments: { id: "2" } });
|
|
555
|
+
|
|
556
|
+
await client.connect();
|
|
557
|
+
|
|
558
|
+
await expect(
|
|
559
|
+
client.query({ query, variables: { id: "2" } })
|
|
560
|
+
).resolves.toStrictEqual({
|
|
561
|
+
data: { product: { id: "2", title: "iPad", __typename: "Product" } },
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
expect(client.extract()).toStrictEqual({
|
|
565
|
+
"Product:1": {
|
|
566
|
+
__typename: "Product",
|
|
567
|
+
id: "1",
|
|
568
|
+
title: "iPhone",
|
|
569
|
+
},
|
|
570
|
+
"Product:2": {
|
|
571
|
+
__typename: "Product",
|
|
572
|
+
id: "2",
|
|
573
|
+
title: "iPad",
|
|
574
|
+
},
|
|
575
|
+
ROOT_QUERY: {
|
|
576
|
+
__typename: "Query",
|
|
577
|
+
topProducts: [{ __ref: "Product:1" }],
|
|
578
|
+
'product({"id":"2"})@private': { __ref: "Product:2" },
|
|
579
|
+
},
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
test("_meta.structuredContent wins over structuredContent", async () => {
|
|
584
|
+
using _ = spyOnConsole("debug");
|
|
585
|
+
|
|
586
|
+
const query = gql`
|
|
587
|
+
query Product($id: ID!) {
|
|
588
|
+
product(id: $id) @tool(name: "GetProduct", description: "Get a product") {
|
|
589
|
+
id
|
|
590
|
+
title @private
|
|
591
|
+
__typename
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
`;
|
|
595
|
+
|
|
596
|
+
const { client, host } = await setup({ query });
|
|
597
|
+
using _host = host;
|
|
598
|
+
|
|
599
|
+
host.sendToolResult({
|
|
600
|
+
structuredContent: {
|
|
601
|
+
result: {
|
|
602
|
+
data: {
|
|
603
|
+
product: { id: "1", __typename: "Product" },
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
},
|
|
607
|
+
_meta: {
|
|
608
|
+
toolName: "GetProduct",
|
|
609
|
+
structuredContent: {
|
|
610
|
+
result: {
|
|
611
|
+
data: {
|
|
612
|
+
product: { id: "1", title: "Meta title", __typename: "Product" },
|
|
613
|
+
},
|
|
614
|
+
},
|
|
615
|
+
},
|
|
616
|
+
},
|
|
617
|
+
});
|
|
618
|
+
host.sendToolInput({ arguments: { id: "1" } });
|
|
619
|
+
|
|
620
|
+
await client.connect();
|
|
621
|
+
|
|
622
|
+
await expect(
|
|
623
|
+
client.query({ query, variables: { id: "1" } })
|
|
624
|
+
).resolves.toStrictEqual({
|
|
625
|
+
data: { product: { id: "1", title: "Meta title", __typename: "Product" } },
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
expect(client.extract()).toEqual({
|
|
629
|
+
"Product:1": {
|
|
630
|
+
__typename: "Product",
|
|
631
|
+
id: "1",
|
|
632
|
+
"title@private": "Meta title",
|
|
633
|
+
},
|
|
634
|
+
ROOT_QUERY: {
|
|
635
|
+
__typename: "Query",
|
|
636
|
+
'product({"id":"1"})': {
|
|
637
|
+
__ref: "Product:1",
|
|
638
|
+
},
|
|
639
|
+
},
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
test("serves tool result data on network-only query without calling execute tool", async () => {
|
|
644
|
+
using _ = spyOnConsole("debug");
|
|
645
|
+
const query = gql`
|
|
646
|
+
query Product($id: ID!)
|
|
647
|
+
@tool(name: "GetProduct", description: "Get a product") {
|
|
648
|
+
product(id: $id) {
|
|
649
|
+
id
|
|
650
|
+
title
|
|
651
|
+
__typename
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
`;
|
|
655
|
+
|
|
656
|
+
const data = {
|
|
657
|
+
product: { id: "1", title: "Pen", __typename: "Product" },
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
const { client, host } = await setup({ query });
|
|
661
|
+
using _host = host;
|
|
662
|
+
|
|
663
|
+
const execute = vi.fn();
|
|
664
|
+
host.mockToolCall("execute", execute);
|
|
665
|
+
|
|
666
|
+
host.sendToolResult({ structuredContent: { result: { data } } });
|
|
667
|
+
host.sendToolInput({ arguments: { id: "1" } });
|
|
668
|
+
|
|
669
|
+
await client.connect();
|
|
670
|
+
|
|
671
|
+
await expect(
|
|
672
|
+
client.query({
|
|
673
|
+
query,
|
|
674
|
+
variables: { id: "1" },
|
|
675
|
+
fetchPolicy: "network-only",
|
|
676
|
+
})
|
|
677
|
+
).resolves.toStrictEqual({ data });
|
|
678
|
+
expect(execute).not.toHaveBeenCalled();
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
test("calls execute tool on second network-only query after hydration is consumed", async () => {
|
|
682
|
+
using _ = spyOnConsole("debug");
|
|
683
|
+
const query = gql`
|
|
684
|
+
query Product($id: ID!)
|
|
685
|
+
@tool(name: "GetProduct", description: "Get a product") {
|
|
686
|
+
product(id: $id) {
|
|
687
|
+
id
|
|
688
|
+
title
|
|
689
|
+
__typename
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
`;
|
|
693
|
+
|
|
694
|
+
const { client, host } = await setup({ query });
|
|
695
|
+
using _host = host;
|
|
696
|
+
|
|
697
|
+
host.mockToolCall("execute", () => ({
|
|
698
|
+
structuredContent: {
|
|
699
|
+
data: {
|
|
700
|
+
product: { id: "1", title: "Updated Pen", __typename: "Product" },
|
|
701
|
+
},
|
|
702
|
+
},
|
|
703
|
+
}));
|
|
704
|
+
|
|
705
|
+
host.sendToolResult({
|
|
706
|
+
structuredContent: {
|
|
707
|
+
result: {
|
|
708
|
+
data: {
|
|
709
|
+
product: { id: "1", title: "Pen", __typename: "Product" },
|
|
710
|
+
},
|
|
711
|
+
},
|
|
712
|
+
},
|
|
713
|
+
});
|
|
714
|
+
host.sendToolInput({ arguments: { id: "1" } });
|
|
715
|
+
|
|
716
|
+
await client.connect();
|
|
717
|
+
|
|
718
|
+
await client.query({
|
|
719
|
+
query,
|
|
720
|
+
variables: { id: "1" },
|
|
721
|
+
fetchPolicy: "network-only",
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
await expect(
|
|
725
|
+
client.query({
|
|
726
|
+
query,
|
|
727
|
+
variables: { id: "1" },
|
|
728
|
+
fetchPolicy: "network-only",
|
|
729
|
+
})
|
|
730
|
+
).resolves.toStrictEqual({
|
|
731
|
+
data: {
|
|
732
|
+
product: { id: "1", title: "Updated Pen", __typename: "Product" },
|
|
733
|
+
},
|
|
734
|
+
});
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
test("serves tool result data on cache-and-network query without calling execute tool", async () => {
|
|
738
|
+
using _ = spyOnConsole("debug");
|
|
739
|
+
const query = gql`
|
|
740
|
+
query Product($id: ID!)
|
|
741
|
+
@tool(name: "GetProduct", description: "Get a product") {
|
|
742
|
+
product(id: $id) {
|
|
743
|
+
id
|
|
744
|
+
title
|
|
745
|
+
__typename
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
`;
|
|
749
|
+
|
|
750
|
+
const { client, host } = await setup({ query });
|
|
751
|
+
using _host = host;
|
|
752
|
+
|
|
753
|
+
const execute = vi.fn();
|
|
754
|
+
host.mockToolCall("execute", execute);
|
|
755
|
+
|
|
756
|
+
host.sendToolResult({
|
|
757
|
+
structuredContent: {
|
|
758
|
+
result: {
|
|
759
|
+
data: {
|
|
760
|
+
product: { id: "1", title: "Pen", __typename: "Product" },
|
|
761
|
+
},
|
|
762
|
+
},
|
|
763
|
+
},
|
|
764
|
+
});
|
|
765
|
+
host.sendToolInput({ arguments: { id: "1" } });
|
|
766
|
+
|
|
767
|
+
await client.connect();
|
|
768
|
+
|
|
769
|
+
const stream = new ObservableStream(
|
|
770
|
+
client.watchQuery({
|
|
771
|
+
query,
|
|
772
|
+
variables: { id: "1" },
|
|
773
|
+
fetchPolicy: "cache-and-network",
|
|
774
|
+
})
|
|
775
|
+
);
|
|
776
|
+
|
|
777
|
+
// The hydrated result is emitted synchronously so we won't observe a loading
|
|
778
|
+
// state like we normally would with `cache-and-network`
|
|
779
|
+
await expect(stream).toEmitValue({
|
|
780
|
+
data: { product: { id: "1", title: "Pen", __typename: "Product" } },
|
|
781
|
+
dataState: "complete",
|
|
782
|
+
loading: false,
|
|
783
|
+
networkStatus: NetworkStatus.ready,
|
|
784
|
+
partial: false,
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
await expect(stream).not.toEmitAnything();
|
|
788
|
+
|
|
789
|
+
expect(execute).not.toHaveBeenCalled();
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
test("serves tool result data on no-cache query without calling execute tool and does not write to cache", async () => {
|
|
793
|
+
using _ = spyOnConsole("debug");
|
|
794
|
+
const query = gql`
|
|
795
|
+
query Product($id: ID!)
|
|
796
|
+
@tool(name: "GetProduct", description: "Get a product") {
|
|
797
|
+
product(id: $id) {
|
|
798
|
+
id
|
|
799
|
+
title
|
|
800
|
+
__typename
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
`;
|
|
804
|
+
|
|
805
|
+
const data = {
|
|
806
|
+
product: { id: "1", title: "Pen", __typename: "Product" },
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
const { client, host } = await setup({ query });
|
|
810
|
+
using _host = host;
|
|
811
|
+
|
|
812
|
+
const execute = vi.fn();
|
|
813
|
+
host.mockToolCall("execute", execute);
|
|
814
|
+
|
|
815
|
+
host.sendToolInput({ arguments: { id: "1" } });
|
|
816
|
+
host.sendToolResult({ structuredContent: { result: { data } } });
|
|
817
|
+
|
|
818
|
+
await client.connect();
|
|
819
|
+
|
|
820
|
+
await expect(
|
|
821
|
+
client.query({ query, variables: { id: "1" }, fetchPolicy: "no-cache" })
|
|
822
|
+
).resolves.toStrictEqual({ data });
|
|
823
|
+
expect(execute).not.toHaveBeenCalled();
|
|
824
|
+
expect(client.extract()).toStrictEqual({});
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
test("serves hydrated query from tool result while other pending network-only queries call execute", async () => {
|
|
828
|
+
using _ = spyOnConsole("debug");
|
|
829
|
+
|
|
830
|
+
const productQuery = gql`
|
|
831
|
+
query Product($id: ID!)
|
|
832
|
+
@tool(name: "GetProduct", description: "Get a product") {
|
|
833
|
+
product(id: $id) {
|
|
834
|
+
id
|
|
835
|
+
title
|
|
836
|
+
__typename
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
`;
|
|
840
|
+
|
|
841
|
+
const cartQuery = gql`
|
|
842
|
+
query Cart @tool(name: "GetCart", description: "Get the cart") {
|
|
843
|
+
cart {
|
|
844
|
+
id
|
|
845
|
+
__typename
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
`;
|
|
849
|
+
|
|
850
|
+
const productOperation = parseManifestOperation(productQuery);
|
|
851
|
+
const cartOperation = parseManifestOperation(cartQuery);
|
|
852
|
+
|
|
853
|
+
const client = new ApolloClient({
|
|
854
|
+
cache: new InMemoryCache(),
|
|
855
|
+
manifest: mockApplicationManifest({
|
|
856
|
+
operations: [productOperation, cartOperation],
|
|
857
|
+
}),
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
using host = await mockMcpHost({
|
|
861
|
+
hostContext: minimalHostContextWithToolName("GetProduct"),
|
|
862
|
+
});
|
|
863
|
+
host.onCleanup(() => client.stop());
|
|
864
|
+
|
|
865
|
+
const execute = vi.fn(() => ({
|
|
866
|
+
structuredContent: {
|
|
867
|
+
data: { cart: { id: "1", __typename: "Cart" } },
|
|
868
|
+
},
|
|
869
|
+
}));
|
|
870
|
+
host.mockToolCall("execute", execute);
|
|
871
|
+
host.sendToolResult({
|
|
872
|
+
structuredContent: {
|
|
873
|
+
result: {
|
|
874
|
+
data: { product: { id: "1", title: "Pen", __typename: "Product" } },
|
|
875
|
+
},
|
|
876
|
+
},
|
|
877
|
+
});
|
|
878
|
+
host.sendToolInput({ arguments: { id: "1" } });
|
|
879
|
+
|
|
880
|
+
await client.connect();
|
|
881
|
+
|
|
882
|
+
const productPromise = client.query({
|
|
883
|
+
query: productQuery,
|
|
884
|
+
variables: { id: "1" },
|
|
885
|
+
fetchPolicy: "network-only",
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
const cartPromise = client.query({
|
|
889
|
+
query: cartQuery,
|
|
890
|
+
fetchPolicy: "network-only",
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
await expect(productPromise).resolves.toStrictEqual({
|
|
894
|
+
data: { product: { id: "1", title: "Pen", __typename: "Product" } },
|
|
895
|
+
});
|
|
896
|
+
await expect(cartPromise).resolves.toStrictEqual({
|
|
897
|
+
data: { cart: { id: "1", __typename: "Cart" } },
|
|
898
|
+
});
|
|
899
|
+
expect(execute).toHaveBeenCalledOnce();
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
test("hydrates prefetch query with network-only fetch policy", async () => {
|
|
903
|
+
using _ = spyOnConsole("debug");
|
|
904
|
+
|
|
905
|
+
const query = gql`
|
|
906
|
+
query TopProducts @tool(description: "Shows top products") @prefetch {
|
|
907
|
+
topProducts {
|
|
908
|
+
id
|
|
909
|
+
title
|
|
910
|
+
__typename
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
`;
|
|
914
|
+
|
|
915
|
+
const data = {
|
|
916
|
+
topProducts: [{ id: "1", title: "iPhone", __typename: "Product" }],
|
|
917
|
+
};
|
|
918
|
+
|
|
919
|
+
const { client, host } = await setup({ query, toolName: "OtherTool" });
|
|
920
|
+
using _host = host;
|
|
921
|
+
|
|
922
|
+
const execute = vi.fn();
|
|
923
|
+
host.mockToolCall("execute", execute);
|
|
924
|
+
|
|
925
|
+
host.sendToolResult({
|
|
926
|
+
structuredContent: {
|
|
927
|
+
prefetch: { __anonymous: { data } },
|
|
928
|
+
},
|
|
929
|
+
});
|
|
930
|
+
host.sendToolInput({ arguments: {} });
|
|
931
|
+
|
|
932
|
+
await client.connect();
|
|
933
|
+
|
|
934
|
+
await expect(
|
|
935
|
+
client.query({ query, fetchPolicy: "network-only" })
|
|
936
|
+
).resolves.toStrictEqual({
|
|
937
|
+
data: {
|
|
938
|
+
topProducts: [{ id: "1", title: "iPhone", __typename: "Product" }],
|
|
939
|
+
},
|
|
940
|
+
});
|
|
941
|
+
expect(execute).not.toHaveBeenCalled();
|
|
942
|
+
});
|
|
943
|
+
|
|
467
944
|
describe("watchQuery dev warnings", () => {
|
|
468
945
|
const query = gql`
|
|
469
946
|
query Products($category: String!, $page: Int!, $sortBy: String!)
|
|
@@ -565,3 +1042,26 @@ describe("watchQuery dev warnings", () => {
|
|
|
565
1042
|
expect(console.warn).toHaveBeenCalledTimes(1);
|
|
566
1043
|
});
|
|
567
1044
|
});
|
|
1045
|
+
|
|
1046
|
+
async function setup({
|
|
1047
|
+
query,
|
|
1048
|
+
toolName,
|
|
1049
|
+
}: {
|
|
1050
|
+
query: DocumentNode;
|
|
1051
|
+
toolName?: string;
|
|
1052
|
+
}) {
|
|
1053
|
+
const operation = parseManifestOperation(query);
|
|
1054
|
+
const client = new ApolloClient({
|
|
1055
|
+
cache: new InMemoryCache(),
|
|
1056
|
+
manifest: mockApplicationManifest({ operations: [operation] }),
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
const host = await mockMcpHost({
|
|
1060
|
+
hostContext: minimalHostContextWithToolName(
|
|
1061
|
+
toolName ?? operation.tools[0].name
|
|
1062
|
+
),
|
|
1063
|
+
});
|
|
1064
|
+
host.onCleanup(() => client.stop());
|
|
1065
|
+
|
|
1066
|
+
return { client, host };
|
|
1067
|
+
}
|