@apollo/client-ai-apps 0.7.2 → 0.7.3

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 (36) hide show
  1. package/.github/workflows/pr.yaml +35 -0
  2. package/.github/workflows/prep-release.yml +4 -3
  3. package/.github/workflows/release.yaml +8 -5
  4. package/.github/workflows/verify-changeset.yml +4 -4
  5. package/CHANGELOG.md +8 -0
  6. package/dist/utilities/getToolNamesFromDocument.d.ts +1 -1
  7. package/dist/utilities/getToolNamesFromDocument.d.ts.map +1 -1
  8. package/dist/utilities/getToolNamesFromDocument.js +14 -5
  9. package/dist/utilities/getToolNamesFromDocument.js.map +1 -1
  10. package/integration-tests/docker-compose.yml +30 -0
  11. package/integration-tests/global-teardown.js +9 -0
  12. package/integration-tests/graphql-server/Dockerfile +12 -0
  13. package/integration-tests/graphql-server/package.json +10 -0
  14. package/integration-tests/graphql-server/server.ts +22 -0
  15. package/integration-tests/mcp-config.yaml +29 -0
  16. package/integration-tests/mock-app/index.html +12 -0
  17. package/integration-tests/mock-app/package.json +24 -0
  18. package/integration-tests/mock-app/src/App.tsx +23 -0
  19. package/integration-tests/mock-app/src/main.tsx +22 -0
  20. package/integration-tests/mock-app/src/tools/Echo.tsx +33 -0
  21. package/integration-tests/mock-app/src/tools/Hello.tsx +19 -0
  22. package/integration-tests/mock-app/src/tools/Private.tsx +34 -0
  23. package/integration-tests/mock-app/src/tools/SemiPrivate.tsx +34 -0
  24. package/integration-tests/mock-app/tsconfig.json +25 -0
  25. package/integration-tests/mock-app/vite.config.ts +22 -0
  26. package/integration-tests/package-lock.json +5749 -0
  27. package/integration-tests/package.json +20 -0
  28. package/integration-tests/playwright.config.ts +23 -0
  29. package/integration-tests/schema.graphql +10 -0
  30. package/integration-tests/tests/hello.spec.ts +11 -0
  31. package/integration-tests/tests/privateDirective.spec.ts +73 -0
  32. package/integration-tests/tests/variables.spec.ts +24 -0
  33. package/package.json +3 -2
  34. package/src/react/__tests__/createHydrationUtils.test.tsx +62 -0
  35. package/src/utilities/getToolNamesFromDocument.ts +19 -7
  36. package/vitest.config.ts +2 -1
@@ -80,6 +80,41 @@ jobs:
80
80
  shell: bash
81
81
  run: npm test -- --coverage
82
82
 
83
+ integration-tests:
84
+ runs-on: ubuntu-latest
85
+
86
+ steps:
87
+ - name: Checkout code
88
+ uses: actions/checkout@v4
89
+
90
+ - name: Set up Node.js
91
+ uses: actions/setup-node@v4
92
+ with:
93
+ node-version: 24.x
94
+
95
+ - name: Install root dependencies
96
+ run: npm ci
97
+
98
+ - name: Install integration tests dependencies
99
+ run: npm ci
100
+ working-directory: integration-tests
101
+
102
+ - name: Install Playwright browsers
103
+ run: npx playwright install --with-deps chromium
104
+ working-directory: integration-tests
105
+
106
+ - name: Run integration tests
107
+ run: npm test
108
+ working-directory: integration-tests
109
+
110
+ - name: Upload Playwright report
111
+ if: ${{ !cancelled() }}
112
+ uses: actions/upload-artifact@v4
113
+ with:
114
+ name: playwright-report
115
+ path: integration-tests/playwright-report
116
+ retention-days: 7
117
+
83
118
  arethetypeswrong:
84
119
  name: Are the types wrong
85
120
  runs-on: ubuntu-latest
@@ -12,13 +12,14 @@ jobs:
12
12
  prepare-release:
13
13
  runs-on: ubuntu-latest
14
14
  steps:
15
- - uses: actions/checkout@v6
15
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
16
16
 
17
17
  # We need to setup node because knope runs `npm run format` so we need to install prettier via npm
18
18
  - name: Set up Node.js
19
- uses: actions/setup-node@v4
19
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
20
20
  with:
21
21
  node-version: 24.x
22
+ package-manager-cache: false
22
23
 
23
24
  - name: Install dependencies
24
25
  run: npm ci
@@ -28,7 +29,7 @@ jobs:
28
29
  git config --global user.name GitHub Actions
29
30
  git config user.email github-actions@github.com
30
31
 
31
- - uses: knope-dev/action@v2.1.0
32
+ - uses: knope-dev/action@19617851f9f13ab2f27a05989c55efb18aca3675 # v2.1.2
32
33
  with:
33
34
  version: 0.22.0
34
35
 
@@ -17,13 +17,16 @@ jobs:
17
17
  if: github.repository == 'apollographql/apollo-client-ai-apps' && (github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/'))
18
18
  runs-on: ubuntu-latest
19
19
  steps:
20
- - uses: actions/checkout@v6
20
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
21
+
22
+ - name: Set up Node.js
23
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
21
24
 
22
- - name: Setup Node.js 20.x
23
- uses: actions/setup-node@v4
24
25
  with:
25
- node-version: ">=23.6.0"
26
+ node-version: 24.x
26
27
  registry-url: "https://registry.npmjs.org/"
28
+ package-manager-cache: false
29
+
27
30
  env:
28
31
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
29
32
 
@@ -40,7 +43,7 @@ jobs:
40
43
  git config --global user.name GitHub Actions
41
44
  git config user.email github-actions@github.com
42
45
 
43
- - uses: knope-dev/action@v2.1.0
46
+ - uses: knope-dev/action@19617851f9f13ab2f27a05989c55efb18aca3675 # v2.1.2
44
47
  with:
45
48
  version: 0.22.0
46
49
 
@@ -18,7 +18,7 @@ jobs:
18
18
  contents: read
19
19
  steps:
20
20
  - name: Verify changeset included
21
- uses: actions/github-script@v7
21
+ uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
22
22
  with:
23
23
  script: |
24
24
  const dir = '.changeset/';
@@ -38,7 +38,7 @@ jobs:
38
38
  }
39
39
  core.setOutput('ok', ok ? 'true' : 'false');
40
40
  - name: Add or update changeset comment on failure
41
- uses: actions/github-script@v7
41
+ uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
42
42
  if: failure()
43
43
  with:
44
44
  script: |
@@ -75,7 +75,7 @@ jobs:
75
75
  }
76
76
 
77
77
  - name: Add or update changeset comment on success
78
- uses: actions/github-script@v7
78
+ uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
79
79
  if: success()
80
80
  with:
81
81
  script: |
@@ -116,7 +116,7 @@ jobs:
116
116
  pull-requests: write
117
117
  steps:
118
118
  - name: Add or update comment to show skipped
119
- uses: actions/github-script@v7
119
+ uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
120
120
  with:
121
121
  script: |
122
122
  const pr = context.payload.pull_request;
package/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 0.7.3 (2026-05-13)
2
+
3
+ ### Fixes
4
+
5
+ #### Fix tool match when `@tool` doesn't use arguments
6
+
7
+ Fix an issue when using `@tool` without arguments where `useHydratedVariables` doesn't match against the tool correctly which causes mismatches in intended query result.
8
+
1
9
  ## 0.7.2 (2026-04-17)
2
10
 
3
11
  ### Fixes
@@ -1,3 +1,3 @@
1
1
  import { type DocumentNode } from "graphql";
2
- export declare function getToolNamesFromDocument(document: DocumentNode): Set<string>;
2
+ export declare function getToolNamesFromDocument(document: DocumentNode): Set<string | undefined>;
3
3
  //# sourceMappingURL=getToolNamesFromDocument.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getToolNamesFromDocument.d.ts","sourceRoot":"","sources":["../../src/utilities/getToolNamesFromDocument.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAElD,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,YAAY,eAW9D"}
1
+ {"version":3,"file":"getToolNamesFromDocument.d.ts","sourceRoot":"","sources":["../../src/utilities/getToolNamesFromDocument.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAEtE,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,YAAY,2BAkB9D"}
@@ -2,11 +2,20 @@ import { getOperationDefinition } from "@apollo/client/utilities/internal";
2
2
  import { Kind } from "graphql";
3
3
  export function getToolNamesFromDocument(document) {
4
4
  const operationDef = getOperationDefinition(document);
5
- return new Set(operationDef?.directives
6
- ?.filter((d) => d.name.value === "tool")
7
- .flatMap((d) => {
8
- const nameArg = d.arguments?.find((arg) => arg.name.value === "name");
9
- return nameArg?.value.kind === Kind.STRING ? [nameArg.value.value] : [];
5
+ const operationName = operationDef?.name?.value;
6
+ const toolDirectives = operationDef?.directives?.filter((d) => d.name.value === "tool") ?? [];
7
+ if (toolDirectives.length === 1) {
8
+ return new Set([
9
+ getToolNameFromDirective(toolDirectives[0]) ?? operationName,
10
+ ]);
11
+ }
12
+ return new Set(toolDirectives.flatMap((d) => {
13
+ const name = getToolNameFromDirective(d);
14
+ return name ? [name] : [];
10
15
  }));
11
16
  }
17
+ function getToolNameFromDirective(directive) {
18
+ const nameArg = directive.arguments?.find((arg) => arg.name.value === "name");
19
+ return nameArg?.value.kind === Kind.STRING ? nameArg.value.value : undefined;
20
+ }
12
21
  //# sourceMappingURL=getToolNamesFromDocument.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"getToolNamesFromDocument.js","sourceRoot":"","sources":["../../src/utilities/getToolNamesFromDocument.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,IAAI,EAAqB,MAAM,SAAS,CAAC;AAElD,MAAM,UAAU,wBAAwB,CAAC,QAAsB;IAC7D,MAAM,YAAY,GAAG,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IAEtD,OAAO,IAAI,GAAG,CACZ,YAAY,EAAE,UAAU;QACtB,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,KAAK,MAAM,CAAC;SACvC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;QACb,MAAM,OAAO,GAAG,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC;QACtE,OAAO,OAAO,EAAE,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,CAAC,CAAC,CACL,CAAC;AACJ,CAAC","sourcesContent":["import { getOperationDefinition } from \"@apollo/client/utilities/internal\";\nimport { Kind, type DocumentNode } from \"graphql\";\n\nexport function getToolNamesFromDocument(document: DocumentNode) {\n const operationDef = getOperationDefinition(document);\n\n return new Set(\n operationDef?.directives\n ?.filter((d) => d.name.value === \"tool\")\n .flatMap((d) => {\n const nameArg = d.arguments?.find((arg) => arg.name.value === \"name\");\n return nameArg?.value.kind === Kind.STRING ? [nameArg.value.value] : [];\n })\n );\n}\n"]}
1
+ {"version":3,"file":"getToolNamesFromDocument.js","sourceRoot":"","sources":["../../src/utilities/getToolNamesFromDocument.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,IAAI,EAAyC,MAAM,SAAS,CAAC;AAEtE,MAAM,UAAU,wBAAwB,CAAC,QAAsB;IAC7D,MAAM,YAAY,GAAG,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACtD,MAAM,aAAa,GAAG,YAAY,EAAE,IAAI,EAAE,KAAK,CAAC;IAChD,MAAM,cAAc,GAClB,YAAY,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC;IAEzE,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,IAAI,GAAG,CAAC;YACb,wBAAwB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,aAAa;SAC7D,CAAC,CAAC;IACL,CAAC;IAED,OAAO,IAAI,GAAG,CACZ,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;QAC3B,MAAM,IAAI,GAAG,wBAAwB,CAAC,CAAC,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5B,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAAC,SAAwB;IACxD,MAAM,OAAO,GAAG,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC;IAC9E,OAAO,OAAO,EAAE,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/E,CAAC","sourcesContent":["import { getOperationDefinition } from \"@apollo/client/utilities/internal\";\nimport { Kind, type DirectiveNode, type DocumentNode } from \"graphql\";\n\nexport function getToolNamesFromDocument(document: DocumentNode) {\n const operationDef = getOperationDefinition(document);\n const operationName = operationDef?.name?.value;\n const toolDirectives =\n operationDef?.directives?.filter((d) => d.name.value === \"tool\") ?? [];\n\n if (toolDirectives.length === 1) {\n return new Set([\n getToolNameFromDirective(toolDirectives[0]) ?? operationName,\n ]);\n }\n\n return new Set(\n toolDirectives.flatMap((d) => {\n const name = getToolNameFromDirective(d);\n return name ? [name] : [];\n })\n );\n}\n\nfunction getToolNameFromDirective(directive: DirectiveNode) {\n const nameArg = directive.arguments?.find((arg) => arg.name.value === \"name\");\n return nameArg?.value.kind === Kind.STRING ? nameArg.value.value : undefined;\n}\n"]}
@@ -0,0 +1,30 @@
1
+ services:
2
+ graphql:
3
+ build: ./graphql-server
4
+ ports:
5
+ - "4000:4000"
6
+ volumes:
7
+ - ./schema.graphql:/data/schema.graphql
8
+ healthcheck:
9
+ test:
10
+ [
11
+ "CMD-SHELL",
12
+ 'node -e "fetch(''http://localhost:4000'', {method: ''POST'', headers: {''content-type'': ''application/json''}, body: JSON.stringify({query: ''{__typename}''})}).then(r => r.ok ? process.exit(0) : process.exit(1)).catch(() => process.exit(1))"',
13
+ ]
14
+ interval: 5s
15
+ timeout: 3s
16
+ retries: 5
17
+ start_period: 10s
18
+
19
+ mcp-server:
20
+ image: ghcr.io/apollographql/apollo-mcp-server:latest
21
+ ports:
22
+ - "8000:8000"
23
+ volumes:
24
+ - ./mcp-config.yaml:/config.yaml
25
+ - ./schema.graphql:/data/schema.graphql
26
+ - ./apps:/data/apps
27
+ command: ["/config.yaml"]
28
+ depends_on:
29
+ graphql:
30
+ condition: service_healthy
@@ -0,0 +1,9 @@
1
+ import { execSync } from "child_process";
2
+ import { dirname } from "path";
3
+ import { fileURLToPath } from "url";
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+
7
+ export default function globalTeardown() {
8
+ execSync("docker compose down", { cwd: __dirname });
9
+ }
@@ -0,0 +1,12 @@
1
+ FROM node:22-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY package.json ./
6
+ RUN npm install
7
+
8
+ COPY server.ts ./
9
+
10
+ EXPOSE 4000
11
+
12
+ CMD ["node_modules/.bin/tsx", "server.ts"]
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "integration-tests-graphql-server",
3
+ "private": true,
4
+ "type": "module",
5
+ "dependencies": {
6
+ "@apollo/server": "^5.5.0",
7
+ "graphql": "^16.12.0",
8
+ "tsx": "^4.21.0"
9
+ }
10
+ }
@@ -0,0 +1,22 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { ApolloServer } from "@apollo/server";
3
+ import { startStandaloneServer } from "@apollo/server/standalone";
4
+
5
+ const typeDefs = readFileSync("/data/schema.graphql", "utf-8");
6
+
7
+ const resolvers = {
8
+ Query: {
9
+ hello: () => "Hello, world!",
10
+ echo: (_: unknown, { message }: { message: string }) =>
11
+ `${message} (${message})`,
12
+ user: () => ({ address: "1234 Main St", fullName: "MCP User" }),
13
+ },
14
+ };
15
+
16
+ const server = new ApolloServer({ typeDefs, resolvers });
17
+
18
+ const { url } = await startStandaloneServer(server, {
19
+ listen: { port: 4000, host: "0.0.0.0" },
20
+ });
21
+
22
+ console.log(`GraphQL server ready at ${url}`);
@@ -0,0 +1,29 @@
1
+ endpoint: http://graphql:4000/
2
+
3
+ schema:
4
+ source: local
5
+ path: /data/schema.graphql
6
+
7
+ introspection:
8
+ introspect:
9
+ enabled: true
10
+ execute:
11
+ enabled: true
12
+
13
+ transport:
14
+ type: streamable_http
15
+ port: 8000
16
+
17
+ health_check:
18
+ enabled: true
19
+ path: /health
20
+
21
+ cors:
22
+ enabled: true
23
+ allow_any_origin: true
24
+
25
+ logging:
26
+ level: info
27
+
28
+ overrides:
29
+ mutation_mode: all
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Mock App</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "mock-app",
3
+ "private": true,
4
+ "type": "module",
5
+ "version": "0.0.0",
6
+ "scripts": {
7
+ "build": "vite build"
8
+ },
9
+ "dependencies": {
10
+ "@apollo/client": "^4.1.7",
11
+ "@apollo/client-ai-apps": "file:../..",
12
+ "@modelcontextprotocol/ext-apps": "^1.1.0",
13
+ "react": "^19.1.1",
14
+ "react-dom": "^19.1.1"
15
+ },
16
+ "devDependencies": {
17
+ "@types/react": "^19.1.16",
18
+ "@types/react-dom": "^19.1.9",
19
+ "@vitejs/plugin-react": "^5.0.4",
20
+ "typescript": "~5.9.3",
21
+ "vite": "^7.3.2",
22
+ "vite-plugin-singlefile": "^2.3.0"
23
+ }
24
+ }
@@ -0,0 +1,23 @@
1
+ import { useToolInfo } from "@apollo/client-ai-apps/react";
2
+ import { Hello } from "./tools/Hello";
3
+ import { Echo } from "./tools/Echo";
4
+ import { Private } from "./tools/Private";
5
+ import { SemiPrivate } from "./tools/SemiPrivate";
6
+
7
+ export function App() {
8
+ const toolInfo = useToolInfo();
9
+
10
+ switch (toolInfo?.toolName) {
11
+ case "Hello":
12
+ return <Hello />;
13
+ case "Echo":
14
+ return <Echo />;
15
+ case "Private":
16
+ return <Private />;
17
+ case "SemiPrivate":
18
+ return <SemiPrivate />;
19
+ default:
20
+ // @ts-expect-error type should be never
21
+ throw new Error(`Unknown tool: ${toolInfo?.toolName}`);
22
+ }
23
+ }
@@ -0,0 +1,22 @@
1
+ import { StrictMode, Suspense } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import { InMemoryCache } from "@apollo/client";
4
+ import { ApolloClient } from "@apollo/client-ai-apps";
5
+ import { ApolloProvider } from "@apollo/client-ai-apps/react";
6
+ import manifest from "../.application-manifest.json";
7
+ import { App } from "./App.tsx";
8
+
9
+ const client = new ApolloClient({
10
+ cache: new InMemoryCache(),
11
+ manifest,
12
+ });
13
+
14
+ createRoot(document.getElementById("root")!).render(
15
+ <StrictMode>
16
+ <Suspense fallback={<p>Loading…</p>}>
17
+ <ApolloProvider client={client}>
18
+ <App />
19
+ </ApolloProvider>
20
+ </Suspense>
21
+ </StrictMode>
22
+ );
@@ -0,0 +1,33 @@
1
+ import { gql, type TypedDocumentNode } from "@apollo/client";
2
+ import { useQuery } from "@apollo/client/react";
3
+ import { createHydrationUtils } from "@apollo/client-ai-apps/react";
4
+
5
+ interface Data {
6
+ echo: string;
7
+ }
8
+
9
+ interface Variables {
10
+ message: string;
11
+ }
12
+
13
+ const ECHO_QUERY: TypedDocumentNode<Data, Variables> = gql`
14
+ "Echos the message back to the user"
15
+ query Echo($message: String!) @tool(name: "Echo") {
16
+ echo(message: $message)
17
+ }
18
+ `;
19
+
20
+ const { useHydratedVariables } = createHydrationUtils(ECHO_QUERY);
21
+
22
+ export function Echo() {
23
+ const [variables] = useHydratedVariables({
24
+ message: "This should be unused",
25
+ });
26
+ const { data, dataState } = useQuery(ECHO_QUERY, { variables });
27
+
28
+ if (dataState !== "complete") {
29
+ return <div>Loading...</div>;
30
+ }
31
+
32
+ return <div data-testid="echo">{data.echo}</div>;
33
+ }
@@ -0,0 +1,19 @@
1
+ import { gql, type TypedDocumentNode } from "@apollo/client";
2
+ import { useQuery } from "@apollo/client/react";
3
+
4
+ interface HelloQueryData {
5
+ hello: string;
6
+ }
7
+
8
+ const HELLO_QUERY: TypedDocumentNode<HelloQueryData> = gql`
9
+ "Returns a greeting"
10
+ query Hello @tool {
11
+ hello
12
+ }
13
+ `;
14
+
15
+ export function Hello() {
16
+ const { data } = useQuery(HELLO_QUERY);
17
+
18
+ return <h1 data-testid="greeting">{data?.hello}</h1>;
19
+ }
@@ -0,0 +1,34 @@
1
+ import { gql, type TypedDocumentNode } from "@apollo/client";
2
+ import { useQuery } from "@apollo/client/react";
3
+
4
+ interface Data {
5
+ user: {
6
+ address: string;
7
+ fullName: string;
8
+ };
9
+ }
10
+
11
+ const PRIVATE_QUERY: TypedDocumentNode<Data, Record<string, never>> = gql`
12
+ "Returns private user information"
13
+ query Private @tool {
14
+ user @private {
15
+ fullName
16
+ address
17
+ }
18
+ }
19
+ `;
20
+
21
+ export function Private() {
22
+ const { data, dataState } = useQuery(PRIVATE_QUERY);
23
+
24
+ if (dataState !== "complete") {
25
+ return <div>Loading...</div>;
26
+ }
27
+
28
+ return (
29
+ <div>
30
+ <div data-testid="fullName">{data.user.fullName}</div>
31
+ <div data-testid="address">{data.user.address}</div>
32
+ </div>
33
+ );
34
+ }
@@ -0,0 +1,34 @@
1
+ import { gql, type TypedDocumentNode } from "@apollo/client";
2
+ import { useQuery } from "@apollo/client/react";
3
+
4
+ interface Data {
5
+ user: {
6
+ address: string;
7
+ fullName: string;
8
+ };
9
+ }
10
+
11
+ const SEMI_PRIVATE_QUERY: TypedDocumentNode<Data, Record<string, never>> = gql`
12
+ "Returns user information with a private field"
13
+ query SemiPrivate @tool {
14
+ user {
15
+ fullName
16
+ address @private
17
+ }
18
+ }
19
+ `;
20
+
21
+ export function SemiPrivate() {
22
+ const { data, dataState } = useQuery(SEMI_PRIVATE_QUERY);
23
+
24
+ if (dataState !== "complete") {
25
+ return <div>Loading...</div>;
26
+ }
27
+
28
+ return (
29
+ <div>
30
+ <div data-testid="fullName">{data.user.fullName}</div>
31
+ <div data-testid="address">{data.user.address}</div>
32
+ </div>
33
+ );
34
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "extends": "@apollo/client-ai-apps/tsconfig/core",
3
+ "compilerOptions": {
4
+ "target": "ES2022",
5
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "moduleResolution": "bundler",
8
+ "jsx": "react-jsx",
9
+ "strict": true,
10
+ "skipLibCheck": true,
11
+ "noEmit": true,
12
+ "allowImportingTsExtensions": true,
13
+ "verbatimModuleSyntax": true,
14
+ "types": ["vite/client"],
15
+ "baseUrl": ".",
16
+ "paths": {
17
+ /* Prevent in-editor TypeScript issues by using the types from the root installed version */
18
+ "@apollo/client": ["../node_modules/@apollo/client"],
19
+ "@apollo/client/*": ["../node_modules/@apollo/client/*"],
20
+ "vite": ["../node_modules/vite"],
21
+ "vite/*": ["../node_modules/vite/*"]
22
+ }
23
+ },
24
+ "include": ["src", "vite.config.ts", ".apollo-client-ai-apps/types"]
25
+ }
@@ -0,0 +1,22 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+ import { viteSingleFile } from "vite-plugin-singlefile";
4
+ import { apolloClientAiApps } from "@apollo/client-ai-apps/vite";
5
+
6
+ export default defineConfig({
7
+ build: {
8
+ emptyOutDir: true,
9
+ },
10
+ resolve: {
11
+ dedupe: ["react", "react-dom", "@apollo/client"],
12
+ },
13
+ plugins: [
14
+ apolloClientAiApps({
15
+ targets: ["mcp"],
16
+ appsOutDir: "../apps",
17
+ schema: "../schema.graphql",
18
+ }),
19
+ react(),
20
+ viteSingleFile(),
21
+ ],
22
+ });