vendure-mcp-graphql 1.4.0 → 1.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/dist/index.js CHANGED
@@ -66,6 +66,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
66
66
  properties: {
67
67
  query: { type: "string", description: "The GraphQL query string" },
68
68
  variables: { type: "object", description: "Optional variables" },
69
+ channelToken: {
70
+ type: "string",
71
+ description: "Optional channel token to scope the request to a specific channel. Requires CHANNEL_API_KEY_MAP to be configured with a key for this channel.",
72
+ },
69
73
  },
70
74
  required: ["query"],
71
75
  },
@@ -81,6 +85,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
81
85
  description: "The GraphQL mutation string",
82
86
  },
83
87
  variables: { type: "object", description: "Optional variables" },
88
+ channelToken: {
89
+ type: "string",
90
+ description: "Optional channel token to scope the request to a specific channel. Requires CHANNEL_API_KEY_MAP to be configured with a key for this channel.",
91
+ },
84
92
  },
85
93
  required: ["mutation"],
86
94
  },
@@ -114,6 +122,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
114
122
  type: "number",
115
123
  description: "Max number of concurrent mutations. Default: 5.",
116
124
  },
125
+ channelToken: {
126
+ type: "string",
127
+ description: "Optional channel token to set channel context (e.g., 'default-channel', 'test-channel')",
128
+ },
117
129
  },
118
130
  required: ["mutation", "ids"],
119
131
  },
@@ -136,6 +148,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
136
148
  properties: {
137
149
  query: { type: "string", description: "The GraphQL query string" },
138
150
  variables: { type: "object", description: "Optional variables" },
151
+ channelToken: {
152
+ type: "string",
153
+ description: "Optional channel token to scope the request to a specific channel. Requires CHANNEL_API_KEY_MAP to be configured with a key for this channel.",
154
+ },
139
155
  },
140
156
  required: ["query"],
141
157
  },
@@ -151,6 +167,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
151
167
  description: "The GraphQL mutation string",
152
168
  },
153
169
  variables: { type: "object", description: "Optional variables" },
170
+ channelToken: {
171
+ type: "string",
172
+ description: "Optional channel token to scope the request to a specific channel. Requires CHANNEL_API_KEY_MAP to be configured with a key for this channel.",
173
+ },
154
174
  },
155
175
  required: ["mutation"],
156
176
  },
@@ -184,6 +204,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
184
204
  type: "number",
185
205
  description: "Max number of concurrent mutations. Default: 5.",
186
206
  },
207
+ channelToken: {
208
+ type: "string",
209
+ description: "Optional channel token to scope the request to a specific channel. Requires CHANNEL_API_KEY_MAP to be configured with a key for this channel.",
210
+ },
187
211
  },
188
212
  required: ["mutation", "ids"],
189
213
  },
@@ -229,7 +253,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
229
253
  content: [
230
254
  {
231
255
  type: "text",
232
- text: JSON.stringify(await adminQuery(args?.query, args?.variables), null, 2),
256
+ text: JSON.stringify(await adminQuery(args?.query, args?.variables, args?.channelToken), null, 2),
233
257
  },
234
258
  ],
235
259
  };
@@ -238,7 +262,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
238
262
  content: [
239
263
  {
240
264
  type: "text",
241
- text: JSON.stringify(await adminMutation(args?.mutation, args?.variables), null, 2),
265
+ text: JSON.stringify(await adminMutation(args?.mutation, args?.variables, args?.channelToken), null, 2),
242
266
  },
243
267
  ],
244
268
  };
@@ -247,7 +271,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
247
271
  content: [
248
272
  {
249
273
  type: "text",
250
- text: JSON.stringify(await adminBatchMutation(args?.mutation, args?.ids, args?.variableName || "id", args?.extraVariables, args?.concurrency || 5), null, 2),
274
+ text: JSON.stringify(await adminBatchMutation(args?.mutation, args?.ids, args?.variableName || "id", args?.extraVariables, args?.concurrency || 5, args?.channelToken), null, 2),
251
275
  },
252
276
  ],
253
277
  };
@@ -274,7 +298,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
274
298
  content: [
275
299
  {
276
300
  type: "text",
277
- text: JSON.stringify(await shopQuery(args?.query, args?.variables), null, 2),
301
+ text: JSON.stringify(await shopQuery(args?.query, args?.variables, args?.channelToken), null, 2),
278
302
  },
279
303
  ],
280
304
  };
@@ -283,7 +307,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
283
307
  content: [
284
308
  {
285
309
  type: "text",
286
- text: JSON.stringify(await shopMutation(args?.mutation, args?.variables), null, 2),
310
+ text: JSON.stringify(await shopMutation(args?.mutation, args?.variables, args?.channelToken), null, 2),
287
311
  },
288
312
  ],
289
313
  };
@@ -292,7 +316,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
292
316
  content: [
293
317
  {
294
318
  type: "text",
295
- text: JSON.stringify(await shopBatchMutation(args?.mutation, args?.ids, args?.variableName || "id", args?.extraVariables, args?.concurrency || 5), null, 2),
319
+ text: JSON.stringify(await shopBatchMutation(args?.mutation, args?.ids, args?.variableName || "id", args?.extraVariables, args?.concurrency || 5, args?.channelToken), null, 2),
296
320
  },
297
321
  ],
298
322
  };
@@ -346,6 +370,20 @@ async function main() {
346
370
  console.error(`API Key: ${process.env.VENDURE_API_KEY ? "Present" : "Missing"}`);
347
371
  console.error(`Admin URL: ${process.env.ADMIN_API_URL || "default"}`);
348
372
  console.error(`Shop URL: ${process.env.SHOP_API_URL || "default"}`);
373
+ // Log available channel API key mappings
374
+ const raw = process.env.CHANNEL_API_KEY_MAP;
375
+ if (raw) {
376
+ try {
377
+ const map = JSON.parse(raw);
378
+ const channels = Object.keys(map);
379
+ if (channels.length > 0) {
380
+ console.error(`Channel API keys configured for: ${channels.join(", ")}`);
381
+ }
382
+ }
383
+ catch {
384
+ console.error("Invalid CHANNEL_API_KEY_MAP environment variable");
385
+ }
386
+ }
349
387
  }
350
388
  main().catch((error) => {
351
389
  console.error("Server error:", error);
@@ -1,6 +1,12 @@
1
1
  /**
2
2
  * GraphQL Client for Vendure API
3
- * Handles authentication via API Key for both Admin and Shop APIs
3
+ * Handles authentication via API Key for both Admin and Shop APIs.
4
+ *
5
+ * Channel switching is done via per-channel API keys (CHANNEL_API_KEY_MAP)
6
+ * combined with the vendure-token header. The per-channel API key provides
7
+ * authentication scoped to the target channel, and the vendure-token header
8
+ * tells Vendure which channel context to use for the request.
9
+ * Both are required for proper channel switching.
4
10
  */
5
11
  export interface GraphQLResponse<T = any> {
6
12
  data?: T;
@@ -10,9 +16,10 @@ export interface GraphQLResponse<T = any> {
10
16
  }>;
11
17
  }
12
18
  export declare class GraphQLClient {
13
- private apiKey;
14
- constructor(apiKey?: string | null);
15
- request<T = any>(url: string, queryString: string, variables?: Record<string, any>): Promise<T>;
19
+ private defaultApiKey;
20
+ private channelApiKeys;
21
+ constructor(defaultApiKey?: string | null, channelApiKeys?: Record<string, string>);
22
+ request<T = any>(url: string, queryString: string, variables?: Record<string, any>, channelToken?: string): Promise<T>;
16
23
  }
17
24
  export declare function getClient(): GraphQLClient;
18
25
  export declare function getAdminUrl(): string;
@@ -1,18 +1,34 @@
1
1
  /**
2
2
  * GraphQL Client for Vendure API
3
- * Handles authentication via API Key for both Admin and Shop APIs
3
+ * Handles authentication via API Key for both Admin and Shop APIs.
4
+ *
5
+ * Channel switching is done via per-channel API keys (CHANNEL_API_KEY_MAP)
6
+ * combined with the vendure-token header. The per-channel API key provides
7
+ * authentication scoped to the target channel, and the vendure-token header
8
+ * tells Vendure which channel context to use for the request.
9
+ * Both are required for proper channel switching.
4
10
  */
5
11
  export class GraphQLClient {
6
- apiKey;
7
- constructor(apiKey = null) {
8
- this.apiKey = apiKey;
12
+ defaultApiKey;
13
+ channelApiKeys;
14
+ constructor(defaultApiKey = null, channelApiKeys = {}) {
15
+ this.defaultApiKey = defaultApiKey;
16
+ this.channelApiKeys = channelApiKeys;
9
17
  }
10
- async request(url, queryString, variables) {
18
+ async request(url, queryString, variables, channelToken) {
19
+ // Resolve API key: use channel-specific key if available, fall back to default
20
+ const apiKey = (channelToken && this.channelApiKeys[channelToken]) || this.defaultApiKey;
11
21
  const headers = {
12
22
  "Content-Type": "application/json",
13
23
  };
14
- if (this.apiKey) {
15
- headers["vendure-api-key"] = this.apiKey;
24
+ if (apiKey) {
25
+ headers["vendure-api-key"] = apiKey;
26
+ }
27
+ // Set vendure-token header when a channel token is provided and we have
28
+ // a mapped API key for it (ensuring the API key has access to this channel).
29
+ // The vendure-token header tells Vendure which channel context to use for the request.
30
+ if (channelToken) {
31
+ headers["vendure-token"] = channelToken;
16
32
  }
17
33
  const response = await fetch(url, {
18
34
  method: "POST",
@@ -36,11 +52,20 @@ export class GraphQLClient {
36
52
  // Singleton instance
37
53
  let clientInstance = null;
38
54
  export function getClient() {
39
- if (clientInstance) {
55
+ if (clientInstance)
40
56
  return clientInstance;
57
+ const defaultApiKey = process.env.VENDURE_API_KEY || null;
58
+ let channelApiKeys = {};
59
+ const raw = process.env.CHANNEL_API_KEY_MAP;
60
+ if (raw) {
61
+ try {
62
+ channelApiKeys = JSON.parse(raw);
63
+ }
64
+ catch (e) {
65
+ console.error("Invalid CHANNEL_API_KEY_MAP JSON:", e);
66
+ }
41
67
  }
42
- const apiKey = process.env.VENDURE_API_KEY || null;
43
- clientInstance = new GraphQLClient(apiKey);
68
+ clientInstance = new GraphQLClient(defaultApiKey, channelApiKeys);
44
69
  return clientInstance;
45
70
  }
46
71
  export function getAdminUrl() {
@@ -1,19 +1,19 @@
1
1
  /**
2
2
  * Execute a GraphQL query on the Admin API
3
3
  */
4
- export declare function adminQuery(queryString: string, variables?: Record<string, any>): Promise<any>;
4
+ export declare function adminQuery(queryString: string, variables?: Record<string, any>, channelToken?: string): Promise<any>;
5
5
  /**
6
6
  * Execute a GraphQL mutation on the Admin API
7
7
  */
8
- export declare function adminMutation(mutationString: string, variables?: Record<string, any>): Promise<any>;
8
+ export declare function adminMutation(mutationString: string, variables?: Record<string, any>, channelToken?: string): Promise<any>;
9
9
  /**
10
10
  * Execute a GraphQL query on the Shop API
11
11
  */
12
- export declare function shopQuery(queryString: string, variables?: Record<string, any>): Promise<any>;
12
+ export declare function shopQuery(queryString: string, variables?: Record<string, any>, channelToken?: string): Promise<any>;
13
13
  /**
14
14
  * Execute a GraphQL mutation on the Shop API
15
15
  */
16
- export declare function shopMutation(mutationString: string, variables?: Record<string, any>): Promise<any>;
16
+ export declare function shopMutation(mutationString: string, variables?: Record<string, any>, channelToken?: string): Promise<any>;
17
17
  /**
18
18
  * Fetch the Admin GraphQL schema introspection
19
19
  */
@@ -63,9 +63,9 @@ export interface BatchResult {
63
63
  * Execute a GraphQL mutation in batch for multiple IDs on the Admin API.
64
64
  * Runs mutations concurrently with configurable concurrency.
65
65
  */
66
- export declare function adminBatchMutation(mutationString: string, ids: string[], variableName?: string, extraVariables?: Record<string, any>, concurrency?: number): Promise<BatchResult>;
66
+ export declare function adminBatchMutation(mutationString: string, ids: string[], variableName?: string, extraVariables?: Record<string, any>, concurrency?: number, channelToken?: string): Promise<BatchResult>;
67
67
  /**
68
68
  * Execute a GraphQL mutation in batch for multiple IDs on the Shop API.
69
69
  * Runs mutations concurrently with configurable concurrency.
70
70
  */
71
- export declare function shopBatchMutation(mutationString: string, ids: string[], variableName?: string, extraVariables?: Record<string, any>, concurrency?: number): Promise<BatchResult>;
71
+ export declare function shopBatchMutation(mutationString: string, ids: string[], variableName?: string, extraVariables?: Record<string, any>, concurrency?: number, channelToken?: string): Promise<BatchResult>;
@@ -2,30 +2,30 @@ import { getClient, getAdminUrl, getShopUrl } from "../client.js";
2
2
  /**
3
3
  * Execute a GraphQL query on the Admin API
4
4
  */
5
- export async function adminQuery(queryString, variables) {
5
+ export async function adminQuery(queryString, variables, channelToken) {
6
6
  const client = getClient();
7
- return client.request(getAdminUrl(), queryString, variables);
7
+ return client.request(getAdminUrl(), queryString, variables, channelToken);
8
8
  }
9
9
  /**
10
10
  * Execute a GraphQL mutation on the Admin API
11
11
  */
12
- export async function adminMutation(mutationString, variables) {
12
+ export async function adminMutation(mutationString, variables, channelToken) {
13
13
  const client = getClient();
14
- return client.request(getAdminUrl(), mutationString, variables);
14
+ return client.request(getAdminUrl(), mutationString, variables, channelToken);
15
15
  }
16
16
  /**
17
17
  * Execute a GraphQL query on the Shop API
18
18
  */
19
- export async function shopQuery(queryString, variables) {
19
+ export async function shopQuery(queryString, variables, channelToken) {
20
20
  const client = getClient();
21
- return client.request(getShopUrl(), queryString, variables);
21
+ return client.request(getShopUrl(), queryString, variables, channelToken);
22
22
  }
23
23
  /**
24
24
  * Execute a GraphQL mutation on the Shop API
25
25
  */
26
- export async function shopMutation(mutationString, variables) {
26
+ export async function shopMutation(mutationString, variables, channelToken) {
27
27
  const client = getClient();
28
- return client.request(getShopUrl(), mutationString, variables);
28
+ return client.request(getShopUrl(), mutationString, variables, channelToken);
29
29
  }
30
30
  const introspectionQuery = `
31
31
  query IntrospectionQuery {
@@ -497,17 +497,17 @@ export async function describeOperation(operationName, api) {
497
497
  * Execute a GraphQL mutation in batch for multiple IDs on the Admin API.
498
498
  * Runs mutations concurrently with configurable concurrency.
499
499
  */
500
- export async function adminBatchMutation(mutationString, ids, variableName = "id", extraVariables, concurrency = 5) {
501
- return executeBatch(getAdminUrl(), mutationString, ids, variableName, extraVariables, concurrency);
500
+ export async function adminBatchMutation(mutationString, ids, variableName = "id", extraVariables, concurrency = 5, channelToken) {
501
+ return executeBatch(getAdminUrl(), mutationString, ids, variableName, extraVariables, concurrency, channelToken);
502
502
  }
503
503
  /**
504
504
  * Execute a GraphQL mutation in batch for multiple IDs on the Shop API.
505
505
  * Runs mutations concurrently with configurable concurrency.
506
506
  */
507
- export async function shopBatchMutation(mutationString, ids, variableName = "id", extraVariables, concurrency = 5) {
508
- return executeBatch(getShopUrl(), mutationString, ids, variableName, extraVariables, concurrency);
507
+ export async function shopBatchMutation(mutationString, ids, variableName = "id", extraVariables, concurrency = 5, channelToken) {
508
+ return executeBatch(getShopUrl(), mutationString, ids, variableName, extraVariables, concurrency, channelToken);
509
509
  }
510
- async function executeBatch(url, mutationString, ids, variableName, extraVariables, concurrency) {
510
+ async function executeBatch(url, mutationString, ids, variableName, extraVariables, concurrency, channelToken) {
511
511
  const client = getClient();
512
512
  const results = [];
513
513
  const queue = [...ids];
@@ -518,7 +518,7 @@ async function executeBatch(url, mutationString, ids, variableName, extraVariabl
518
518
  const id = queue.shift();
519
519
  const variables = { ...extraVariables, [variableName]: id };
520
520
  try {
521
- const data = await client.request(url, mutationString, variables);
521
+ const data = await client.request(url, mutationString, variables, channelToken);
522
522
  succeeded++;
523
523
  results.push({ id, success: true, data });
524
524
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vendure-mcp-graphql",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "MCP server for Vendure Admin and Shop GraphQL APIs",
5
5
  "type": "module",
6
6
  "bin": {