vendure-mcp-graphql 1.0.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 ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0 (2025-01-24)
4
+ - Initial public release of `vendure-mcp-graphql` (formerly `raptorr-graphql-mcp`).
5
+ - Standardized package structure for npm publication.
package/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # vendure-mcp-graphql
2
+
3
+ MCP (Model Context Protocol) server for interacting with Vendure GraphQL APIs (Admin & Shop).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g vendure-mcp-graphql
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Configure as an MCP server in your IDE (like Claude Desktop):
14
+
15
+ ```json
16
+ {
17
+ "mcpServers": {
18
+ "vendure-mcp-graphql": {
19
+ "command": "vendure-mcp-graphql",
20
+ "args": [],
21
+ "env": {
22
+ "VENDURE_URL": "http://localhost:3000/admin-api",
23
+ "VENDURE_AUTH_TOKEN": "your-token-here"
24
+ }
25
+ }
26
+ }
27
+ }
28
+ ```
29
+
30
+ ## Features
31
+
32
+ - Admin API access
33
+ - Shop API access
34
+ - GraphQL introspection and execution
35
+
36
+ ## License
37
+
38
+ MIT
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Raptorr GraphQL MCP Server
4
+ * Consolidated version for both Vendure Admin and Shop APIs
5
+ */
6
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Raptorr GraphQL MCP Server
4
+ * Consolidated version for both Vendure Admin and Shop APIs
5
+ */
6
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
7
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
+ import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js";
9
+ import { config } from "dotenv";
10
+ import { resolve, dirname } from "path";
11
+ import { fileURLToPath } from "url";
12
+ import { existsSync } from "fs";
13
+ // Load environment variables
14
+ const __dirname = dirname(fileURLToPath(import.meta.url));
15
+ // 1. Try to load from project root .env.local
16
+ config({ path: resolve(__dirname, "../../.env.local") });
17
+ // 2. Also try to load from specific vendure-api-key.env if it exists
18
+ const apiKeyPath = resolve(__dirname, "../../vendure-api-key.env");
19
+ if (existsSync(apiKeyPath)) {
20
+ const envConfig = config({ path: apiKeyPath });
21
+ if (envConfig.parsed?.VENDURE_API_KEY) {
22
+ process.env.VENDURE_API_KEY = envConfig.parsed.VENDURE_API_KEY;
23
+ }
24
+ }
25
+ import { adminQuery, adminMutation, getAdminSchema, getAdminOperations, shopQuery, shopMutation, getShopSchema, getShopOperations, } from "./src/tools/index.js";
26
+ const server = new Server({
27
+ name: "raptorr-graphql-mcp",
28
+ version: "3.1.0",
29
+ }, {
30
+ capabilities: {
31
+ tools: {},
32
+ },
33
+ });
34
+ // Define available tools
35
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
36
+ tools: [
37
+ {
38
+ name: "admin_query",
39
+ description: "Execute a GraphQL query on the Vendure Admin API.",
40
+ inputSchema: {
41
+ type: "object",
42
+ properties: {
43
+ query: { type: "string", description: "The GraphQL query string" },
44
+ variables: { type: "object", description: "Optional variables" },
45
+ },
46
+ required: ["query"],
47
+ },
48
+ },
49
+ {
50
+ name: "admin_mutation",
51
+ description: "Execute a GraphQL mutation on the Vendure Admin API.",
52
+ inputSchema: {
53
+ type: "object",
54
+ properties: {
55
+ mutation: {
56
+ type: "string",
57
+ description: "The GraphQL mutation string",
58
+ },
59
+ variables: { type: "object", description: "Optional variables" },
60
+ },
61
+ required: ["mutation"],
62
+ },
63
+ },
64
+ {
65
+ name: "get_admin_schema",
66
+ description: "Fetch the full Admin API GraphQL schema introspection. (Warning: Large output)",
67
+ inputSchema: { type: "object", properties: {}, required: [] },
68
+ },
69
+ {
70
+ name: "list_admin_operations",
71
+ description: "List all available queries and mutations for the Admin API with descriptions.",
72
+ inputSchema: { type: "object", properties: {}, required: [] },
73
+ },
74
+ {
75
+ name: "shop_query",
76
+ description: "Execute a GraphQL query on the Vendure Shop API.",
77
+ inputSchema: {
78
+ type: "object",
79
+ properties: {
80
+ query: { type: "string", description: "The GraphQL query string" },
81
+ variables: { type: "object", description: "Optional variables" },
82
+ },
83
+ required: ["query"],
84
+ },
85
+ },
86
+ {
87
+ name: "shop_mutation",
88
+ description: "Execute a GraphQL mutation on the Vendure Shop API.",
89
+ inputSchema: {
90
+ type: "object",
91
+ properties: {
92
+ mutation: {
93
+ type: "string",
94
+ description: "The GraphQL mutation string",
95
+ },
96
+ variables: { type: "object", description: "Optional variables" },
97
+ },
98
+ required: ["mutation"],
99
+ },
100
+ },
101
+ {
102
+ name: "get_shop_schema",
103
+ description: "Fetch the full Shop API GraphQL schema introspection. (Warning: Large output)",
104
+ inputSchema: { type: "object", properties: {}, required: [] },
105
+ },
106
+ {
107
+ name: "list_shop_operations",
108
+ description: "List all available queries and mutations for the Shop API with descriptions.",
109
+ inputSchema: { type: "object", properties: {}, required: [] },
110
+ },
111
+ ],
112
+ }));
113
+ // Handle tool calls
114
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
115
+ const { name, arguments: args } = request.params;
116
+ try {
117
+ switch (name) {
118
+ case "admin_query":
119
+ return {
120
+ content: [
121
+ {
122
+ type: "text",
123
+ text: JSON.stringify(await adminQuery(args?.query, args?.variables), null, 2),
124
+ },
125
+ ],
126
+ };
127
+ case "admin_mutation":
128
+ return {
129
+ content: [
130
+ {
131
+ type: "text",
132
+ text: JSON.stringify(await adminMutation(args?.mutation, args?.variables), null, 2),
133
+ },
134
+ ],
135
+ };
136
+ case "get_admin_schema":
137
+ return {
138
+ content: [
139
+ {
140
+ type: "text",
141
+ text: JSON.stringify(await getAdminSchema(), null, 2),
142
+ },
143
+ ],
144
+ };
145
+ case "list_admin_operations":
146
+ return {
147
+ content: [
148
+ {
149
+ type: "text",
150
+ text: JSON.stringify(await getAdminOperations(), null, 2),
151
+ },
152
+ ],
153
+ };
154
+ case "shop_query":
155
+ return {
156
+ content: [
157
+ {
158
+ type: "text",
159
+ text: JSON.stringify(await shopQuery(args?.query, args?.variables), null, 2),
160
+ },
161
+ ],
162
+ };
163
+ case "shop_mutation":
164
+ return {
165
+ content: [
166
+ {
167
+ type: "text",
168
+ text: JSON.stringify(await shopMutation(args?.mutation, args?.variables), null, 2),
169
+ },
170
+ ],
171
+ };
172
+ case "get_shop_schema":
173
+ return {
174
+ content: [
175
+ {
176
+ type: "text",
177
+ text: JSON.stringify(await getShopSchema(), null, 2),
178
+ },
179
+ ],
180
+ };
181
+ case "list_shop_operations":
182
+ return {
183
+ content: [
184
+ {
185
+ type: "text",
186
+ text: JSON.stringify(await getShopOperations(), null, 2),
187
+ },
188
+ ],
189
+ };
190
+ default:
191
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
192
+ }
193
+ }
194
+ catch (error) {
195
+ const message = error instanceof Error ? error.message : String(error);
196
+ return {
197
+ content: [{ type: "text", text: `Error: ${message}` }],
198
+ isError: true,
199
+ };
200
+ }
201
+ });
202
+ // Start the server
203
+ async function main() {
204
+ const transport = new StdioServerTransport();
205
+ await server.connect(transport);
206
+ console.error("Raptorr GraphQL MCP server running on stdio");
207
+ console.error(`API Key: ${process.env.VENDURE_API_KEY ? "Present" : "Missing"}`);
208
+ console.error(`Admin URL: ${process.env.ADMIN_API_URL || "default"}`);
209
+ console.error(`Shop URL: ${process.env.SHOP_API_URL || "default"}`);
210
+ }
211
+ main().catch((error) => {
212
+ console.error("Server error:", error);
213
+ process.exit(1);
214
+ });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * GraphQL Client for Vendure API
3
+ * Handles authentication via API Key for both Admin and Shop APIs
4
+ */
5
+ export interface GraphQLResponse<T = any> {
6
+ data?: T;
7
+ errors?: Array<{
8
+ message: string;
9
+ path?: string[];
10
+ }>;
11
+ }
12
+ 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>;
16
+ }
17
+ export declare function getClient(): GraphQLClient;
18
+ export declare function getAdminUrl(): string;
19
+ export declare function getShopUrl(): string;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * GraphQL Client for Vendure API
3
+ * Handles authentication via API Key for both Admin and Shop APIs
4
+ */
5
+ export class GraphQLClient {
6
+ apiKey;
7
+ constructor(apiKey = null) {
8
+ this.apiKey = apiKey;
9
+ }
10
+ async request(url, queryString, variables) {
11
+ const headers = {
12
+ "Content-Type": "application/json",
13
+ };
14
+ if (this.apiKey) {
15
+ headers["vendure-api-key"] = this.apiKey;
16
+ }
17
+ const response = await fetch(url, {
18
+ method: "POST",
19
+ headers,
20
+ body: JSON.stringify({
21
+ query: queryString,
22
+ variables,
23
+ }),
24
+ });
25
+ if (!response.ok) {
26
+ const text = await response.text();
27
+ throw new Error(`HTTP Error ${response.status}: ${text}`);
28
+ }
29
+ const result = (await response.json());
30
+ if (result.errors && result.errors.length > 0) {
31
+ throw new Error(result.errors.map((e) => e.message).join(", "));
32
+ }
33
+ return result.data;
34
+ }
35
+ }
36
+ // Singleton instance
37
+ let clientInstance = null;
38
+ export function getClient() {
39
+ if (clientInstance) {
40
+ return clientInstance;
41
+ }
42
+ const apiKey = process.env.VENDURE_API_KEY || null;
43
+ clientInstance = new GraphQLClient(apiKey);
44
+ return clientInstance;
45
+ }
46
+ export function getAdminUrl() {
47
+ return process.env.ADMIN_API_URL || "http://localhost:3000/admin-api";
48
+ }
49
+ export function getShopUrl() {
50
+ return process.env.SHOP_API_URL || "http://localhost:3000/shop-api";
51
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Execute a GraphQL query on the Admin API
3
+ */
4
+ export declare function adminQuery(queryString: string, variables?: Record<string, any>): Promise<any>;
5
+ /**
6
+ * Execute a GraphQL mutation on the Admin API
7
+ */
8
+ export declare function adminMutation(mutationString: string, variables?: Record<string, any>): Promise<any>;
9
+ /**
10
+ * Execute a GraphQL query on the Shop API
11
+ */
12
+ export declare function shopQuery(queryString: string, variables?: Record<string, any>): Promise<any>;
13
+ /**
14
+ * Execute a GraphQL mutation on the Shop API
15
+ */
16
+ export declare function shopMutation(mutationString: string, variables?: Record<string, any>): Promise<any>;
17
+ /**
18
+ * Fetch the Admin GraphQL schema introspection
19
+ */
20
+ export declare function getAdminSchema(): Promise<any>;
21
+ /**
22
+ * Fetch the Shop GraphQL schema introspection
23
+ */
24
+ export declare function getShopSchema(): Promise<any>;
25
+ /**
26
+ * Fetch a summary of available queries and mutations for the Admin API
27
+ */
28
+ export declare function getAdminOperations(): Promise<any>;
29
+ /**
30
+ * Fetch a summary of available queries and mutations for the Shop API
31
+ */
32
+ export declare function getShopOperations(): Promise<any>;
@@ -0,0 +1,168 @@
1
+ import { getClient, getAdminUrl, getShopUrl } from "../client.js";
2
+ /**
3
+ * Execute a GraphQL query on the Admin API
4
+ */
5
+ export async function adminQuery(queryString, variables) {
6
+ const client = getClient();
7
+ return client.request(getAdminUrl(), queryString, variables);
8
+ }
9
+ /**
10
+ * Execute a GraphQL mutation on the Admin API
11
+ */
12
+ export async function adminMutation(mutationString, variables) {
13
+ const client = getClient();
14
+ return client.request(getAdminUrl(), mutationString, variables);
15
+ }
16
+ /**
17
+ * Execute a GraphQL query on the Shop API
18
+ */
19
+ export async function shopQuery(queryString, variables) {
20
+ const client = getClient();
21
+ return client.request(getShopUrl(), queryString, variables);
22
+ }
23
+ /**
24
+ * Execute a GraphQL mutation on the Shop API
25
+ */
26
+ export async function shopMutation(mutationString, variables) {
27
+ const client = getClient();
28
+ return client.request(getShopUrl(), mutationString, variables);
29
+ }
30
+ const introspectionQuery = `
31
+ query IntrospectionQuery {
32
+ __schema {
33
+ queryType { name }
34
+ mutationType { name }
35
+ subscriptionType { name }
36
+ types {
37
+ ...FullType
38
+ }
39
+ directives {
40
+ name
41
+ description
42
+ locations
43
+ args {
44
+ ...InputValue
45
+ }
46
+ }
47
+ }
48
+ }
49
+
50
+ fragment FullType on __Type {
51
+ kind
52
+ name
53
+ description
54
+ fields(includeDeprecated: true) {
55
+ name
56
+ description
57
+ args {
58
+ ...InputValue
59
+ }
60
+ type {
61
+ ...TypeRef
62
+ }
63
+ isDeprecated
64
+ deprecationReason
65
+ }
66
+ inputFields {
67
+ ...InputValue
68
+ }
69
+ interfaces {
70
+ ...TypeRef
71
+ }
72
+ enums {
73
+ name
74
+ description
75
+ isDeprecated
76
+ deprecationReason
77
+ }
78
+ possibleTypes {
79
+ ...TypeRef
80
+ }
81
+ }
82
+
83
+ fragment InputValue on __InputValue {
84
+ name
85
+ description
86
+ type { ...TypeRef }
87
+ defaultValue
88
+ }
89
+
90
+ fragment TypeRef on __Type {
91
+ kind
92
+ name
93
+ ofType {
94
+ kind
95
+ name
96
+ ofType {
97
+ kind
98
+ name
99
+ ofType {
100
+ kind
101
+ name
102
+ ofType {
103
+ kind
104
+ name
105
+ ofType {
106
+ kind
107
+ name
108
+ ofType {
109
+ kind
110
+ name
111
+ ofType {
112
+ kind
113
+ name
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
122
+ `;
123
+ const operationsSummaryQuery = `
124
+ query OperationsSummary {
125
+ __schema {
126
+ queryType {
127
+ fields {
128
+ name
129
+ description
130
+ }
131
+ }
132
+ mutationType {
133
+ fields {
134
+ name
135
+ description
136
+ }
137
+ }
138
+ }
139
+ }
140
+ `;
141
+ /**
142
+ * Fetch the Admin GraphQL schema introspection
143
+ */
144
+ export async function getAdminSchema() {
145
+ const client = getClient();
146
+ return client.request(getAdminUrl(), introspectionQuery);
147
+ }
148
+ /**
149
+ * Fetch the Shop GraphQL schema introspection
150
+ */
151
+ export async function getShopSchema() {
152
+ const client = getClient();
153
+ return client.request(getShopUrl(), introspectionQuery);
154
+ }
155
+ /**
156
+ * Fetch a summary of available queries and mutations for the Admin API
157
+ */
158
+ export async function getAdminOperations() {
159
+ const client = getClient();
160
+ return client.request(getAdminUrl(), operationsSummaryQuery);
161
+ }
162
+ /**
163
+ * Fetch a summary of available queries and mutations for the Shop API
164
+ */
165
+ export async function getShopOperations() {
166
+ const client = getClient();
167
+ return client.request(getShopUrl(), operationsSummaryQuery);
168
+ }
@@ -0,0 +1 @@
1
+ export * from "./graphql-operations.js";
@@ -0,0 +1 @@
1
+ export * from "./graphql-operations.js";
@@ -0,0 +1,45 @@
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
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Type definitions for GraphQL entities
3
+ */
4
+ export {};
package/index.ts ADDED
@@ -0,0 +1,275 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Vendure GraphQL MCP Server
4
+ * Consolidated version for both Vendure Admin and Shop APIs
5
+ */
6
+
7
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+ import {
10
+ CallToolRequestSchema,
11
+ ListToolsRequestSchema,
12
+ ErrorCode,
13
+ McpError,
14
+ } from "@modelcontextprotocol/sdk/types.js";
15
+ import { config } from "dotenv";
16
+ import { resolve, dirname } from "path";
17
+ import { fileURLToPath } from "url";
18
+ import { existsSync } from "fs";
19
+
20
+ // Load environment variables
21
+ const __dirname = dirname(fileURLToPath(import.meta.url));
22
+
23
+ // 1. Try to load from project root .env.local
24
+ config({ path: resolve(__dirname, "../../.env.local") });
25
+
26
+ // 2. Also try to load from specific vendure-api-key.env if it exists
27
+ const apiKeyPath = resolve(__dirname, "../../vendure-api-key.env");
28
+ if (existsSync(apiKeyPath)) {
29
+ const envConfig = config({ path: apiKeyPath });
30
+ if (envConfig.parsed?.VENDURE_API_KEY) {
31
+ process.env.VENDURE_API_KEY = envConfig.parsed.VENDURE_API_KEY;
32
+ }
33
+ }
34
+
35
+ import {
36
+ adminQuery,
37
+ adminMutation,
38
+ getAdminSchema,
39
+ getAdminOperations,
40
+ shopQuery,
41
+ shopMutation,
42
+ getShopSchema,
43
+ getShopOperations,
44
+ } from "./src/tools/index.js";
45
+
46
+ const server = new Server(
47
+ {
48
+ name: "vendure-mcp-graphql",
49
+ version: "1.0.0",
50
+ },
51
+ {
52
+ capabilities: {
53
+ tools: {},
54
+ },
55
+ },
56
+ );
57
+
58
+ // Define available tools
59
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
60
+ tools: [
61
+ {
62
+ name: "admin_query",
63
+ description: "Execute a GraphQL query on the Vendure Admin API.",
64
+ inputSchema: {
65
+ type: "object",
66
+ properties: {
67
+ query: { type: "string", description: "The GraphQL query string" },
68
+ variables: { type: "object", description: "Optional variables" },
69
+ },
70
+ required: ["query"],
71
+ },
72
+ },
73
+ {
74
+ name: "admin_mutation",
75
+ description: "Execute a GraphQL mutation on the Vendure Admin API.",
76
+ inputSchema: {
77
+ type: "object",
78
+ properties: {
79
+ mutation: {
80
+ type: "string",
81
+ description: "The GraphQL mutation string",
82
+ },
83
+ variables: { type: "object", description: "Optional variables" },
84
+ },
85
+ required: ["mutation"],
86
+ },
87
+ },
88
+ {
89
+ name: "get_admin_schema",
90
+ description:
91
+ "Fetch the full Admin API GraphQL schema introspection. (Warning: Large output)",
92
+ inputSchema: { type: "object", properties: {}, required: [] },
93
+ },
94
+ {
95
+ name: "list_admin_operations",
96
+ description:
97
+ "List all available queries and mutations for the Admin API with descriptions.",
98
+ inputSchema: { type: "object", properties: {}, required: [] },
99
+ },
100
+ {
101
+ name: "shop_query",
102
+ description: "Execute a GraphQL query on the Vendure Shop API.",
103
+ inputSchema: {
104
+ type: "object",
105
+ properties: {
106
+ query: { type: "string", description: "The GraphQL query string" },
107
+ variables: { type: "object", description: "Optional variables" },
108
+ },
109
+ required: ["query"],
110
+ },
111
+ },
112
+ {
113
+ name: "shop_mutation",
114
+ description: "Execute a GraphQL mutation on the Vendure Shop API.",
115
+ inputSchema: {
116
+ type: "object",
117
+ properties: {
118
+ mutation: {
119
+ type: "string",
120
+ description: "The GraphQL mutation string",
121
+ },
122
+ variables: { type: "object", description: "Optional variables" },
123
+ },
124
+ required: ["mutation"],
125
+ },
126
+ },
127
+ {
128
+ name: "get_shop_schema",
129
+ description:
130
+ "Fetch the full Shop API GraphQL schema introspection. (Warning: Large output)",
131
+ inputSchema: { type: "object", properties: {}, required: [] },
132
+ },
133
+ {
134
+ name: "list_shop_operations",
135
+ description:
136
+ "List all available queries and mutations for the Shop API with descriptions.",
137
+ inputSchema: { type: "object", properties: {}, required: [] },
138
+ },
139
+ ],
140
+ }));
141
+
142
+ // Handle tool calls
143
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
144
+ const { name, arguments: args } = request.params;
145
+
146
+ try {
147
+ switch (name) {
148
+ case "admin_query":
149
+ return {
150
+ content: [
151
+ {
152
+ type: "text",
153
+ text: JSON.stringify(
154
+ await adminQuery(
155
+ args?.query as string,
156
+ args?.variables as Record<string, any>,
157
+ ),
158
+ null,
159
+ 2,
160
+ ),
161
+ },
162
+ ],
163
+ };
164
+ case "admin_mutation":
165
+ return {
166
+ content: [
167
+ {
168
+ type: "text",
169
+ text: JSON.stringify(
170
+ await adminMutation(
171
+ args?.mutation as string,
172
+ args?.variables as Record<string, any>,
173
+ ),
174
+ null,
175
+ 2,
176
+ ),
177
+ },
178
+ ],
179
+ };
180
+ case "get_admin_schema":
181
+ return {
182
+ content: [
183
+ {
184
+ type: "text",
185
+ text: JSON.stringify(await getAdminSchema(), null, 2),
186
+ },
187
+ ],
188
+ };
189
+ case "list_admin_operations":
190
+ return {
191
+ content: [
192
+ {
193
+ type: "text",
194
+ text: JSON.stringify(await getAdminOperations(), null, 2),
195
+ },
196
+ ],
197
+ };
198
+ case "shop_query":
199
+ return {
200
+ content: [
201
+ {
202
+ type: "text",
203
+ text: JSON.stringify(
204
+ await shopQuery(
205
+ args?.query as string,
206
+ args?.variables as Record<string, any>,
207
+ ),
208
+ null,
209
+ 2,
210
+ ),
211
+ },
212
+ ],
213
+ };
214
+ case "shop_mutation":
215
+ return {
216
+ content: [
217
+ {
218
+ type: "text",
219
+ text: JSON.stringify(
220
+ await shopMutation(
221
+ args?.mutation as string,
222
+ args?.variables as Record<string, any>,
223
+ ),
224
+ null,
225
+ 2,
226
+ ),
227
+ },
228
+ ],
229
+ };
230
+ case "get_shop_schema":
231
+ return {
232
+ content: [
233
+ {
234
+ type: "text",
235
+ text: JSON.stringify(await getShopSchema(), null, 2),
236
+ },
237
+ ],
238
+ };
239
+ case "list_shop_operations":
240
+ return {
241
+ content: [
242
+ {
243
+ type: "text",
244
+ text: JSON.stringify(await getShopOperations(), null, 2),
245
+ },
246
+ ],
247
+ };
248
+ default:
249
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
250
+ }
251
+ } catch (error) {
252
+ const message = error instanceof Error ? error.message : String(error);
253
+ return {
254
+ content: [{ type: "text", text: `Error: ${message}` }],
255
+ isError: true,
256
+ };
257
+ }
258
+ });
259
+
260
+ // Start the server
261
+ async function main() {
262
+ const transport = new StdioServerTransport();
263
+ await server.connect(transport);
264
+ console.error("Vendure GraphQL MCP server running on stdio");
265
+ console.error(
266
+ `API Key: ${process.env.VENDURE_API_KEY ? "Present" : "Missing"}`,
267
+ );
268
+ console.error(`Admin URL: ${process.env.ADMIN_API_URL || "default"}`);
269
+ console.error(`Shop URL: ${process.env.SHOP_API_URL || "default"}`);
270
+ }
271
+
272
+ main().catch((error) => {
273
+ console.error("Server error:", error);
274
+ process.exit(1);
275
+ });
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "vendure-mcp-graphql",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for Vendure Admin and Shop GraphQL APIs",
5
+ "type": "module",
6
+ "bin": {
7
+ "vendure-mcp-graphql": "./dist/index.js"
8
+ },
9
+ "main": "dist/index.js",
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "node dist/index.js",
13
+ "dev": "tsx index.ts"
14
+ },
15
+ "dependencies": {
16
+ "@modelcontextprotocol/sdk": "^1.0.0",
17
+ "dotenv": "^16.3.1"
18
+ },
19
+ "devDependencies": {
20
+ "@types/node": "^20.10.0",
21
+ "tsx": "^4.7.0",
22
+ "typescript": "^5.3.0"
23
+ }
24
+ }
package/src/client.ts ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * GraphQL Client for Vendure API
3
+ * Handles authentication via API Key for both Admin and Shop APIs
4
+ */
5
+
6
+ export interface GraphQLResponse<T = any> {
7
+ data?: T;
8
+ errors?: Array<{ message: string; path?: string[] }>;
9
+ }
10
+
11
+ export class GraphQLClient {
12
+ constructor(private apiKey: string | null = null) {}
13
+
14
+ async request<T = any>(
15
+ url: string,
16
+ queryString: string,
17
+ variables?: Record<string, any>,
18
+ ): Promise<T> {
19
+ const headers: Record<string, string> = {
20
+ "Content-Type": "application/json",
21
+ };
22
+
23
+ if (this.apiKey) {
24
+ headers["vendure-api-key"] = this.apiKey;
25
+ }
26
+
27
+ const response = await fetch(url, {
28
+ method: "POST",
29
+ headers,
30
+ body: JSON.stringify({
31
+ query: queryString,
32
+ variables,
33
+ }),
34
+ });
35
+
36
+ if (!response.ok) {
37
+ const text = await response.text();
38
+ throw new Error(`HTTP Error ${response.status}: ${text}`);
39
+ }
40
+
41
+ const result = (await response.json()) as GraphQLResponse<T>;
42
+
43
+ if (result.errors && result.errors.length > 0) {
44
+ throw new Error(result.errors.map((e) => e.message).join(", "));
45
+ }
46
+
47
+ return result.data as T;
48
+ }
49
+ }
50
+
51
+ // Singleton instance
52
+ let clientInstance: GraphQLClient | null = null;
53
+
54
+ export function getClient(): GraphQLClient {
55
+ if (clientInstance) {
56
+ return clientInstance;
57
+ }
58
+
59
+ const apiKey = process.env.VENDURE_API_KEY || null;
60
+ clientInstance = new GraphQLClient(apiKey);
61
+ return clientInstance;
62
+ }
63
+
64
+ export function getAdminUrl(): string {
65
+ return process.env.ADMIN_API_URL || "http://localhost:3000/admin-api";
66
+ }
67
+
68
+ export function getShopUrl(): string {
69
+ return process.env.SHOP_API_URL || "http://localhost:3000/shop-api";
70
+ }
@@ -0,0 +1,190 @@
1
+ import { getClient, getAdminUrl, getShopUrl } from "../client.js";
2
+
3
+ /**
4
+ * Execute a GraphQL query on the Admin API
5
+ */
6
+ export async function adminQuery(
7
+ queryString: string,
8
+ variables?: Record<string, any>,
9
+ ): Promise<any> {
10
+ const client = getClient();
11
+ return client.request(getAdminUrl(), queryString, variables);
12
+ }
13
+
14
+ /**
15
+ * Execute a GraphQL mutation on the Admin API
16
+ */
17
+ export async function adminMutation(
18
+ mutationString: string,
19
+ variables?: Record<string, any>,
20
+ ): Promise<any> {
21
+ const client = getClient();
22
+ return client.request(getAdminUrl(), mutationString, variables);
23
+ }
24
+
25
+ /**
26
+ * Execute a GraphQL query on the Shop API
27
+ */
28
+ export async function shopQuery(
29
+ queryString: string,
30
+ variables?: Record<string, any>,
31
+ ): Promise<any> {
32
+ const client = getClient();
33
+ return client.request(getShopUrl(), queryString, variables);
34
+ }
35
+
36
+ /**
37
+ * Execute a GraphQL mutation on the Shop API
38
+ */
39
+ export async function shopMutation(
40
+ mutationString: string,
41
+ variables?: Record<string, any>,
42
+ ): Promise<any> {
43
+ const client = getClient();
44
+ return client.request(getShopUrl(), mutationString, variables);
45
+ }
46
+
47
+ const introspectionQuery = `
48
+ query IntrospectionQuery {
49
+ __schema {
50
+ queryType { name }
51
+ mutationType { name }
52
+ subscriptionType { name }
53
+ types {
54
+ ...FullType
55
+ }
56
+ directives {
57
+ name
58
+ description
59
+ locations
60
+ args {
61
+ ...InputValue
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ fragment FullType on __Type {
68
+ kind
69
+ name
70
+ description
71
+ fields(includeDeprecated: true) {
72
+ name
73
+ description
74
+ args {
75
+ ...InputValue
76
+ }
77
+ type {
78
+ ...TypeRef
79
+ }
80
+ isDeprecated
81
+ deprecationReason
82
+ }
83
+ inputFields {
84
+ ...InputValue
85
+ }
86
+ interfaces {
87
+ ...TypeRef
88
+ }
89
+ enums {
90
+ name
91
+ description
92
+ isDeprecated
93
+ deprecationReason
94
+ }
95
+ possibleTypes {
96
+ ...TypeRef
97
+ }
98
+ }
99
+
100
+ fragment InputValue on __InputValue {
101
+ name
102
+ description
103
+ type { ...TypeRef }
104
+ defaultValue
105
+ }
106
+
107
+ fragment TypeRef on __Type {
108
+ kind
109
+ name
110
+ ofType {
111
+ kind
112
+ name
113
+ ofType {
114
+ kind
115
+ name
116
+ ofType {
117
+ kind
118
+ name
119
+ ofType {
120
+ kind
121
+ name
122
+ ofType {
123
+ kind
124
+ name
125
+ ofType {
126
+ kind
127
+ name
128
+ ofType {
129
+ kind
130
+ name
131
+ }
132
+ }
133
+ }
134
+ }
135
+ }
136
+ }
137
+ }
138
+ }
139
+ `;
140
+
141
+ const operationsSummaryQuery = `
142
+ query OperationsSummary {
143
+ __schema {
144
+ queryType {
145
+ fields {
146
+ name
147
+ description
148
+ }
149
+ }
150
+ mutationType {
151
+ fields {
152
+ name
153
+ description
154
+ }
155
+ }
156
+ }
157
+ }
158
+ `;
159
+
160
+ /**
161
+ * Fetch the Admin GraphQL schema introspection
162
+ */
163
+ export async function getAdminSchema(): Promise<any> {
164
+ const client = getClient();
165
+ return client.request(getAdminUrl(), introspectionQuery);
166
+ }
167
+
168
+ /**
169
+ * Fetch the Shop GraphQL schema introspection
170
+ */
171
+ export async function getShopSchema(): Promise<any> {
172
+ const client = getClient();
173
+ return client.request(getShopUrl(), introspectionQuery);
174
+ }
175
+
176
+ /**
177
+ * Fetch a summary of available queries and mutations for the Admin API
178
+ */
179
+ export async function getAdminOperations(): Promise<any> {
180
+ const client = getClient();
181
+ return client.request(getAdminUrl(), operationsSummaryQuery);
182
+ }
183
+
184
+ /**
185
+ * Fetch a summary of available queries and mutations for the Shop API
186
+ */
187
+ export async function getShopOperations(): Promise<any> {
188
+ const client = getClient();
189
+ return client.request(getShopUrl(), operationsSummaryQuery);
190
+ }
@@ -0,0 +1 @@
1
+ export * from "./graphql-operations.js";
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "node",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "outDir": "./dist",
9
+ "rootDir": ".",
10
+ "declaration": true,
11
+ "skipLibCheck": true
12
+ },
13
+ "include": [
14
+ "*.ts",
15
+ "src/**/*.ts"
16
+ ],
17
+ "exclude": [
18
+ "node_modules",
19
+ "dist"
20
+ ]
21
+ }