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.
Files changed (88) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +172 -0
  3. package/dist/config.d.ts +10 -0
  4. package/dist/config.js +65 -0
  5. package/dist/db.d.ts +19 -0
  6. package/dist/db.js +161 -0
  7. package/dist/errors.d.ts +36 -0
  8. package/dist/errors.js +93 -0
  9. package/dist/graphql/admin/common/collections.d.ts +8 -0
  10. package/dist/graphql/admin/common/collections.js +44 -0
  11. package/dist/graphql/admin/common/customers.d.ts +13 -0
  12. package/dist/graphql/admin/common/customers.js +112 -0
  13. package/dist/graphql/admin/common/orders.d.ts +13 -0
  14. package/dist/graphql/admin/common/orders.js +142 -0
  15. package/dist/graphql/admin/common/products.d.ts +23 -0
  16. package/dist/graphql/admin/common/products.js +159 -0
  17. package/dist/graphql/admin/common/shop.d.ts +7 -0
  18. package/dist/graphql/admin/common/shop.js +38 -0
  19. package/dist/graphql/admin/index.d.ts +15 -0
  20. package/dist/graphql/admin/index.js +18 -0
  21. package/dist/graphql/admin/specialized/bulk.d.ts +33 -0
  22. package/dist/graphql/admin/specialized/bulk.js +132 -0
  23. package/dist/graphql/admin/specialized/files.d.ts +22 -0
  24. package/dist/graphql/admin/specialized/files.js +170 -0
  25. package/dist/graphql/admin/specialized/inventory.d.ts +13 -0
  26. package/dist/graphql/admin/specialized/inventory.js +78 -0
  27. package/dist/graphql/admin/specialized/metafields.d.ts +22 -0
  28. package/dist/graphql/admin/specialized/metafields.js +100 -0
  29. package/dist/graphql/admin/specialized/metaobjects.d.ts +36 -0
  30. package/dist/graphql/admin/specialized/metaobjects.js +239 -0
  31. package/dist/graphql/admin/specialized/search.d.ts +21 -0
  32. package/dist/graphql/admin/specialized/search.js +100 -0
  33. package/dist/graphql/collections.d.ts +1 -0
  34. package/dist/graphql/collections.js +37 -0
  35. package/dist/graphql/customers.d.ts +2 -0
  36. package/dist/graphql/customers.js +98 -0
  37. package/dist/graphql/inventory.d.ts +2 -0
  38. package/dist/graphql/inventory.js +67 -0
  39. package/dist/graphql/metafields.d.ts +2 -0
  40. package/dist/graphql/metafields.js +43 -0
  41. package/dist/graphql/orders.d.ts +2 -0
  42. package/dist/graphql/orders.js +116 -0
  43. package/dist/graphql/products.d.ts +4 -0
  44. package/dist/graphql/products.js +140 -0
  45. package/dist/graphql/shop.d.ts +1 -0
  46. package/dist/graphql/shop.js +32 -0
  47. package/dist/graphql/storefront/common/cart.d.ts +23 -0
  48. package/dist/graphql/storefront/common/cart.js +210 -0
  49. package/dist/graphql/storefront/common/collections.d.ts +11 -0
  50. package/dist/graphql/storefront/common/collections.js +114 -0
  51. package/dist/graphql/storefront/common/products.d.ts +14 -0
  52. package/dist/graphql/storefront/common/products.js +155 -0
  53. package/dist/graphql/storefront/index.d.ts +7 -0
  54. package/dist/graphql/storefront/index.js +8 -0
  55. package/dist/index.d.ts +2 -0
  56. package/dist/index.js +97 -0
  57. package/dist/logger.d.ts +58 -0
  58. package/dist/logger.js +165 -0
  59. package/dist/prompts/index.d.ts +2 -0
  60. package/dist/prompts/index.js +169 -0
  61. package/dist/queue.d.ts +73 -0
  62. package/dist/queue.js +120 -0
  63. package/dist/resources/index.d.ts +3 -0
  64. package/dist/resources/index.js +180 -0
  65. package/dist/shopify-client.d.ts +16 -0
  66. package/dist/shopify-client.js +39 -0
  67. package/dist/tools/graphql.d.ts +3 -0
  68. package/dist/tools/graphql.js +41 -0
  69. package/dist/tools/index.d.ts +3 -0
  70. package/dist/tools/index.js +23 -0
  71. package/dist/tools/infrastructure.d.ts +6 -0
  72. package/dist/tools/infrastructure.js +215 -0
  73. package/dist/tools/shop.d.ts +3 -0
  74. package/dist/tools/shop.js +28 -0
  75. package/dist/tools/smart-bulk.d.ts +7 -0
  76. package/dist/tools/smart-bulk.js +286 -0
  77. package/dist/tools/smart-files.d.ts +7 -0
  78. package/dist/tools/smart-files.js +169 -0
  79. package/dist/tools/smart-metaobjects.d.ts +7 -0
  80. package/dist/tools/smart-metaobjects.js +186 -0
  81. package/dist/tools/smart-schema.d.ts +7 -0
  82. package/dist/tools/smart-schema.js +138 -0
  83. package/dist/types.d.ts +19 -0
  84. package/dist/types.js +2 -0
  85. package/dist/utils/polling.d.ts +53 -0
  86. package/dist/utils/polling.js +77 -0
  87. package/package.json +83 -0
  88. package/prisma/schema.prisma +82 -0
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ ISC License
2
+
3
+ Copyright 2025-present, Brahim Benzarti
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,172 @@
1
+ # Shopify Store MCP Server
2
+
3
+ A Model Context Protocol (MCP) server that connects to **live Shopify stores** via the Admin and Storefront APIs. Unlike documentation-only MCPs, this server enables AI agents to perform real operations on your store.
4
+
5
+ ## Features
6
+
7
+ - **Universal GraphQL Access** - Execute any Admin API query or mutation via `run_graphql_query`
8
+ - **Smart Multi-Step Tools** - File uploads, bulk operations, metaobject upserts with automatic polling
9
+ - **Rate Limiting** - Respects Shopify's plan-based rate limits (Standard/Advanced/Plus/Enterprise)
10
+ - **Operation Logging** - SQLite database tracks all operations for debugging and history
11
+ - **Schema Discovery** - Explore your store's metafield definitions and metaobject types
12
+
13
+ ## Setup
14
+
15
+ ### Prerequisites
16
+
17
+ 1. A Shopify store with a [custom app](https://help.shopify.com/en/manual/apps/app-types/custom-apps)
18
+ 2. Admin API access token with required scopes
19
+ 3. Node.js 18+
20
+
21
+ ### Installation
22
+
23
+ ```bash
24
+ npm install -g shopify-store-mcp
25
+ ```
26
+
27
+ Or run directly with npx:
28
+
29
+ ```bash
30
+ npx shopify-store-mcp
31
+ ```
32
+
33
+ ## Usage with Claude Desktop or Cursor
34
+
35
+ Add the following to your MCP configuration:
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "shopify-store": {
41
+ "command": "npx",
42
+ "args": ["-y", "shopify-store-mcp"],
43
+ "env": {
44
+ "SHOPIFY_STORE_URL": "your-store.myshopify.com",
45
+ "SHOPIFY_ACCESS_TOKEN": "shpat_xxxxxxxxxxxxxxxxxxxxx"
46
+ }
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ On Windows, use this configuration:
53
+
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "shopify-store": {
58
+ "command": "cmd",
59
+ "args": ["/k", "npx", "-y", "shopify-store-mcp"],
60
+ "env": {
61
+ "SHOPIFY_STORE_URL": "your-store.myshopify.com",
62
+ "SHOPIFY_ACCESS_TOKEN": "shpat_xxxxxxxxxxxxxxxxxxxxx"
63
+ }
64
+ }
65
+ }
66
+ }
67
+ ```
68
+
69
+ ## Environment Variables
70
+
71
+ | Variable | Required | Default | Description |
72
+ |----------|----------|---------|-------------|
73
+ | `SHOPIFY_STORE_URL` | Yes | - | Store's myshopify.com domain |
74
+ | `SHOPIFY_ACCESS_TOKEN` | Yes | - | Admin API access token |
75
+ | `SHOPIFY_STOREFRONT_ACCESS_TOKEN` | No | - | Storefront API token |
76
+ | `SHOPIFY_API_VERSION` | No | `2025-01` | API version |
77
+ | `SHOPIFY_TIER` | No | `STANDARD` | Rate limit tier |
78
+
79
+ ## Available Tools
80
+
81
+ ### Core Tools
82
+
83
+ | Tool | Description |
84
+ |------|-------------|
85
+ | `get_shop_info` | Retrieve store configuration and settings |
86
+ | `run_graphql_query` | Execute any GraphQL query or mutation |
87
+
88
+ ### Smart Tools
89
+
90
+ | Tool | Description |
91
+ |------|-------------|
92
+ | `upload_file` | Upload file from URL, poll until ready, return CDN URL |
93
+ | `bulk_export` | Start bulk query, poll completion, return JSONL download URL |
94
+ | `bulk_import` | Staged upload + bulk mutation with automatic polling |
95
+ | `upsert_metaobject` | Create or update metaobject by handle (idempotent) |
96
+ | `schema_discover` | Discover metafield definitions and metaobject types |
97
+
98
+ ### Infrastructure Tools
99
+
100
+ | Tool | Description |
101
+ |------|-------------|
102
+ | `configure` | Set rate limit tier (manual or auto-detect from shop plan) |
103
+ | `get_history` | Query past operations for debugging |
104
+ | `get_stats` | Aggregated usage statistics |
105
+
106
+ ## Rate Limiting
107
+
108
+ The server respects Shopify's rate limits based on your shop plan:
109
+
110
+ | Tier | Requests/sec | Plans |
111
+ |------|--------------|-------|
112
+ | STANDARD | 1 | Basic, Development, Lite |
113
+ | ADVANCED | 2 | Advanced |
114
+ | PLUS | 5 | Shopify Plus |
115
+ | ENTERPRISE | 10 | Commerce Components |
116
+
117
+ Use the `configure` tool to set your tier manually or auto-detect from your shop plan.
118
+
119
+ ## Available Prompts
120
+
121
+ | Prompt | Description |
122
+ |--------|-------------|
123
+ | `analyze-product` | Product analysis template |
124
+ | `summarize-orders` | Order summary by timeframe |
125
+ | `inventory-health` | Inventory health check |
126
+ | `customer-insights` | Customer segment analysis |
127
+ | `custom-query` | Help building custom GraphQL queries |
128
+
129
+ ## Available Resources
130
+
131
+ | Resource | Description |
132
+ |----------|-------------|
133
+ | `shopify://config` | Current store connection info |
134
+ | `shopify://docs/query-syntax` | Query syntax reference |
135
+ | `shopify://docs/gid-format` | GID format reference |
136
+ | `shopify://docs/scopes` | API scopes reference |
137
+
138
+ ## Development
139
+
140
+ ```bash
141
+ # Install dependencies
142
+ npm install
143
+
144
+ # Build
145
+ npm run build
146
+
147
+ # Run with inspector
148
+ npm run inspect
149
+
150
+ # Watch mode
151
+ npm run dev
152
+ ```
153
+
154
+ ### Database
155
+
156
+ The server uses SQLite for operation logging and configuration. The database is automatically created at `~/.shopify-mcp/mcp.db`.
157
+
158
+ ```bash
159
+ # View database
160
+ npm run db:studio
161
+ ```
162
+
163
+ ## Security
164
+
165
+ - Never commit your `.env` file or access tokens
166
+ - Use environment variables or MCP config for credentials
167
+ - Access tokens should have minimal required scopes
168
+ - The server logs operations locally for debugging (disable with `MCP_LOG_OPERATIONS=false`)
169
+
170
+ ## License
171
+
172
+ ISC
@@ -0,0 +1,10 @@
1
+ export interface ShopifyConfig {
2
+ storeDomain: string;
3
+ adminAccessToken: string;
4
+ storefrontAccessToken?: string;
5
+ customerAccessToken?: string;
6
+ apiVersion: string;
7
+ bugReportEnabled: boolean;
8
+ bugReportDir?: string;
9
+ }
10
+ export declare function loadConfig(): ShopifyConfig;
package/dist/config.js ADDED
@@ -0,0 +1,65 @@
1
+ import { config as loadDotenv } from "dotenv";
2
+ import { mkdirSync, existsSync } from "fs";
3
+ import { join, dirname } from "path";
4
+ import { createHash } from "crypto";
5
+ import { fileURLToPath } from "url";
6
+ // Get the directory of this file for relative .env loading
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+ export function loadConfig() {
10
+ // Try to load .env files from the package root (one level up from dist/)
11
+ // Priority: .env.local > .env (local overrides shared)
12
+ const packageRoot = join(__dirname, "..");
13
+ loadDotenv({ path: join(packageRoot, ".env.local") });
14
+ loadDotenv({ path: join(packageRoot, ".env") });
15
+ const storeDomain = process.env.SHOPIFY_STORE_URL;
16
+ const adminAccessToken = process.env.SHOPIFY_ACCESS_TOKEN;
17
+ const storefrontAccessToken = process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN;
18
+ const customerAccessToken = process.env.SHOPIFY_CUSTOMER_ACCESS_TOKEN;
19
+ const apiVersion = process.env.SHOPIFY_API_VERSION || "2025-01";
20
+ const bugReportEnabled = process.env.SHOPIFY_MCP_BUG_REPORTS === "true";
21
+ const bugReportBaseDir = process.env.SHOPIFY_MCP_BUG_REPORT_DIR;
22
+ if (!storeDomain) {
23
+ throw new Error("SHOPIFY_STORE_URL is required. Set it to your store's myshopify.com domain (e.g. my-store.myshopify.com).");
24
+ }
25
+ if (!adminAccessToken) {
26
+ throw new Error("SHOPIFY_ACCESS_TOKEN is required. Create a custom app in Shopify Admin > Settings > Apps and development channels to get one.");
27
+ }
28
+ // Normalize: strip protocol and trailing slash
29
+ const normalized = storeDomain
30
+ .replace(/^https?:\/\//, "")
31
+ .replace(/\/$/, "");
32
+ // Create unique bug report directory per store
33
+ let bugReportDir;
34
+ if (bugReportEnabled) {
35
+ // Create a unique identifier from store domain + session timestamp
36
+ const sessionId = Date.now().toString(36);
37
+ const storeHash = createHash("md5")
38
+ .update(normalized)
39
+ .digest("hex")
40
+ .slice(0, 8);
41
+ const dirName = `${normalized.replace(/\./g, "-")}_${storeHash}_${sessionId}`;
42
+ bugReportDir = bugReportBaseDir
43
+ ? join(bugReportBaseDir, dirName)
44
+ : join(process.cwd(), ".bug-reports", dirName);
45
+ // Create the directory if it doesn't exist
46
+ if (!existsSync(bugReportDir)) {
47
+ mkdirSync(bugReportDir, { recursive: true });
48
+ console.error(`[shopify-store-mcp] Bug reports enabled: ${bugReportDir}`);
49
+ }
50
+ }
51
+ // Debug logging for troubleshooting
52
+ console.error(`[shopify-store-mcp] Store domain: ${normalized}`);
53
+ console.error(`[shopify-store-mcp] Token prefix: ${adminAccessToken.substring(0, 8)}...`);
54
+ console.error(`[shopify-store-mcp] Token length: ${adminAccessToken.length} chars`);
55
+ return {
56
+ storeDomain: normalized,
57
+ adminAccessToken,
58
+ storefrontAccessToken,
59
+ customerAccessToken,
60
+ apiVersion,
61
+ bugReportEnabled,
62
+ bugReportDir,
63
+ };
64
+ }
65
+ //# sourceMappingURL=config.js.map
package/dist/db.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Prisma client singleton for SQLite database
3
+ * Handles operation logging, store configuration, and background jobs
4
+ */
5
+ import { PrismaClient } from "@prisma/client";
6
+ declare global {
7
+ var prismaGlobal: PrismaClient | undefined;
8
+ }
9
+ declare const prisma: PrismaClient<import("@prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
10
+ export default prisma;
11
+ /**
12
+ * Initialize the database (create tables if they don't exist)
13
+ * This is called on MCP server startup
14
+ */
15
+ export declare function initializeDatabase(): Promise<void>;
16
+ /**
17
+ * Clean up old operation logs based on retention period
18
+ */
19
+ export declare function cleanupOldLogs(retentionDays: number): Promise<number>;
package/dist/db.js ADDED
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Prisma client singleton for SQLite database
3
+ * Handles operation logging, store configuration, and background jobs
4
+ */
5
+ import { PrismaClient } from "@prisma/client";
6
+ // Create Prisma client with optimized settings
7
+ const createPrismaClient = () => new PrismaClient({
8
+ log: process.env.NODE_ENV === "development"
9
+ ? ["warn", "error"]
10
+ : ["error"],
11
+ });
12
+ // Use global singleton in development to prevent multiple instances during hot reload
13
+ if (process.env.NODE_ENV !== "production") {
14
+ if (!global.prismaGlobal) {
15
+ global.prismaGlobal = createPrismaClient();
16
+ }
17
+ }
18
+ const prisma = global.prismaGlobal ?? createPrismaClient();
19
+ // Graceful shutdown handling
20
+ const gracefulShutdown = () => {
21
+ prisma
22
+ .$disconnect()
23
+ .then(() => {
24
+ process.exit(0);
25
+ })
26
+ .catch(() => {
27
+ process.exit(1);
28
+ });
29
+ };
30
+ process.on("SIGINT", gracefulShutdown);
31
+ process.on("SIGTERM", gracefulShutdown);
32
+ export default prisma;
33
+ /**
34
+ * Initialize the database (create tables if they don't exist)
35
+ * This is called on MCP server startup
36
+ */
37
+ export async function initializeDatabase() {
38
+ try {
39
+ await prisma.$connect();
40
+ // Test if tables exist by trying a simple query
41
+ // If tables don't exist, this will fail and we'll create them
42
+ try {
43
+ await prisma.storeConfig.count();
44
+ console.error("[shopify-store-mcp] Database connected");
45
+ }
46
+ catch (tableError) {
47
+ // Tables don't exist - this is expected on first run
48
+ // Prisma will create them on first write
49
+ console.error("[shopify-store-mcp] Database initializing (first run)...");
50
+ // Create a dummy record to trigger table creation, then delete it
51
+ // This is a workaround since Prisma SQLite doesn't auto-create tables
52
+ try {
53
+ await prisma.$executeRaw `
54
+ CREATE TABLE IF NOT EXISTS "StoreConfig" (
55
+ "id" TEXT NOT NULL PRIMARY KEY,
56
+ "storeDomain" TEXT NOT NULL,
57
+ "tier" TEXT NOT NULL DEFAULT 'STANDARD',
58
+ "autoDetected" BOOLEAN NOT NULL DEFAULT false,
59
+ "shopName" TEXT,
60
+ "shopPlan" TEXT,
61
+ "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
62
+ "updatedAt" DATETIME NOT NULL
63
+ )
64
+ `;
65
+ await prisma.$executeRaw `
66
+ CREATE UNIQUE INDEX IF NOT EXISTS "StoreConfig_storeDomain_key" ON "StoreConfig"("storeDomain")
67
+ `;
68
+ await prisma.$executeRaw `
69
+ CREATE TABLE IF NOT EXISTS "OperationLog" (
70
+ "id" TEXT NOT NULL PRIMARY KEY,
71
+ "storeDomain" TEXT NOT NULL,
72
+ "sessionId" TEXT NOT NULL,
73
+ "toolName" TEXT NOT NULL,
74
+ "operationType" TEXT NOT NULL,
75
+ "query" TEXT NOT NULL,
76
+ "variables" TEXT,
77
+ "response" TEXT,
78
+ "success" BOOLEAN NOT NULL,
79
+ "errorMessage" TEXT,
80
+ "durationMs" INTEGER NOT NULL,
81
+ "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
82
+ )
83
+ `;
84
+ await prisma.$executeRaw `
85
+ CREATE INDEX IF NOT EXISTS "OperationLog_storeDomain_idx" ON "OperationLog"("storeDomain")
86
+ `;
87
+ await prisma.$executeRaw `
88
+ CREATE INDEX IF NOT EXISTS "OperationLog_storeDomain_createdAt_idx" ON "OperationLog"("storeDomain", "createdAt")
89
+ `;
90
+ await prisma.$executeRaw `
91
+ CREATE INDEX IF NOT EXISTS "OperationLog_toolName_idx" ON "OperationLog"("toolName")
92
+ `;
93
+ await prisma.$executeRaw `
94
+ CREATE INDEX IF NOT EXISTS "OperationLog_sessionId_idx" ON "OperationLog"("sessionId")
95
+ `;
96
+ await prisma.$executeRaw `
97
+ CREATE INDEX IF NOT EXISTS "OperationLog_createdAt_idx" ON "OperationLog"("createdAt")
98
+ `;
99
+ await prisma.$executeRaw `
100
+ CREATE TABLE IF NOT EXISTS "BackgroundJob" (
101
+ "id" TEXT NOT NULL PRIMARY KEY,
102
+ "storeDomain" TEXT NOT NULL,
103
+ "sessionId" TEXT NOT NULL,
104
+ "bulkOperationId" TEXT,
105
+ "type" TEXT NOT NULL,
106
+ "name" TEXT NOT NULL,
107
+ "status" TEXT NOT NULL DEFAULT 'PENDING',
108
+ "progress" REAL NOT NULL DEFAULT 0,
109
+ "total" INTEGER NOT NULL DEFAULT 0,
110
+ "processed" INTEGER NOT NULL DEFAULT 0,
111
+ "data" TEXT,
112
+ "result" TEXT,
113
+ "error" TEXT,
114
+ "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
115
+ "updatedAt" DATETIME NOT NULL
116
+ )
117
+ `;
118
+ await prisma.$executeRaw `
119
+ CREATE INDEX IF NOT EXISTS "BackgroundJob_storeDomain_idx" ON "BackgroundJob"("storeDomain")
120
+ `;
121
+ await prisma.$executeRaw `
122
+ CREATE INDEX IF NOT EXISTS "BackgroundJob_storeDomain_status_idx" ON "BackgroundJob"("storeDomain", "status")
123
+ `;
124
+ await prisma.$executeRaw `
125
+ CREATE INDEX IF NOT EXISTS "BackgroundJob_bulkOperationId_idx" ON "BackgroundJob"("bulkOperationId")
126
+ `;
127
+ await prisma.$executeRaw `
128
+ CREATE INDEX IF NOT EXISTS "BackgroundJob_status_idx" ON "BackgroundJob"("status")
129
+ `;
130
+ await prisma.$executeRaw `
131
+ CREATE INDEX IF NOT EXISTS "BackgroundJob_sessionId_idx" ON "BackgroundJob"("sessionId")
132
+ `;
133
+ console.error("[shopify-store-mcp] Database tables created");
134
+ }
135
+ catch (createError) {
136
+ console.error("[shopify-store-mcp] Failed to create tables:", createError);
137
+ throw createError;
138
+ }
139
+ }
140
+ }
141
+ catch (error) {
142
+ console.error("[shopify-store-mcp] Database connection failed:", error);
143
+ throw error;
144
+ }
145
+ }
146
+ /**
147
+ * Clean up old operation logs based on retention period
148
+ */
149
+ export async function cleanupOldLogs(retentionDays) {
150
+ const cutoffDate = new Date();
151
+ cutoffDate.setDate(cutoffDate.getDate() - retentionDays);
152
+ const result = await prisma.operationLog.deleteMany({
153
+ where: {
154
+ createdAt: {
155
+ lt: cutoffDate,
156
+ },
157
+ },
158
+ });
159
+ return result.count;
160
+ }
161
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1,36 @@
1
+ import type { UserError } from "./types.js";
2
+ export declare function formatErrorResponse(error: unknown): {
3
+ content: Array<{
4
+ type: "text";
5
+ text: string;
6
+ }>;
7
+ isError: true;
8
+ };
9
+ export declare function formatGraphQLErrors(response: {
10
+ errors?: unknown;
11
+ extensions?: Record<string, unknown>;
12
+ }): {
13
+ content: Array<{
14
+ type: "text";
15
+ text: string;
16
+ }>;
17
+ isError: true;
18
+ };
19
+ export declare function formatUserErrors(userErrors: UserError[]): {
20
+ content: Array<{
21
+ type: "text";
22
+ text: string;
23
+ }>;
24
+ isError: true;
25
+ };
26
+ export declare function formatSuccessResponse(data: unknown): {
27
+ content: Array<{
28
+ type: "text";
29
+ text: string;
30
+ }>;
31
+ };
32
+ /**
33
+ * Normalize a Shopify ID — accepts either a numeric ID or a full GID.
34
+ * e.g. "123" becomes "gid://shopify/Product/123" when resourceType is "Product"
35
+ */
36
+ export declare function normalizeGid(id: string, resourceType: string): string;
package/dist/errors.js ADDED
@@ -0,0 +1,93 @@
1
+ export function formatErrorResponse(error) {
2
+ let message;
3
+ if (error instanceof Error) {
4
+ message = error.message;
5
+ }
6
+ else if (typeof error === "string") {
7
+ message = error;
8
+ }
9
+ else {
10
+ message = JSON.stringify(error);
11
+ }
12
+ return {
13
+ content: [{ type: "text", text: `Error: ${message}` }],
14
+ isError: true,
15
+ };
16
+ }
17
+ export function formatGraphQLErrors(response) {
18
+ const parts = [];
19
+ if (response.errors) {
20
+ if (Array.isArray(response.errors)) {
21
+ for (const err of response.errors) {
22
+ parts.push(err.message || JSON.stringify(err));
23
+ }
24
+ }
25
+ else if (typeof response.errors === "object") {
26
+ const errObj = response.errors;
27
+ if (errObj.networkStatusCode) {
28
+ const code = errObj.networkStatusCode;
29
+ parts.push(`HTTP ${code}`);
30
+ if (code === 401) {
31
+ parts.push("The access token is invalid or expired. Check SHOPIFY_ACCESS_TOKEN.");
32
+ }
33
+ else if (code === 403) {
34
+ parts.push("The access token lacks required scopes. Update your custom app's API access scopes in Shopify Admin.");
35
+ }
36
+ else if (code === 429) {
37
+ parts.push("Rate limited by Shopify. Wait a moment and retry.");
38
+ }
39
+ }
40
+ if (errObj.message) {
41
+ parts.push(String(errObj.message));
42
+ }
43
+ if (Array.isArray(errObj.graphQLErrors)) {
44
+ for (const gqlErr of errObj.graphQLErrors) {
45
+ parts.push(`GraphQL: ${gqlErr.message}`);
46
+ }
47
+ }
48
+ }
49
+ else {
50
+ parts.push(String(response.errors));
51
+ }
52
+ }
53
+ if (parts.length === 0) {
54
+ parts.push("Unknown GraphQL error");
55
+ }
56
+ return {
57
+ content: [{ type: "text", text: parts.join("\n") }],
58
+ isError: true,
59
+ };
60
+ }
61
+ export function formatUserErrors(userErrors) {
62
+ const lines = userErrors.map((e) => `- ${e.field.join(".")}: ${e.message}`);
63
+ return {
64
+ content: [
65
+ {
66
+ type: "text",
67
+ text: `Shopify validation errors:\n${lines.join("\n")}`,
68
+ },
69
+ ],
70
+ isError: true,
71
+ };
72
+ }
73
+ export function formatSuccessResponse(data) {
74
+ return {
75
+ content: [
76
+ {
77
+ type: "text",
78
+ text: JSON.stringify(data, null, 2),
79
+ },
80
+ ],
81
+ };
82
+ }
83
+ /**
84
+ * Normalize a Shopify ID — accepts either a numeric ID or a full GID.
85
+ * e.g. "123" becomes "gid://shopify/Product/123" when resourceType is "Product"
86
+ */
87
+ export function normalizeGid(id, resourceType) {
88
+ if (id.startsWith("gid://")) {
89
+ return id;
90
+ }
91
+ return `gid://shopify/${resourceType}/${id}`;
92
+ }
93
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * GraphQL queries for collection operations
3
+ */
4
+ /**
5
+ * Get a paginated list of collections
6
+ * Returns both smart and custom collections with rules and product counts
7
+ */
8
+ export declare const GET_COLLECTIONS = "#graphql\n query GetCollections($first: Int!, $after: String, $query: String) {\n collections(first: $first, after: $after, query: $query) {\n edges {\n node {\n id\n title\n handle\n descriptionHtml\n sortOrder\n productsCount {\n count\n }\n ruleSet {\n appliedDisjunctively\n rules {\n column\n relation\n condition\n }\n }\n image {\n url\n altText\n }\n updatedAt\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n";
@@ -0,0 +1,44 @@
1
+ /**
2
+ * GraphQL queries for collection operations
3
+ */
4
+ /**
5
+ * Get a paginated list of collections
6
+ * Returns both smart and custom collections with rules and product counts
7
+ */
8
+ export const GET_COLLECTIONS = `#graphql
9
+ query GetCollections($first: Int!, $after: String, $query: String) {
10
+ collections(first: $first, after: $after, query: $query) {
11
+ edges {
12
+ node {
13
+ id
14
+ title
15
+ handle
16
+ descriptionHtml
17
+ sortOrder
18
+ productsCount {
19
+ count
20
+ }
21
+ ruleSet {
22
+ appliedDisjunctively
23
+ rules {
24
+ column
25
+ relation
26
+ condition
27
+ }
28
+ }
29
+ image {
30
+ url
31
+ altText
32
+ }
33
+ updatedAt
34
+ }
35
+ cursor
36
+ }
37
+ pageInfo {
38
+ hasNextPage
39
+ endCursor
40
+ }
41
+ }
42
+ }
43
+ `;
44
+ //# sourceMappingURL=collections.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * GraphQL queries for customer operations
3
+ */
4
+ /**
5
+ * Get a paginated list of customers with basic info
6
+ * Supports search query filtering
7
+ */
8
+ export declare const GET_CUSTOMERS = "#graphql\n query GetCustomers($first: Int!, $after: String, $query: String) {\n customers(first: $first, after: $after, query: $query) {\n edges {\n node {\n id\n displayName\n email\n phone\n state\n numberOfOrders\n amountSpent {\n amount\n currencyCode\n }\n createdAt\n updatedAt\n tags\n defaultAddress {\n city\n province\n country\n }\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n";
9
+ /**
10
+ * Get a single customer by ID with full details
11
+ * Includes addresses, recent orders, and metafields
12
+ */
13
+ export declare const GET_CUSTOMER = "#graphql\n query GetCustomer($id: ID!) {\n customer(id: $id) {\n id\n displayName\n firstName\n lastName\n email\n phone\n state\n note\n tags\n numberOfOrders\n amountSpent {\n amount\n currencyCode\n }\n createdAt\n updatedAt\n defaultAddress {\n address1\n address2\n city\n province\n country\n zip\n phone\n }\n addresses {\n address1\n address2\n city\n province\n country\n zip\n }\n orders(first: 10) {\n edges {\n node {\n id\n name\n createdAt\n displayFinancialStatus\n displayFulfillmentStatus\n totalPriceSet {\n shopMoney {\n amount\n currencyCode\n }\n }\n }\n }\n }\n metafields(first: 10) {\n edges {\n node {\n id\n namespace\n key\n value\n type\n }\n }\n }\n }\n }\n";