vendure-mcp-graphql 1.3.0 → 1.4.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/dist/index.js +29 -1
- package/dist/src/tools/graphql-operations.d.ts +18 -0
- package/dist/src/tools/graphql-operations.js +327 -0
- package/package.json +2 -2
- package/dist/src/types.d.ts +0 -45
- package/dist/src/types.js +0 -4
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ if (existsSync(apiKeyPath)) {
|
|
|
22
22
|
process.env.VENDURE_API_KEY = envConfig.parsed.VENDURE_API_KEY;
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
-
import { adminQuery, adminMutation, adminBatchMutation, getAdminSchema, getAdminOperations, shopQuery, shopMutation, shopBatchMutation, getShopSchema, getShopOperations, } from "./src/tools/index.js";
|
|
25
|
+
import { adminQuery, adminMutation, adminBatchMutation, getAdminSchema, getAdminOperations, shopQuery, shopMutation, shopBatchMutation, getShopSchema, getShopOperations, describeOperation, } from "./src/tools/index.js";
|
|
26
26
|
// Singleton pattern for shared resources
|
|
27
27
|
class SharedResources {
|
|
28
28
|
static instance = null;
|
|
@@ -198,6 +198,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
198
198
|
description: "List all available queries and mutations for the Shop API with descriptions.",
|
|
199
199
|
inputSchema: { type: "object", properties: {}, required: [] },
|
|
200
200
|
},
|
|
201
|
+
{
|
|
202
|
+
name: "describe_operation",
|
|
203
|
+
description: "Describe a GraphQL query or mutation in detail: shows which API(s) it's available on, arguments with input types, and the return type with all its fields. Checks both Admin and Shop APIs by default.",
|
|
204
|
+
inputSchema: {
|
|
205
|
+
type: "object",
|
|
206
|
+
properties: {
|
|
207
|
+
operation: {
|
|
208
|
+
type: "string",
|
|
209
|
+
description: "The name of the query or mutation to describe (e.g. productGroups, createProduct)",
|
|
210
|
+
},
|
|
211
|
+
api: {
|
|
212
|
+
type: "string",
|
|
213
|
+
enum: ["admin", "shop"],
|
|
214
|
+
description: "Optional: restrict lookup to a specific API. If omitted, checks both Admin and Shop APIs.",
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
required: ["operation"],
|
|
218
|
+
},
|
|
219
|
+
},
|
|
201
220
|
],
|
|
202
221
|
}));
|
|
203
222
|
// Handle tool calls
|
|
@@ -295,6 +314,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
295
314
|
},
|
|
296
315
|
],
|
|
297
316
|
};
|
|
317
|
+
case "describe_operation":
|
|
318
|
+
return {
|
|
319
|
+
content: [
|
|
320
|
+
{
|
|
321
|
+
type: "text",
|
|
322
|
+
text: JSON.stringify(await describeOperation(args?.operation, args?.api), null, 2),
|
|
323
|
+
},
|
|
324
|
+
],
|
|
325
|
+
};
|
|
298
326
|
default:
|
|
299
327
|
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
300
328
|
}
|
|
@@ -22,6 +22,14 @@ export declare function getAdminSchema(): Promise<any>;
|
|
|
22
22
|
* Fetch the Shop GraphQL schema introspection
|
|
23
23
|
*/
|
|
24
24
|
export declare function getShopSchema(): Promise<any>;
|
|
25
|
+
/**
|
|
26
|
+
* Fetch details for a single type from the Admin API schema
|
|
27
|
+
*/
|
|
28
|
+
export declare function getAdminTypeDetails(name: string): Promise<any>;
|
|
29
|
+
/**
|
|
30
|
+
* Fetch details for a single type from the Shop API schema
|
|
31
|
+
*/
|
|
32
|
+
export declare function getShopTypeDetails(name: string): Promise<any>;
|
|
25
33
|
/**
|
|
26
34
|
* Fetch a summary of available queries and mutations for the Admin API
|
|
27
35
|
*/
|
|
@@ -30,6 +38,16 @@ export declare function getAdminOperations(): Promise<any>;
|
|
|
30
38
|
* Fetch a summary of available queries and mutations for the Shop API
|
|
31
39
|
*/
|
|
32
40
|
export declare function getShopOperations(): Promise<any>;
|
|
41
|
+
/**
|
|
42
|
+
* Describe a GraphQL operation (query or mutation) with full details.
|
|
43
|
+
*
|
|
44
|
+
* Checks both Admin and Shop APIs (or just one if `api` is specified).
|
|
45
|
+
* Returns the operation's arguments with input types, and its return type with fields.
|
|
46
|
+
*
|
|
47
|
+
* @param operationName - The name of the query or mutation
|
|
48
|
+
* @param api - Optional: restrict to "admin" or "shop" only
|
|
49
|
+
*/
|
|
50
|
+
export declare function describeOperation(operationName: string, api?: "admin" | "shop"): Promise<any>;
|
|
33
51
|
export interface BatchResult {
|
|
34
52
|
total: number;
|
|
35
53
|
succeeded: number;
|
|
@@ -138,6 +138,102 @@ const operationsSummaryQuery = `
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
`;
|
|
141
|
+
/**
|
|
142
|
+
* Query that fetches all operations with their return type chain and args.
|
|
143
|
+
* Used by describeOperation to find a specific operation's details.
|
|
144
|
+
*/
|
|
145
|
+
const allOperationsWithTypesQuery = `
|
|
146
|
+
query AllOperationsWithTypes {
|
|
147
|
+
__schema {
|
|
148
|
+
queryType {
|
|
149
|
+
fields {
|
|
150
|
+
name
|
|
151
|
+
description
|
|
152
|
+
args {
|
|
153
|
+
name
|
|
154
|
+
description
|
|
155
|
+
defaultValue
|
|
156
|
+
type {
|
|
157
|
+
name
|
|
158
|
+
kind
|
|
159
|
+
ofType {
|
|
160
|
+
name
|
|
161
|
+
kind
|
|
162
|
+
ofType {
|
|
163
|
+
name
|
|
164
|
+
kind
|
|
165
|
+
ofType {
|
|
166
|
+
name
|
|
167
|
+
kind
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
type {
|
|
174
|
+
name
|
|
175
|
+
kind
|
|
176
|
+
ofType {
|
|
177
|
+
name
|
|
178
|
+
kind
|
|
179
|
+
ofType {
|
|
180
|
+
name
|
|
181
|
+
kind
|
|
182
|
+
ofType {
|
|
183
|
+
name
|
|
184
|
+
kind
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
mutationType {
|
|
192
|
+
fields {
|
|
193
|
+
name
|
|
194
|
+
description
|
|
195
|
+
args {
|
|
196
|
+
name
|
|
197
|
+
description
|
|
198
|
+
defaultValue
|
|
199
|
+
type {
|
|
200
|
+
name
|
|
201
|
+
kind
|
|
202
|
+
ofType {
|
|
203
|
+
name
|
|
204
|
+
kind
|
|
205
|
+
ofType {
|
|
206
|
+
name
|
|
207
|
+
kind
|
|
208
|
+
ofType {
|
|
209
|
+
name
|
|
210
|
+
kind
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
type {
|
|
217
|
+
name
|
|
218
|
+
kind
|
|
219
|
+
ofType {
|
|
220
|
+
name
|
|
221
|
+
kind
|
|
222
|
+
ofType {
|
|
223
|
+
name
|
|
224
|
+
kind
|
|
225
|
+
ofType {
|
|
226
|
+
name
|
|
227
|
+
kind
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
`;
|
|
141
237
|
/**
|
|
142
238
|
* Fetch the Admin GraphQL schema introspection
|
|
143
239
|
*/
|
|
@@ -152,6 +248,93 @@ export async function getShopSchema() {
|
|
|
152
248
|
const client = getClient();
|
|
153
249
|
return client.request(getShopUrl(), introspectionQuery);
|
|
154
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Fetch a summary of available queries and mutations for the Admin API
|
|
253
|
+
*/
|
|
254
|
+
const typeDetailsQuery = `
|
|
255
|
+
query GetTypeDetails($name: String!) {
|
|
256
|
+
__type(name: $name) {
|
|
257
|
+
kind
|
|
258
|
+
name
|
|
259
|
+
description
|
|
260
|
+
fields(includeDeprecated: false) {
|
|
261
|
+
name
|
|
262
|
+
description
|
|
263
|
+
args {
|
|
264
|
+
name
|
|
265
|
+
description
|
|
266
|
+
type {
|
|
267
|
+
name
|
|
268
|
+
kind
|
|
269
|
+
ofType {
|
|
270
|
+
name
|
|
271
|
+
kind
|
|
272
|
+
ofType {
|
|
273
|
+
name
|
|
274
|
+
kind
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
defaultValue
|
|
279
|
+
}
|
|
280
|
+
type {
|
|
281
|
+
name
|
|
282
|
+
kind
|
|
283
|
+
ofType {
|
|
284
|
+
name
|
|
285
|
+
kind
|
|
286
|
+
ofType {
|
|
287
|
+
name
|
|
288
|
+
kind
|
|
289
|
+
ofType {
|
|
290
|
+
name
|
|
291
|
+
kind
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
inputFields {
|
|
298
|
+
name
|
|
299
|
+
description
|
|
300
|
+
type {
|
|
301
|
+
name
|
|
302
|
+
kind
|
|
303
|
+
ofType {
|
|
304
|
+
name
|
|
305
|
+
kind
|
|
306
|
+
ofType {
|
|
307
|
+
name
|
|
308
|
+
kind
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
defaultValue
|
|
313
|
+
}
|
|
314
|
+
enumValues(includeDeprecated: false) {
|
|
315
|
+
name
|
|
316
|
+
description
|
|
317
|
+
}
|
|
318
|
+
interfaces {
|
|
319
|
+
name
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
`;
|
|
324
|
+
/**
|
|
325
|
+
* Fetch details for a single type from the Admin API schema
|
|
326
|
+
*/
|
|
327
|
+
export async function getAdminTypeDetails(name) {
|
|
328
|
+
const client = getClient();
|
|
329
|
+
return client.request(getAdminUrl(), typeDetailsQuery, { name });
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Fetch details for a single type from the Shop API schema
|
|
333
|
+
*/
|
|
334
|
+
export async function getShopTypeDetails(name) {
|
|
335
|
+
const client = getClient();
|
|
336
|
+
return client.request(getShopUrl(), typeDetailsQuery, { name });
|
|
337
|
+
}
|
|
155
338
|
/**
|
|
156
339
|
* Fetch a summary of available queries and mutations for the Admin API
|
|
157
340
|
*/
|
|
@@ -166,6 +349,150 @@ export async function getShopOperations() {
|
|
|
166
349
|
const client = getClient();
|
|
167
350
|
return client.request(getShopUrl(), operationsSummaryQuery);
|
|
168
351
|
}
|
|
352
|
+
// ─── Helpers for describeOperation ───────────────────────────────
|
|
353
|
+
/**
|
|
354
|
+
* Extract the named type name from a type chain (through NonNull/List wrappers).
|
|
355
|
+
* e.g., `[ProductGroup!]!` → `ProductGroup`
|
|
356
|
+
*/
|
|
357
|
+
function extractNamedTypeName(type) {
|
|
358
|
+
if (!type)
|
|
359
|
+
return null;
|
|
360
|
+
if (type.kind !== "NON_NULL" && type.kind !== "LIST") {
|
|
361
|
+
return type.name || null;
|
|
362
|
+
}
|
|
363
|
+
return extractNamedTypeName(type.ofType);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Format a GraphQL type chain into a readable string like `[ProductGroup!]!`
|
|
367
|
+
*/
|
|
368
|
+
function formatTypeString(type) {
|
|
369
|
+
if (!type)
|
|
370
|
+
return "unknown";
|
|
371
|
+
switch (type.kind) {
|
|
372
|
+
case "NON_NULL":
|
|
373
|
+
return `${formatTypeString(type.ofType)}!`;
|
|
374
|
+
case "LIST":
|
|
375
|
+
return `[${formatTypeString(type.ofType)}]`;
|
|
376
|
+
default:
|
|
377
|
+
return type.name || "unknown";
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Describe a GraphQL operation (query or mutation) with full details.
|
|
382
|
+
*
|
|
383
|
+
* Checks both Admin and Shop APIs (or just one if `api` is specified).
|
|
384
|
+
* Returns the operation's arguments with input types, and its return type with fields.
|
|
385
|
+
*
|
|
386
|
+
* @param operationName - The name of the query or mutation
|
|
387
|
+
* @param api - Optional: restrict to "admin" or "shop" only
|
|
388
|
+
*/
|
|
389
|
+
export async function describeOperation(operationName, api) {
|
|
390
|
+
const apisToCheck = api ? [api] : ["admin", "shop"];
|
|
391
|
+
const client = getClient();
|
|
392
|
+
const details = [];
|
|
393
|
+
for (const targetApi of apisToCheck) {
|
|
394
|
+
const url = targetApi === "admin" ? getAdminUrl() : getShopUrl();
|
|
395
|
+
try {
|
|
396
|
+
// Step 1: Fetch all operations with type chains to find the one we need
|
|
397
|
+
const schemaData = await client.request(url, allOperationsWithTypesQuery);
|
|
398
|
+
const queryFields = schemaData.__schema.queryType.fields;
|
|
399
|
+
const mutationFields = schemaData.__schema.mutationType.fields;
|
|
400
|
+
const queryField = queryFields.find((f) => f.name === operationName);
|
|
401
|
+
const mutationField = mutationFields.find((f) => f.name === operationName);
|
|
402
|
+
if (!queryField && !mutationField)
|
|
403
|
+
continue;
|
|
404
|
+
const field = queryField || mutationField;
|
|
405
|
+
const kind = queryField ? "query" : "mutation";
|
|
406
|
+
// Step 2: Resolve the return type's fields
|
|
407
|
+
const returnTypeName = extractNamedTypeName(field.type);
|
|
408
|
+
let returnType = {
|
|
409
|
+
name: formatTypeString(field.type),
|
|
410
|
+
namedType: returnTypeName,
|
|
411
|
+
};
|
|
412
|
+
if (returnTypeName) {
|
|
413
|
+
try {
|
|
414
|
+
const result = await client.request(url, typeDetailsQuery, {
|
|
415
|
+
name: returnTypeName,
|
|
416
|
+
});
|
|
417
|
+
if (result.__type) {
|
|
418
|
+
returnType = {
|
|
419
|
+
name: returnTypeName,
|
|
420
|
+
kind: result.__type.kind,
|
|
421
|
+
description: result.__type.description,
|
|
422
|
+
fields: (result.__type.fields || []).map((f) => ({
|
|
423
|
+
name: f.name,
|
|
424
|
+
type: formatTypeString(f.type),
|
|
425
|
+
description: f.description,
|
|
426
|
+
args: f.args?.length
|
|
427
|
+
? f.args.map((a) => ({
|
|
428
|
+
name: a.name,
|
|
429
|
+
type: formatTypeString(a.type),
|
|
430
|
+
description: a.description,
|
|
431
|
+
defaultValue: a.defaultValue,
|
|
432
|
+
}))
|
|
433
|
+
: undefined,
|
|
434
|
+
})),
|
|
435
|
+
enumValues: result.__type.enumValues,
|
|
436
|
+
inputFields: result.__type.inputFields,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
// Type might be a built-in scalar - skip
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
// Step 3: Resolve argument input types
|
|
445
|
+
const argDetails = await Promise.all((field.args || []).map(async (arg) => {
|
|
446
|
+
const argTypeName = extractNamedTypeName(arg.type);
|
|
447
|
+
const detail = {
|
|
448
|
+
name: arg.name,
|
|
449
|
+
type: formatTypeString(arg.type),
|
|
450
|
+
description: arg.description,
|
|
451
|
+
defaultValue: arg.defaultValue,
|
|
452
|
+
};
|
|
453
|
+
if (argTypeName) {
|
|
454
|
+
try {
|
|
455
|
+
const result = await client.request(url, typeDetailsQuery, {
|
|
456
|
+
name: argTypeName,
|
|
457
|
+
});
|
|
458
|
+
if (result.__type) {
|
|
459
|
+
detail.inputFields = (result.__type.inputFields || []).map((f) => ({
|
|
460
|
+
name: f.name,
|
|
461
|
+
type: formatTypeString(f.type),
|
|
462
|
+
description: f.description,
|
|
463
|
+
defaultValue: f.defaultValue,
|
|
464
|
+
}));
|
|
465
|
+
detail.inputKind = result.__type.kind;
|
|
466
|
+
detail.inputDescription = result.__type.description;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
catch {
|
|
470
|
+
// ignore
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return detail;
|
|
474
|
+
}));
|
|
475
|
+
details.push({
|
|
476
|
+
api: targetApi,
|
|
477
|
+
kind,
|
|
478
|
+
description: field.description,
|
|
479
|
+
arguments: argDetails,
|
|
480
|
+
returnType,
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
catch (error) {
|
|
484
|
+
details.push({
|
|
485
|
+
api: targetApi,
|
|
486
|
+
error: error instanceof Error ? error.message : String(error),
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return {
|
|
491
|
+
operation: operationName,
|
|
492
|
+
availableOn: details.filter((d) => !d.error).map((d) => d.api),
|
|
493
|
+
details,
|
|
494
|
+
};
|
|
495
|
+
}
|
|
169
496
|
/**
|
|
170
497
|
* Execute a GraphQL mutation in batch for multiple IDs on the Admin API.
|
|
171
498
|
* Runs mutations concurrently with configurable concurrency.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vendure-mcp-graphql",
|
|
3
|
-
"version": "1.
|
|
4
|
-
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "MCP server for Vendure Admin and Shop GraphQL APIs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"vendure-mcp-graphql": "./dist/index.js"
|
package/dist/src/types.d.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type definitions for GraphQL entities
|
|
3
|
-
*/
|
|
4
|
-
export interface Archetype {
|
|
5
|
-
id: string;
|
|
6
|
-
code: string;
|
|
7
|
-
name: string;
|
|
8
|
-
coreDrive: string;
|
|
9
|
-
primaryFear: string;
|
|
10
|
-
languageSignature: string;
|
|
11
|
-
psychologicalTriggers: string[];
|
|
12
|
-
}
|
|
13
|
-
export type PulseScriptType = 'OPENING' | 'RESPONSE' | 'RESOLUTION' | 'CUSTOM';
|
|
14
|
-
export interface PulseScript {
|
|
15
|
-
id: string;
|
|
16
|
-
code: string;
|
|
17
|
-
name: string;
|
|
18
|
-
type: PulseScriptType;
|
|
19
|
-
description?: string;
|
|
20
|
-
archetypeId: string;
|
|
21
|
-
archetype?: Archetype;
|
|
22
|
-
}
|
|
23
|
-
export interface CreatePulseScriptInput {
|
|
24
|
-
code: string;
|
|
25
|
-
name: string;
|
|
26
|
-
type: PulseScriptType;
|
|
27
|
-
description?: string;
|
|
28
|
-
archetypeId: string;
|
|
29
|
-
}
|
|
30
|
-
export interface UpdatePulseScriptInput {
|
|
31
|
-
id: string;
|
|
32
|
-
code?: string;
|
|
33
|
-
name?: string;
|
|
34
|
-
type?: PulseScriptType;
|
|
35
|
-
description?: string;
|
|
36
|
-
archetypeId?: string;
|
|
37
|
-
}
|
|
38
|
-
export interface Pulse {
|
|
39
|
-
id: string;
|
|
40
|
-
code: string;
|
|
41
|
-
status: string;
|
|
42
|
-
currentState: string;
|
|
43
|
-
initiatorArchetype?: Archetype;
|
|
44
|
-
targetArchetype?: Archetype;
|
|
45
|
-
}
|
package/dist/src/types.js
DELETED