shopify-store-mcp 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/LICENSE +7 -0
- package/README.md +172 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.js +65 -0
- package/dist/db.d.ts +19 -0
- package/dist/db.js +161 -0
- package/dist/errors.d.ts +36 -0
- package/dist/errors.js +93 -0
- package/dist/graphql/admin/common/collections.d.ts +8 -0
- package/dist/graphql/admin/common/collections.js +44 -0
- package/dist/graphql/admin/common/customers.d.ts +13 -0
- package/dist/graphql/admin/common/customers.js +112 -0
- package/dist/graphql/admin/common/orders.d.ts +13 -0
- package/dist/graphql/admin/common/orders.js +142 -0
- package/dist/graphql/admin/common/products.d.ts +23 -0
- package/dist/graphql/admin/common/products.js +159 -0
- package/dist/graphql/admin/common/shop.d.ts +7 -0
- package/dist/graphql/admin/common/shop.js +38 -0
- package/dist/graphql/admin/index.d.ts +15 -0
- package/dist/graphql/admin/index.js +18 -0
- package/dist/graphql/admin/specialized/bulk.d.ts +33 -0
- package/dist/graphql/admin/specialized/bulk.js +132 -0
- package/dist/graphql/admin/specialized/files.d.ts +22 -0
- package/dist/graphql/admin/specialized/files.js +170 -0
- package/dist/graphql/admin/specialized/inventory.d.ts +13 -0
- package/dist/graphql/admin/specialized/inventory.js +78 -0
- package/dist/graphql/admin/specialized/metafields.d.ts +22 -0
- package/dist/graphql/admin/specialized/metafields.js +100 -0
- package/dist/graphql/admin/specialized/metaobjects.d.ts +36 -0
- package/dist/graphql/admin/specialized/metaobjects.js +239 -0
- package/dist/graphql/admin/specialized/search.d.ts +21 -0
- package/dist/graphql/admin/specialized/search.js +100 -0
- package/dist/graphql/collections.d.ts +1 -0
- package/dist/graphql/collections.js +37 -0
- package/dist/graphql/customers.d.ts +2 -0
- package/dist/graphql/customers.js +98 -0
- package/dist/graphql/inventory.d.ts +2 -0
- package/dist/graphql/inventory.js +67 -0
- package/dist/graphql/metafields.d.ts +2 -0
- package/dist/graphql/metafields.js +43 -0
- package/dist/graphql/orders.d.ts +2 -0
- package/dist/graphql/orders.js +116 -0
- package/dist/graphql/products.d.ts +4 -0
- package/dist/graphql/products.js +140 -0
- package/dist/graphql/shop.d.ts +1 -0
- package/dist/graphql/shop.js +32 -0
- package/dist/graphql/storefront/common/cart.d.ts +23 -0
- package/dist/graphql/storefront/common/cart.js +210 -0
- package/dist/graphql/storefront/common/collections.d.ts +11 -0
- package/dist/graphql/storefront/common/collections.js +114 -0
- package/dist/graphql/storefront/common/products.d.ts +14 -0
- package/dist/graphql/storefront/common/products.js +155 -0
- package/dist/graphql/storefront/index.d.ts +7 -0
- package/dist/graphql/storefront/index.js +8 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +97 -0
- package/dist/logger.d.ts +58 -0
- package/dist/logger.js +165 -0
- package/dist/prompts/index.d.ts +2 -0
- package/dist/prompts/index.js +169 -0
- package/dist/queue.d.ts +73 -0
- package/dist/queue.js +120 -0
- package/dist/resources/index.d.ts +3 -0
- package/dist/resources/index.js +180 -0
- package/dist/shopify-client.d.ts +16 -0
- package/dist/shopify-client.js +39 -0
- package/dist/tools/graphql.d.ts +3 -0
- package/dist/tools/graphql.js +41 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.js +23 -0
- package/dist/tools/infrastructure.d.ts +6 -0
- package/dist/tools/infrastructure.js +215 -0
- package/dist/tools/shop.d.ts +3 -0
- package/dist/tools/shop.js +28 -0
- package/dist/tools/smart-bulk.d.ts +7 -0
- package/dist/tools/smart-bulk.js +286 -0
- package/dist/tools/smart-files.d.ts +7 -0
- package/dist/tools/smart-files.js +169 -0
- package/dist/tools/smart-metaobjects.d.ts +7 -0
- package/dist/tools/smart-metaobjects.js +186 -0
- package/dist/tools/smart-schema.d.ts +7 -0
- package/dist/tools/smart-schema.js +138 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.js +2 -0
- package/dist/utils/polling.d.ts +53 -0
- package/dist/utils/polling.js +77 -0
- package/package.json +83 -0
- package/prisma/schema.prisma +82 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart schema discovery tool
|
|
3
|
+
* Combines metafield definitions and metaobject definitions in one call
|
|
4
|
+
*/
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { formatSuccessResponse, formatGraphQLErrors, formatErrorResponse, } from "../errors.js";
|
|
7
|
+
import { GET_METAFIELD_DEFINITIONS, GET_METAOBJECT_DEFINITIONS, } from "../graphql/admin/index.js";
|
|
8
|
+
import { enqueue } from "../queue.js";
|
|
9
|
+
import { logOperation } from "../logger.js";
|
|
10
|
+
// Valid metafield owner types
|
|
11
|
+
const METAFIELD_OWNER_TYPES = [
|
|
12
|
+
"PRODUCT",
|
|
13
|
+
"PRODUCTVARIANT",
|
|
14
|
+
"COLLECTION",
|
|
15
|
+
"CUSTOMER",
|
|
16
|
+
"ORDER",
|
|
17
|
+
"DRAFTORDER",
|
|
18
|
+
"SHOP",
|
|
19
|
+
"COMPANY",
|
|
20
|
+
"LOCATION",
|
|
21
|
+
"MARKET",
|
|
22
|
+
"PAGE",
|
|
23
|
+
"BLOG",
|
|
24
|
+
"ARTICLE",
|
|
25
|
+
"DISCOUNT",
|
|
26
|
+
];
|
|
27
|
+
export function registerSmartSchemaTools(server, client, storeDomain) {
|
|
28
|
+
server.registerTool("schema_discover", {
|
|
29
|
+
title: "Discover Store Schema",
|
|
30
|
+
description: "Discover custom schema in the store: metafield definitions and metaobject types. " +
|
|
31
|
+
"This is a SMART tool that combines multiple discovery queries in one call. " +
|
|
32
|
+
"Use this to understand what custom fields and data types are available in the store " +
|
|
33
|
+
"before working with metafields or metaobjects.",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
includeMetafields: z
|
|
36
|
+
.boolean()
|
|
37
|
+
.default(true)
|
|
38
|
+
.describe("Include metafield definitions in the response"),
|
|
39
|
+
includeMetaobjects: z
|
|
40
|
+
.boolean()
|
|
41
|
+
.default(true)
|
|
42
|
+
.describe("Include metaobject definitions in the response"),
|
|
43
|
+
metafieldOwnerTypes: z
|
|
44
|
+
.array(z.enum(METAFIELD_OWNER_TYPES))
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("Filter metafield definitions by owner type(s). " +
|
|
47
|
+
"If not provided, fetches definitions for PRODUCT, CUSTOMER, ORDER, and SHOP."),
|
|
48
|
+
},
|
|
49
|
+
annotations: {
|
|
50
|
+
readOnlyHint: true,
|
|
51
|
+
destructiveHint: false,
|
|
52
|
+
idempotentHint: true,
|
|
53
|
+
openWorldHint: false,
|
|
54
|
+
},
|
|
55
|
+
}, async ({ includeMetafields, includeMetaobjects, metafieldOwnerTypes }) => {
|
|
56
|
+
const startTime = Date.now();
|
|
57
|
+
try {
|
|
58
|
+
const result = {};
|
|
59
|
+
// Fetch metafield definitions
|
|
60
|
+
if (includeMetafields) {
|
|
61
|
+
const ownerTypes = metafieldOwnerTypes || ["PRODUCT", "CUSTOMER", "ORDER", "SHOP"];
|
|
62
|
+
const metafieldDefs = {};
|
|
63
|
+
for (const ownerType of ownerTypes) {
|
|
64
|
+
const response = await enqueue(() => client.request(GET_METAFIELD_DEFINITIONS, {
|
|
65
|
+
variables: {
|
|
66
|
+
ownerType,
|
|
67
|
+
first: 100,
|
|
68
|
+
},
|
|
69
|
+
}));
|
|
70
|
+
if (response.errors) {
|
|
71
|
+
console.error(`[schema_discover] Error fetching metafield definitions for ${ownerType}:`, response.errors);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const data = response.data;
|
|
75
|
+
metafieldDefs[ownerType] = data.metafieldDefinitions.edges.map((edge) => edge.node);
|
|
76
|
+
}
|
|
77
|
+
result.metafieldDefinitions = metafieldDefs;
|
|
78
|
+
}
|
|
79
|
+
// Fetch metaobject definitions
|
|
80
|
+
if (includeMetaobjects) {
|
|
81
|
+
const response = await enqueue(() => client.request(GET_METAOBJECT_DEFINITIONS, {
|
|
82
|
+
variables: { first: 100 },
|
|
83
|
+
}));
|
|
84
|
+
if (response.errors) {
|
|
85
|
+
await logOperation({
|
|
86
|
+
storeDomain,
|
|
87
|
+
toolName: "schema_discover",
|
|
88
|
+
query: GET_METAOBJECT_DEFINITIONS,
|
|
89
|
+
variables: {},
|
|
90
|
+
response,
|
|
91
|
+
success: false,
|
|
92
|
+
errorMessage: "GraphQL errors fetching metaobject definitions",
|
|
93
|
+
durationMs: Date.now() - startTime,
|
|
94
|
+
});
|
|
95
|
+
return formatGraphQLErrors(response);
|
|
96
|
+
}
|
|
97
|
+
const data = response.data;
|
|
98
|
+
result.metaobjectDefinitions = data.metaobjectDefinitions.edges.map((edge) => edge.node);
|
|
99
|
+
}
|
|
100
|
+
// Build summary
|
|
101
|
+
const summary = [];
|
|
102
|
+
if (result.metafieldDefinitions) {
|
|
103
|
+
const totalDefs = Object.values(result.metafieldDefinitions).reduce((sum, defs) => sum + defs.length, 0);
|
|
104
|
+
summary.push(`${totalDefs} metafield definitions across ${Object.keys(result.metafieldDefinitions).length} owner types`);
|
|
105
|
+
}
|
|
106
|
+
if (result.metaobjectDefinitions) {
|
|
107
|
+
summary.push(`${result.metaobjectDefinitions.length} metaobject types`);
|
|
108
|
+
}
|
|
109
|
+
await logOperation({
|
|
110
|
+
storeDomain,
|
|
111
|
+
toolName: "schema_discover",
|
|
112
|
+
query: "schema_discover",
|
|
113
|
+
variables: { includeMetafields, includeMetaobjects, metafieldOwnerTypes },
|
|
114
|
+
response: { summary },
|
|
115
|
+
success: true,
|
|
116
|
+
durationMs: Date.now() - startTime,
|
|
117
|
+
});
|
|
118
|
+
return formatSuccessResponse({
|
|
119
|
+
success: true,
|
|
120
|
+
summary: summary.join(", "),
|
|
121
|
+
...result,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
await logOperation({
|
|
126
|
+
storeDomain,
|
|
127
|
+
toolName: "schema_discover",
|
|
128
|
+
query: "schema_discover",
|
|
129
|
+
variables: { includeMetafields, includeMetaobjects, metafieldOwnerTypes },
|
|
130
|
+
success: false,
|
|
131
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
132
|
+
durationMs: Date.now() - startTime,
|
|
133
|
+
});
|
|
134
|
+
return formatErrorResponse(error);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=smart-schema.js.map
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface ToolResponse {
|
|
2
|
+
content: Array<{
|
|
3
|
+
type: "text";
|
|
4
|
+
text: string;
|
|
5
|
+
}>;
|
|
6
|
+
isError?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface PageInfo {
|
|
9
|
+
hasNextPage: boolean;
|
|
10
|
+
endCursor: string | null;
|
|
11
|
+
}
|
|
12
|
+
export interface MoneyV2 {
|
|
13
|
+
amount: string;
|
|
14
|
+
currencyCode: string;
|
|
15
|
+
}
|
|
16
|
+
export interface UserError {
|
|
17
|
+
field: string[];
|
|
18
|
+
message: string;
|
|
19
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polling utility for async Shopify operations
|
|
3
|
+
* Used by smart tools to wait for bulk operations, file uploads, etc.
|
|
4
|
+
*/
|
|
5
|
+
export interface PollResult<T> {
|
|
6
|
+
done: boolean;
|
|
7
|
+
result?: T;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface PollOptions {
|
|
11
|
+
/** Time between checks in milliseconds */
|
|
12
|
+
intervalMs: number;
|
|
13
|
+
/** Maximum time to wait in milliseconds */
|
|
14
|
+
timeoutMs: number;
|
|
15
|
+
/** Optional callback on each poll iteration */
|
|
16
|
+
onPoll?: (elapsed: number) => void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Poll until a condition is met or timeout
|
|
20
|
+
*
|
|
21
|
+
* @param checkFn Function that checks the condition and returns result
|
|
22
|
+
* @param options Polling configuration
|
|
23
|
+
* @returns The result when done
|
|
24
|
+
* @throws Error if timeout or error returned from checkFn
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const result = await pollUntil(
|
|
28
|
+
* async () => {
|
|
29
|
+
* const file = await getFile(id);
|
|
30
|
+
* if (file.status === 'READY') return { done: true, result: file };
|
|
31
|
+
* if (file.status === 'FAILED') return { done: true, error: 'Upload failed' };
|
|
32
|
+
* return { done: false };
|
|
33
|
+
* },
|
|
34
|
+
* { intervalMs: 2000, timeoutMs: 30000 }
|
|
35
|
+
* );
|
|
36
|
+
*/
|
|
37
|
+
export declare function pollUntil<T>(checkFn: () => Promise<PollResult<T>>, options: PollOptions): Promise<T>;
|
|
38
|
+
/**
|
|
39
|
+
* Sleep for a specified duration
|
|
40
|
+
*/
|
|
41
|
+
export declare function sleep(ms: number): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Retry a function with exponential backoff
|
|
44
|
+
*
|
|
45
|
+
* @param fn Function to retry
|
|
46
|
+
* @param options Retry configuration
|
|
47
|
+
*/
|
|
48
|
+
export declare function retryWithBackoff<T>(fn: () => Promise<T>, options: {
|
|
49
|
+
maxRetries: number;
|
|
50
|
+
initialDelayMs: number;
|
|
51
|
+
maxDelayMs: number;
|
|
52
|
+
shouldRetry?: (error: unknown) => boolean;
|
|
53
|
+
}): Promise<T>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polling utility for async Shopify operations
|
|
3
|
+
* Used by smart tools to wait for bulk operations, file uploads, etc.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Poll until a condition is met or timeout
|
|
7
|
+
*
|
|
8
|
+
* @param checkFn Function that checks the condition and returns result
|
|
9
|
+
* @param options Polling configuration
|
|
10
|
+
* @returns The result when done
|
|
11
|
+
* @throws Error if timeout or error returned from checkFn
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const result = await pollUntil(
|
|
15
|
+
* async () => {
|
|
16
|
+
* const file = await getFile(id);
|
|
17
|
+
* if (file.status === 'READY') return { done: true, result: file };
|
|
18
|
+
* if (file.status === 'FAILED') return { done: true, error: 'Upload failed' };
|
|
19
|
+
* return { done: false };
|
|
20
|
+
* },
|
|
21
|
+
* { intervalMs: 2000, timeoutMs: 30000 }
|
|
22
|
+
* );
|
|
23
|
+
*/
|
|
24
|
+
export async function pollUntil(checkFn, options) {
|
|
25
|
+
const start = Date.now();
|
|
26
|
+
while (Date.now() - start < options.timeoutMs) {
|
|
27
|
+
const { done, result, error } = await checkFn();
|
|
28
|
+
if (error) {
|
|
29
|
+
throw new Error(error);
|
|
30
|
+
}
|
|
31
|
+
if (done) {
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
// Call optional progress callback
|
|
35
|
+
if (options.onPoll) {
|
|
36
|
+
options.onPoll(Date.now() - start);
|
|
37
|
+
}
|
|
38
|
+
// Wait before next check
|
|
39
|
+
await sleep(options.intervalMs);
|
|
40
|
+
}
|
|
41
|
+
throw new Error(`Polling timeout after ${options.timeoutMs}ms`);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Sleep for a specified duration
|
|
45
|
+
*/
|
|
46
|
+
export function sleep(ms) {
|
|
47
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Retry a function with exponential backoff
|
|
51
|
+
*
|
|
52
|
+
* @param fn Function to retry
|
|
53
|
+
* @param options Retry configuration
|
|
54
|
+
*/
|
|
55
|
+
export async function retryWithBackoff(fn, options) {
|
|
56
|
+
let lastError;
|
|
57
|
+
let delay = options.initialDelayMs;
|
|
58
|
+
for (let attempt = 0; attempt <= options.maxRetries; attempt++) {
|
|
59
|
+
try {
|
|
60
|
+
return await fn();
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
lastError = error;
|
|
64
|
+
// Check if we should retry this error
|
|
65
|
+
if (options.shouldRetry && !options.shouldRetry(error)) {
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
// Don't sleep after the last attempt
|
|
69
|
+
if (attempt < options.maxRetries) {
|
|
70
|
+
await sleep(delay);
|
|
71
|
+
delay = Math.min(delay * 2, options.maxDelayMs);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
throw lastError;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=polling.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "shopify-store-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Shopify store operations via Admin and Storefront APIs",
|
|
5
|
+
"author": "Brahim Benzarti",
|
|
6
|
+
"license": "ISC",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"bin": {
|
|
10
|
+
"shopify-store-mcp": "dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/**/*.js",
|
|
14
|
+
"dist/**/*.d.ts",
|
|
15
|
+
"prisma/schema.prisma",
|
|
16
|
+
"LICENSE",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"mcp",
|
|
21
|
+
"modelcontextprotocol",
|
|
22
|
+
"shopify",
|
|
23
|
+
"admin-api",
|
|
24
|
+
"graphql",
|
|
25
|
+
"ai",
|
|
26
|
+
"claude",
|
|
27
|
+
"cursor"
|
|
28
|
+
],
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/ibrahimbenzarti/shopify-store-mcp.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/ibrahimbenzarti/shopify-store-mcp/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/ibrahimbenzarti/shopify-store-mcp#readme",
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "prisma generate && tsc && node -e \"require('fs').chmodSync('dist/index.js', '755')\"",
|
|
42
|
+
"dev": "tsc --watch",
|
|
43
|
+
"start": "node dist/index.js",
|
|
44
|
+
"inspect": "npm run build && npx @modelcontextprotocol/inspector dist/index.js",
|
|
45
|
+
"typecheck": "tsc --noEmit",
|
|
46
|
+
"graphql-codegen": "graphql-codegen -p=adminApi && graphql-codegen -p=storefrontApi",
|
|
47
|
+
"graphql-codegen:admin": "graphql-codegen -p=adminApi",
|
|
48
|
+
"graphql-codegen:storefront": "graphql-codegen -p=storefrontApi",
|
|
49
|
+
"graphql-codegen:watch": "graphql-codegen --watch",
|
|
50
|
+
"db:generate": "prisma generate",
|
|
51
|
+
"db:push": "prisma db push",
|
|
52
|
+
"db:studio": "prisma studio",
|
|
53
|
+
"prepublishOnly": "npm run build"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@modelcontextprotocol/sdk": "^1.11.0",
|
|
57
|
+
"@prisma/client": "^6.3.0",
|
|
58
|
+
"@shopify/admin-api-client": "^1.1.0",
|
|
59
|
+
"@shopify/storefront-api-client": "^1.0.0",
|
|
60
|
+
"dotenv": "^16.4.5",
|
|
61
|
+
"p-queue": "^8.0.1",
|
|
62
|
+
"zod": "^3.23.0"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@graphql-codegen/cli": "^5.0.0",
|
|
66
|
+
"@shopify/api-codegen-preset": "^1.1.1",
|
|
67
|
+
"@types/node": "^22.0.0",
|
|
68
|
+
"graphql": "^16.9.0",
|
|
69
|
+
"graphql-config": "^5.1.0",
|
|
70
|
+
"prisma": "^6.3.0",
|
|
71
|
+
"typescript": "^5.6.0"
|
|
72
|
+
},
|
|
73
|
+
"resolutions": {
|
|
74
|
+
"@graphql-tools/url-loader": "8.0.16",
|
|
75
|
+
"@graphql-codegen/client-preset": "4.7.0",
|
|
76
|
+
"@graphql-codegen/typescript-operations": "4.5.0"
|
|
77
|
+
},
|
|
78
|
+
"overrides": {
|
|
79
|
+
"@graphql-tools/url-loader": "8.0.16",
|
|
80
|
+
"@graphql-codegen/client-preset": "4.7.0",
|
|
81
|
+
"@graphql-codegen/typescript-operations": "4.5.0"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Shopify Store MCP - Prisma Schema
|
|
2
|
+
// SQLite database for operation logging, configuration, and background jobs
|
|
3
|
+
|
|
4
|
+
generator client {
|
|
5
|
+
provider = "prisma-client-js"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
datasource db {
|
|
9
|
+
provider = "sqlite"
|
|
10
|
+
url = env("DATABASE_URL")
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// STORE CONFIGURATION
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
// Store configuration per shop (rate limit tier, settings)
|
|
18
|
+
model StoreConfig {
|
|
19
|
+
id String @id @default(uuid())
|
|
20
|
+
storeDomain String @unique
|
|
21
|
+
tier String @default("STANDARD") // STANDARD, ADVANCED, PLUS, ENTERPRISE
|
|
22
|
+
autoDetected Boolean @default(false)
|
|
23
|
+
shopName String?
|
|
24
|
+
shopPlan String?
|
|
25
|
+
createdAt DateTime @default(now())
|
|
26
|
+
updatedAt DateTime @updatedAt
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// OPERATION LOGGING
|
|
31
|
+
// ============================================================================
|
|
32
|
+
|
|
33
|
+
// Logs every GraphQL operation for debugging and history
|
|
34
|
+
model OperationLog {
|
|
35
|
+
id String @id @default(uuid())
|
|
36
|
+
storeDomain String
|
|
37
|
+
sessionId String // MCP session identifier
|
|
38
|
+
toolName String // Tool that was called
|
|
39
|
+
operationType String // "query" | "mutation"
|
|
40
|
+
query String // GraphQL query (may be truncated)
|
|
41
|
+
variables String? // JSON string of variables
|
|
42
|
+
response String? // JSON string of response (may be truncated)
|
|
43
|
+
success Boolean
|
|
44
|
+
errorMessage String?
|
|
45
|
+
durationMs Int
|
|
46
|
+
createdAt DateTime @default(now())
|
|
47
|
+
|
|
48
|
+
@@index([storeDomain])
|
|
49
|
+
@@index([storeDomain, createdAt])
|
|
50
|
+
@@index([toolName])
|
|
51
|
+
@@index([sessionId])
|
|
52
|
+
@@index([createdAt])
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// BACKGROUND JOBS
|
|
57
|
+
// ============================================================================
|
|
58
|
+
|
|
59
|
+
// Tracks long-running async operations (bulk export/import, file uploads)
|
|
60
|
+
model BackgroundJob {
|
|
61
|
+
id String @id @default(uuid())
|
|
62
|
+
storeDomain String
|
|
63
|
+
sessionId String // MCP session that started the job
|
|
64
|
+
bulkOperationId String? // Shopify bulk operation GID if applicable
|
|
65
|
+
type String // BULK_EXPORT, BULK_IMPORT, FILE_UPLOAD
|
|
66
|
+
name String // Human-readable job name
|
|
67
|
+
status String @default("PENDING") // PENDING, RUNNING, COMPLETED, FAILED, CANCELLED
|
|
68
|
+
progress Float @default(0) // Progress percentage (0-100)
|
|
69
|
+
total Int @default(0) // Total items to process
|
|
70
|
+
processed Int @default(0) // Items processed so far
|
|
71
|
+
data String? // JSON - additional job metadata
|
|
72
|
+
result String? // JSON - job results
|
|
73
|
+
error String? // Error message if failed
|
|
74
|
+
createdAt DateTime @default(now())
|
|
75
|
+
updatedAt DateTime @updatedAt
|
|
76
|
+
|
|
77
|
+
@@index([storeDomain])
|
|
78
|
+
@@index([storeDomain, status])
|
|
79
|
+
@@index([bulkOperationId])
|
|
80
|
+
@@index([status])
|
|
81
|
+
@@index([sessionId])
|
|
82
|
+
}
|