@apollo/client-ai-apps 0.5.3 → 0.6.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 +65 -0
- package/dist/config/defineConfig.d.ts +1 -0
- package/dist/config/defineConfig.d.ts.map +1 -1
- package/dist/config/schema.d.ts +1 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +1 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/mcp/core/McpAppManager.d.ts +2 -1
- package/dist/mcp/core/McpAppManager.d.ts.map +1 -1
- package/dist/mcp/core/McpAppManager.js +11 -1
- package/dist/mcp/core/McpAppManager.js.map +1 -1
- package/dist/mcp/react/hooks/useHostContext.d.ts +2 -0
- package/dist/mcp/react/hooks/useHostContext.d.ts.map +1 -0
- package/dist/mcp/react/hooks/useHostContext.js +7 -0
- package/dist/mcp/react/hooks/useHostContext.js.map +1 -0
- package/dist/mcp/react/index.d.ts +1 -0
- package/dist/mcp/react/index.d.ts.map +1 -1
- package/dist/mcp/react/index.js +1 -0
- package/dist/mcp/react/index.js.map +1 -1
- package/dist/openai/core/McpAppManager.d.ts +2 -1
- package/dist/openai/core/McpAppManager.d.ts.map +1 -1
- package/dist/openai/core/McpAppManager.js +11 -1
- package/dist/openai/core/McpAppManager.js.map +1 -1
- package/dist/openai/react/hooks/useHostContext.d.ts +2 -0
- package/dist/openai/react/hooks/useHostContext.d.ts.map +1 -0
- package/dist/openai/react/hooks/useHostContext.js +7 -0
- package/dist/openai/react/hooks/useHostContext.js.map +1 -0
- package/dist/openai/react/index.d.ts +1 -0
- package/dist/openai/react/index.d.ts.map +1 -1
- package/dist/openai/react/index.js +1 -0
- package/dist/openai/react/index.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mcp.d.ts +1 -1
- package/dist/react/index.mcp.d.ts.map +1 -1
- package/dist/react/index.mcp.js +1 -1
- package/dist/react/index.mcp.js.map +1 -1
- package/dist/react/index.openai.d.ts +1 -1
- package/dist/react/index.openai.d.ts.map +1 -1
- package/dist/react/index.openai.js +1 -1
- package/dist/react/index.openai.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/vite/__tests__/utilities/build.d.ts.map +1 -1
- package/dist/vite/__tests__/utilities/build.js +0 -1
- package/dist/vite/__tests__/utilities/build.js.map +1 -1
- package/dist/vite/apolloClientAiApps.d.ts +1 -0
- package/dist/vite/apolloClientAiApps.d.ts.map +1 -1
- package/dist/vite/apolloClientAiApps.js +63 -46
- package/dist/vite/apolloClientAiApps.js.map +1 -1
- package/package.json +1 -4
- package/src/config/schema.ts +1 -0
- package/src/mcp/core/McpAppManager.ts +23 -1
- package/src/mcp/react/hooks/__tests__/useHostContext.test.tsx +95 -0
- package/src/mcp/react/hooks/useHostContext.ts +14 -0
- package/src/mcp/react/index.ts +1 -0
- package/src/openai/core/McpAppManager.ts +22 -1
- package/src/openai/react/hooks/useHostContext.ts +14 -0
- package/src/openai/react/index.ts +1 -0
- package/src/react/index.mcp.ts +1 -0
- package/src/react/index.openai.ts +1 -0
- package/src/react/index.ts +3 -0
- package/src/testing/internal/mcp/mockMcpHost.ts +12 -0
- package/src/testing/internal/utilities/mockApplicationManifest.ts +1 -0
- package/src/types/application-manifest.ts +1 -0
- package/src/vite/__tests__/apolloClientAiApps.test.ts +460 -61
- package/src/vite/__tests__/utilities/build.ts +0 -1
- package/src/vite/apolloClientAiApps.ts +100 -57
|
@@ -14,7 +14,7 @@ import { glob } from "glob";
|
|
|
14
14
|
import { print } from "@apollo/client/utilities";
|
|
15
15
|
import { removeDirectivesFromDocument } from "@apollo/client/utilities/internal";
|
|
16
16
|
import { of } from "rxjs";
|
|
17
|
-
import { Kind,
|
|
17
|
+
import { Kind, OperationTypeNode, parse } from "graphql";
|
|
18
18
|
import {
|
|
19
19
|
getArgumentValue,
|
|
20
20
|
getDirectiveArgument,
|
|
@@ -30,6 +30,7 @@ import { explorer } from "./utilities/config.js";
|
|
|
30
30
|
import type { ApolloClientAiAppsConfig } from "../config/index.js";
|
|
31
31
|
import { ApolloClientAiAppsConfigSchema } from "../config/schema.js";
|
|
32
32
|
import { z } from "zod";
|
|
33
|
+
import { createFragmentRegistry } from "@apollo/client/cache";
|
|
33
34
|
|
|
34
35
|
export declare namespace apolloClientAiApps {
|
|
35
36
|
export type Target = ApolloClientAiAppsConfig.AppTarget;
|
|
@@ -37,6 +38,7 @@ export declare namespace apolloClientAiApps {
|
|
|
37
38
|
export interface Options {
|
|
38
39
|
targets: Target[];
|
|
39
40
|
devTarget?: Target | undefined;
|
|
41
|
+
appsOutDir: string;
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -66,19 +68,23 @@ export function devTarget(target: string | undefined) {
|
|
|
66
68
|
interface FileCache {
|
|
67
69
|
file: string;
|
|
68
70
|
hash: string;
|
|
69
|
-
|
|
71
|
+
sources: DocumentNode[];
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
export function apolloClientAiApps(
|
|
73
75
|
options: apolloClientAiApps.Options
|
|
74
76
|
): Plugin {
|
|
75
77
|
const targets = Array.from(new Set(options.targets));
|
|
76
|
-
const {
|
|
78
|
+
const {
|
|
79
|
+
devTarget = targets.length === 1 ? targets[0] : undefined,
|
|
80
|
+
appsOutDir,
|
|
81
|
+
} = options;
|
|
77
82
|
const cache = new Map<string, FileCache>();
|
|
78
83
|
|
|
79
|
-
let packageJson!: Record<string, any>;
|
|
80
84
|
let config!: ResolvedConfig;
|
|
81
85
|
|
|
86
|
+
const fragments = createFragmentRegistry();
|
|
87
|
+
|
|
82
88
|
invariant(
|
|
83
89
|
Array.isArray(targets) && targets.length > 0,
|
|
84
90
|
"The `targets` option must be a non-empty array"
|
|
@@ -89,8 +95,13 @@ export function apolloClientAiApps(
|
|
|
89
95
|
`All targets must be one of: ${VALID_TARGETS.join(", ")}`
|
|
90
96
|
);
|
|
91
97
|
|
|
98
|
+
invariant(
|
|
99
|
+
path.basename(path.normalize(appsOutDir)) === "apps",
|
|
100
|
+
"`appsOutDir` must end with `apps` as the final path segment (e.g. `path/to/apps`)."
|
|
101
|
+
);
|
|
102
|
+
|
|
92
103
|
const client = new ApolloClient({
|
|
93
|
-
cache: new InMemoryCache(),
|
|
104
|
+
cache: new InMemoryCache({ fragments }),
|
|
94
105
|
link: processQueryLink,
|
|
95
106
|
});
|
|
96
107
|
|
|
@@ -106,52 +117,57 @@ export function apolloClientAiApps(
|
|
|
106
117
|
{ name: "graphql-tag", identifier: "gql" },
|
|
107
118
|
{ name: "@apollo/client", identifier: "gql" },
|
|
108
119
|
],
|
|
109
|
-
}).map((source) => (
|
|
110
|
-
node: parse(source.body),
|
|
111
|
-
file,
|
|
112
|
-
location: source.locationOffset,
|
|
113
|
-
}));
|
|
120
|
+
}).map((source) => parse(source.body));
|
|
114
121
|
|
|
115
|
-
|
|
116
|
-
for (const source of sources) {
|
|
117
|
-
const type = (
|
|
118
|
-
source.node.definitions.find(
|
|
119
|
-
(d) => d.kind === "OperationDefinition"
|
|
120
|
-
) as OperationDefinitionNode
|
|
121
|
-
).operation;
|
|
122
|
-
|
|
123
|
-
let result;
|
|
124
|
-
if (type === "query") {
|
|
125
|
-
result = await client.query({
|
|
126
|
-
query: source.node,
|
|
127
|
-
fetchPolicy: "no-cache",
|
|
128
|
-
});
|
|
129
|
-
} else if (type === "mutation") {
|
|
130
|
-
result = await client.mutate({
|
|
131
|
-
mutation: source.node,
|
|
132
|
-
fetchPolicy: "no-cache",
|
|
133
|
-
});
|
|
134
|
-
} else {
|
|
135
|
-
throw new Error(
|
|
136
|
-
"Found an unsupported operation type. Only Query and Mutation are supported."
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
operations.push(result.data as ManifestOperation);
|
|
140
|
-
}
|
|
122
|
+
fragments.register(...sources);
|
|
141
123
|
|
|
142
124
|
cache.set(file, {
|
|
143
125
|
file: file,
|
|
144
126
|
hash: fileHash,
|
|
145
|
-
|
|
127
|
+
sources,
|
|
146
128
|
});
|
|
147
129
|
}
|
|
148
130
|
|
|
149
131
|
async function generateManifest(environment?: Environment) {
|
|
150
132
|
const appsConfig = await getAppsConfig();
|
|
151
|
-
const
|
|
152
|
-
(entry) => entry.
|
|
133
|
+
const sources = Array.from(cache.values()).flatMap(
|
|
134
|
+
(entry) => entry.sources
|
|
153
135
|
);
|
|
154
136
|
|
|
137
|
+
const operations: ManifestOperation[] = [];
|
|
138
|
+
for (const source of sources) {
|
|
139
|
+
const operationDef = source.definitions.find(
|
|
140
|
+
(d) => d.kind === Kind.OPERATION_DEFINITION
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
if (!operationDef) continue;
|
|
144
|
+
|
|
145
|
+
switch (operationDef.operation) {
|
|
146
|
+
case OperationTypeNode.QUERY: {
|
|
147
|
+
const result = await client.query<ManifestOperation>({
|
|
148
|
+
query: source,
|
|
149
|
+
fetchPolicy: "no-cache",
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
operations.push(result.data!);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
case OperationTypeNode.MUTATION: {
|
|
156
|
+
const result = await client.mutate<ManifestOperation>({
|
|
157
|
+
mutation: source,
|
|
158
|
+
fetchPolicy: "no-cache",
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
operations.push(result.data!);
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
default:
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Found unsupported operation type '${operationDef.operation}'. Only queries and mutations are supported.`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
155
171
|
invariant(
|
|
156
172
|
operations.filter((o) => o.prefetch).length <= 1,
|
|
157
173
|
"Found multiple operations marked as `@prefetch`. You can only mark 1 operation with `@prefetch`."
|
|
@@ -185,6 +201,14 @@ export function apolloClientAiApps(
|
|
|
185
201
|
) as { mcp?: string; openai?: string };
|
|
186
202
|
}
|
|
187
203
|
|
|
204
|
+
const packageJson = readPackageJson();
|
|
205
|
+
const appName = appsConfig.name ?? packageJson.name;
|
|
206
|
+
|
|
207
|
+
invariant(
|
|
208
|
+
appName,
|
|
209
|
+
"Error generating application manifest. Could not determine app name. Set `name` in your apollo-client-ai-apps config or `package.json`."
|
|
210
|
+
);
|
|
211
|
+
|
|
188
212
|
const manifest: ApplicationManifest = {
|
|
189
213
|
format: "apollo-ai-app-manifest",
|
|
190
214
|
version: "1",
|
|
@@ -192,11 +216,10 @@ export function apolloClientAiApps(
|
|
|
192
216
|
name: appsConfig.name ?? packageJson.name,
|
|
193
217
|
description: appsConfig.description ?? packageJson.description,
|
|
194
218
|
hash: createHash("sha256").update(Date.now().toString()).digest("hex"),
|
|
195
|
-
operations
|
|
196
|
-
(entry) => entry.operations
|
|
197
|
-
),
|
|
219
|
+
operations,
|
|
198
220
|
resource,
|
|
199
221
|
csp: {
|
|
222
|
+
baseUriDomains: appsConfig.csp?.baseUriDomains ?? [],
|
|
200
223
|
connectDomains: appsConfig.csp?.connectDomains ?? [],
|
|
201
224
|
frameDomains: appsConfig.csp?.frameDomains ?? [],
|
|
202
225
|
redirectDomains: appsConfig.csp?.redirectDomains ?? [],
|
|
@@ -212,14 +235,7 @@ export function apolloClientAiApps(
|
|
|
212
235
|
manifest.labels = appsConfig.labels;
|
|
213
236
|
}
|
|
214
237
|
|
|
215
|
-
|
|
216
|
-
// subdirectories, but we want the manifest to be in the root outDir. If we
|
|
217
|
-
// are running in a different environment, we'll put it in the configured
|
|
218
|
-
// outDir directly instead.
|
|
219
|
-
const outDir =
|
|
220
|
-
environment?.name === "mcp" || environment?.name === "openai" ?
|
|
221
|
-
path.resolve(config.build.outDir, "../")
|
|
222
|
-
: config.build.outDir;
|
|
238
|
+
const outDir = path.join(appsOutDir, appName);
|
|
223
239
|
|
|
224
240
|
// Always write to build directory so the MCP server picks it up
|
|
225
241
|
const dest = path.resolve(root, outDir, ".application-manifest.json");
|
|
@@ -233,9 +249,6 @@ export function apolloClientAiApps(
|
|
|
233
249
|
return {
|
|
234
250
|
name: "@apollo/client-ai-apps/vite",
|
|
235
251
|
async buildStart() {
|
|
236
|
-
// Read package.json on start
|
|
237
|
-
packageJson = JSON.parse(fs.readFileSync("package.json", "utf-8"));
|
|
238
|
-
|
|
239
252
|
// Scan all files on startup
|
|
240
253
|
const files = await glob("./src/**/*.{ts,tsx,js,jsx}", { fs });
|
|
241
254
|
|
|
@@ -252,19 +265,27 @@ export function apolloClientAiApps(
|
|
|
252
265
|
configResolved(resolvedConfig) {
|
|
253
266
|
config = resolvedConfig;
|
|
254
267
|
},
|
|
255
|
-
configEnvironment(name, { build }) {
|
|
268
|
+
async configEnvironment(name, { build }) {
|
|
256
269
|
if (!targets.includes(name as any)) return;
|
|
257
270
|
|
|
271
|
+
const appsConfig = await getAppsConfig();
|
|
272
|
+
const appName = appsConfig.name ?? readPackageJson().name;
|
|
273
|
+
|
|
274
|
+
invariant(
|
|
275
|
+
appName,
|
|
276
|
+
"Could not determine app name. Set `name` in your apollo-client-ai-apps config or `package.json`."
|
|
277
|
+
);
|
|
278
|
+
|
|
258
279
|
return {
|
|
259
280
|
build: {
|
|
260
|
-
outDir: path.join(
|
|
281
|
+
outDir: path.join(appsOutDir, appName, name),
|
|
261
282
|
},
|
|
262
283
|
};
|
|
263
284
|
},
|
|
264
285
|
configureServer(server) {
|
|
265
286
|
server.watcher.on("change", async (file) => {
|
|
266
287
|
if (file.endsWith("package.json")) {
|
|
267
|
-
|
|
288
|
+
readPackageJson.resetCache();
|
|
268
289
|
await generateManifest();
|
|
269
290
|
} else if (file.match(/\.?apollo-client-ai-apps\.config\.\w+$/)) {
|
|
270
291
|
explorer.clearCaches();
|
|
@@ -276,7 +297,14 @@ export function apolloClientAiApps(
|
|
|
276
297
|
});
|
|
277
298
|
},
|
|
278
299
|
|
|
279
|
-
config(
|
|
300
|
+
config(userConfig, { command }) {
|
|
301
|
+
if (userConfig.build?.outDir) {
|
|
302
|
+
console.warn(
|
|
303
|
+
"[@apollo/client-ai-apps/vite] `build.outDir` is set in your Vite config but will be " +
|
|
304
|
+
"ignored. Use `appsOutDir` in the plugin options to control the output location."
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
280
308
|
if (command === "serve") {
|
|
281
309
|
invariant(
|
|
282
310
|
isValidTarget(devTarget) || targets.length === 1,
|
|
@@ -497,6 +525,21 @@ function getResourceFromConfig(
|
|
|
497
525
|
return typeof config === "string" ? config : config[target];
|
|
498
526
|
}
|
|
499
527
|
|
|
528
|
+
function readPackageJson(): Record<string, any> {
|
|
529
|
+
if (readPackageJson.cache) {
|
|
530
|
+
return readPackageJson.cache;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return (readPackageJson.cache = JSON.parse(
|
|
534
|
+
fs.readFileSync("package.json", "utf-8")
|
|
535
|
+
));
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
readPackageJson.cache = undefined as Record<string, any> | undefined;
|
|
539
|
+
readPackageJson.resetCache = () => {
|
|
540
|
+
readPackageJson.cache = undefined;
|
|
541
|
+
};
|
|
542
|
+
|
|
500
543
|
const ToolDirectiveSchema = z.strictObject({
|
|
501
544
|
name: z.stringFormat("toolName", (value) => value.indexOf(" ") === -1, {
|
|
502
545
|
error: (iss) => `Tool with name "${iss.input}" must not contain spaces`,
|