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 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.3.0",
4
- "description": "MCP server for Vendure Admin and Shop GraphQL APIs",
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"
@@ -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
@@ -1,4 +0,0 @@
1
- /**
2
- * Type definitions for GraphQL entities
3
- */
4
- export {};