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,180 @@
|
|
|
1
|
+
export function registerAllResources(server, config) {
|
|
2
|
+
// Store Configuration Resource
|
|
3
|
+
server.registerResource("store-config", "shopify://config", {
|
|
4
|
+
title: "Store Configuration",
|
|
5
|
+
description: "Current store connection configuration including domain and API version. " +
|
|
6
|
+
"Use this to verify which store you're connected to.",
|
|
7
|
+
mimeType: "application/json",
|
|
8
|
+
}, async () => ({
|
|
9
|
+
contents: [
|
|
10
|
+
{
|
|
11
|
+
uri: "shopify://config",
|
|
12
|
+
mimeType: "application/json",
|
|
13
|
+
text: JSON.stringify({
|
|
14
|
+
storeDomain: config.storeDomain,
|
|
15
|
+
apiVersion: config.apiVersion,
|
|
16
|
+
hasStorefrontAccess: !!config.storefrontAccessToken,
|
|
17
|
+
hasCustomerAccess: !!config.customerAccessToken,
|
|
18
|
+
}, null, 2),
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
}));
|
|
22
|
+
// Shopify Query Syntax Reference
|
|
23
|
+
server.registerResource("query-syntax", "shopify://docs/query-syntax", {
|
|
24
|
+
title: "Shopify Query Syntax Reference",
|
|
25
|
+
description: "Reference guide for Shopify's search query syntax used in filtering products, orders, customers, etc.",
|
|
26
|
+
mimeType: "text/markdown",
|
|
27
|
+
}, async () => ({
|
|
28
|
+
contents: [
|
|
29
|
+
{
|
|
30
|
+
uri: "shopify://docs/query-syntax",
|
|
31
|
+
mimeType: "text/markdown",
|
|
32
|
+
text: `# Shopify Query Syntax Reference
|
|
33
|
+
|
|
34
|
+
## Basic Syntax
|
|
35
|
+
|
|
36
|
+
Queries use field:value pairs separated by spaces (AND logic).
|
|
37
|
+
|
|
38
|
+
## Common Fields by Resource
|
|
39
|
+
|
|
40
|
+
### Products
|
|
41
|
+
- \`title:boots\` - Title contains "boots"
|
|
42
|
+
- \`status:active\` - Product status (active, draft, archived)
|
|
43
|
+
- \`vendor:Nike\` - Filter by vendor
|
|
44
|
+
- \`product_type:shoes\` - Filter by product type
|
|
45
|
+
- \`tag:sale\` - Has specific tag
|
|
46
|
+
- \`created_at:>2024-01-01\` - Created after date
|
|
47
|
+
- \`inventory_total:>0\` - Has inventory
|
|
48
|
+
|
|
49
|
+
### Orders
|
|
50
|
+
- \`name:#1001\` - Order number
|
|
51
|
+
- \`email:customer@example.com\` - Customer email
|
|
52
|
+
- \`financial_status:paid\` - Payment status (paid, pending, refunded, voided)
|
|
53
|
+
- \`fulfillment_status:unfulfilled\` - Fulfillment status (unfulfilled, partial, fulfilled)
|
|
54
|
+
- \`created_at:>=7_days_ago\` - Relative date
|
|
55
|
+
- \`tag:wholesale\` - Has specific tag
|
|
56
|
+
|
|
57
|
+
### Customers
|
|
58
|
+
- \`email:john@example.com\` - Email address
|
|
59
|
+
- \`country:US\` - Country code
|
|
60
|
+
- \`orders_count:>5\` - Number of orders
|
|
61
|
+
- \`total_spent:>100\` - Total amount spent
|
|
62
|
+
- \`tag:vip\` - Has specific tag
|
|
63
|
+
- \`state:enabled\` - Account state
|
|
64
|
+
|
|
65
|
+
### Collections
|
|
66
|
+
- \`title:Summer\` - Collection title
|
|
67
|
+
- \`collection_type:smart\` - Type (smart or custom)
|
|
68
|
+
|
|
69
|
+
## Operators
|
|
70
|
+
- \`:value\` - Equals or contains
|
|
71
|
+
- \`:>value\` - Greater than
|
|
72
|
+
- \`:>=value\` - Greater than or equal
|
|
73
|
+
- \`:<value\` - Less than
|
|
74
|
+
- \`:<=value\` - Less than or equal
|
|
75
|
+
|
|
76
|
+
## Date Shortcuts
|
|
77
|
+
- \`today\`
|
|
78
|
+
- \`yesterday\`
|
|
79
|
+
- \`7_days_ago\`
|
|
80
|
+
- \`30_days_ago\`
|
|
81
|
+
- \`90_days_ago\`
|
|
82
|
+
|
|
83
|
+
## Examples
|
|
84
|
+
\`\`\`
|
|
85
|
+
# Active products from Nike with low inventory
|
|
86
|
+
status:active vendor:Nike inventory_total:<10
|
|
87
|
+
|
|
88
|
+
# Paid but unfulfilled orders from this week
|
|
89
|
+
financial_status:paid fulfillment_status:unfulfilled created_at:>=7_days_ago
|
|
90
|
+
|
|
91
|
+
# VIP customers with multiple orders
|
|
92
|
+
tag:vip orders_count:>3
|
|
93
|
+
\`\`\`
|
|
94
|
+
`,
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
}));
|
|
98
|
+
// GID Format Reference
|
|
99
|
+
server.registerResource("gid-format", "shopify://docs/gid-format", {
|
|
100
|
+
title: "Shopify GID Format Reference",
|
|
101
|
+
description: "Reference for Shopify's Global ID (GID) format used to identify resources.",
|
|
102
|
+
mimeType: "text/markdown",
|
|
103
|
+
}, async () => ({
|
|
104
|
+
contents: [
|
|
105
|
+
{
|
|
106
|
+
uri: "shopify://docs/gid-format",
|
|
107
|
+
mimeType: "text/markdown",
|
|
108
|
+
text: `# Shopify GID (Global ID) Format
|
|
109
|
+
|
|
110
|
+
## Format
|
|
111
|
+
\`gid://shopify/{ResourceType}/{NumericID}\`
|
|
112
|
+
|
|
113
|
+
## Common Resource Types
|
|
114
|
+
| Type | GID Example |
|
|
115
|
+
|------|-------------|
|
|
116
|
+
| Product | \`gid://shopify/Product/123456789\` |
|
|
117
|
+
| ProductVariant | \`gid://shopify/ProductVariant/123456789\` |
|
|
118
|
+
| Order | \`gid://shopify/Order/123456789\` |
|
|
119
|
+
| Customer | \`gid://shopify/Customer/123456789\` |
|
|
120
|
+
| Collection | \`gid://shopify/Collection/123456789\` |
|
|
121
|
+
| InventoryItem | \`gid://shopify/InventoryItem/123456789\` |
|
|
122
|
+
| InventoryLevel | \`gid://shopify/InventoryLevel/123456789?inventory_item_id=X&location_id=Y\` |
|
|
123
|
+
| Location | \`gid://shopify/Location/123456789\` |
|
|
124
|
+
| Metafield | \`gid://shopify/Metafield/123456789\` |
|
|
125
|
+
| FulfillmentOrder | \`gid://shopify/FulfillmentOrder/123456789\` |
|
|
126
|
+
|
|
127
|
+
## Notes
|
|
128
|
+
- Most tools in this MCP accept both numeric IDs and full GIDs
|
|
129
|
+
- When in doubt, use the full GID format
|
|
130
|
+
- GIDs from query responses can be used directly in subsequent calls
|
|
131
|
+
`,
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
}));
|
|
135
|
+
// Available Scopes Reference
|
|
136
|
+
server.registerResource("api-scopes", "shopify://docs/scopes", {
|
|
137
|
+
title: "API Scopes Reference",
|
|
138
|
+
description: "Reference for Shopify Admin API access scopes required for different operations.",
|
|
139
|
+
mimeType: "text/markdown",
|
|
140
|
+
}, async () => ({
|
|
141
|
+
contents: [
|
|
142
|
+
{
|
|
143
|
+
uri: "shopify://docs/scopes",
|
|
144
|
+
mimeType: "text/markdown",
|
|
145
|
+
text: `# Shopify Admin API Scopes
|
|
146
|
+
|
|
147
|
+
## Required Scopes by Operation
|
|
148
|
+
|
|
149
|
+
### Products
|
|
150
|
+
- \`read_products\` - List and view products
|
|
151
|
+
- \`write_products\` - Create and update products
|
|
152
|
+
|
|
153
|
+
### Orders
|
|
154
|
+
- \`read_orders\` - List and view orders
|
|
155
|
+
- \`write_orders\` - Update orders, create fulfillments
|
|
156
|
+
|
|
157
|
+
### Customers
|
|
158
|
+
- \`read_customers\` - List and view customers
|
|
159
|
+
- \`write_customers\` - Create and update customers
|
|
160
|
+
|
|
161
|
+
### Inventory
|
|
162
|
+
- \`read_inventory\` - View inventory levels
|
|
163
|
+
- \`write_inventory\` - Adjust inventory quantities
|
|
164
|
+
|
|
165
|
+
### Other Common Scopes
|
|
166
|
+
- \`read_locations\` - View store locations
|
|
167
|
+
- \`read_shipping\` - View shipping settings
|
|
168
|
+
- \`read_fulfillments\` - View fulfillments
|
|
169
|
+
- \`write_fulfillments\` - Create fulfillments
|
|
170
|
+
|
|
171
|
+
## Checking Your Scopes
|
|
172
|
+
If a tool returns a 403 error, it likely means your custom app is missing the required scope. Update your app's API access scopes in:
|
|
173
|
+
|
|
174
|
+
**Shopify Admin → Settings → Apps and sales channels → Develop apps → [Your App] → Configuration**
|
|
175
|
+
`,
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type AdminApiClient } from "@shopify/admin-api-client";
|
|
2
|
+
import { type StorefrontApiClient } from "@shopify/storefront-api-client";
|
|
3
|
+
import type { ShopifyConfig } from "./config.js";
|
|
4
|
+
/**
|
|
5
|
+
* Get the Admin API client (singleton).
|
|
6
|
+
* Used for store management operations: products, orders, customers, inventory, etc.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getAdminClient(config: ShopifyConfig): AdminApiClient;
|
|
9
|
+
/**
|
|
10
|
+
* Get the Storefront API client (singleton).
|
|
11
|
+
* Used for customer-facing operations: product browsing, cart, checkout, etc.
|
|
12
|
+
* Requires SHOPIFY_STOREFRONT_ACCESS_TOKEN to be set.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getStorefrontClient(config: ShopifyConfig): StorefrontApiClient | null;
|
|
15
|
+
export declare const getShopifyClient: typeof getAdminClient;
|
|
16
|
+
export type { AdminApiClient, StorefrontApiClient };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createAdminApiClient } from "@shopify/admin-api-client";
|
|
2
|
+
import { createStorefrontApiClient } from "@shopify/storefront-api-client";
|
|
3
|
+
let adminClientInstance = null;
|
|
4
|
+
let storefrontClientInstance = null;
|
|
5
|
+
/**
|
|
6
|
+
* Get the Admin API client (singleton).
|
|
7
|
+
* Used for store management operations: products, orders, customers, inventory, etc.
|
|
8
|
+
*/
|
|
9
|
+
export function getAdminClient(config) {
|
|
10
|
+
if (!adminClientInstance) {
|
|
11
|
+
adminClientInstance = createAdminApiClient({
|
|
12
|
+
storeDomain: config.storeDomain,
|
|
13
|
+
apiVersion: config.apiVersion,
|
|
14
|
+
accessToken: config.adminAccessToken,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return adminClientInstance;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get the Storefront API client (singleton).
|
|
21
|
+
* Used for customer-facing operations: product browsing, cart, checkout, etc.
|
|
22
|
+
* Requires SHOPIFY_STOREFRONT_ACCESS_TOKEN to be set.
|
|
23
|
+
*/
|
|
24
|
+
export function getStorefrontClient(config) {
|
|
25
|
+
if (!config.storefrontAccessToken) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
if (!storefrontClientInstance) {
|
|
29
|
+
storefrontClientInstance = createStorefrontApiClient({
|
|
30
|
+
storeDomain: config.storeDomain,
|
|
31
|
+
apiVersion: config.apiVersion,
|
|
32
|
+
publicAccessToken: config.storefrontAccessToken,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return storefrontClientInstance;
|
|
36
|
+
}
|
|
37
|
+
// Legacy alias for backwards compatibility
|
|
38
|
+
export const getShopifyClient = getAdminClient;
|
|
39
|
+
//# sourceMappingURL=shopify-client.js.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { formatSuccessResponse, formatGraphQLErrors, formatErrorResponse, } from "../errors.js";
|
|
3
|
+
export function registerGraphQLTools(server, client) {
|
|
4
|
+
server.registerTool("run_graphql_query", {
|
|
5
|
+
title: "Run GraphQL Query",
|
|
6
|
+
description: "Execute any GraphQL query or mutation against the Shopify Admin API. " +
|
|
7
|
+
"This is the most powerful and flexible tool - use it when other specialized tools " +
|
|
8
|
+
"don't cover your use case, or when you need precise control over the query shape. " +
|
|
9
|
+
"Returns the raw API response including data and any errors. " +
|
|
10
|
+
"Refer to Shopify's Admin API GraphQL reference for available queries and mutations.",
|
|
11
|
+
inputSchema: {
|
|
12
|
+
query: z
|
|
13
|
+
.string()
|
|
14
|
+
.describe("The GraphQL query or mutation string. Must be valid GraphQL syntax. " +
|
|
15
|
+
"Example: 'query { shop { name } }'"),
|
|
16
|
+
variables: z
|
|
17
|
+
.record(z.unknown())
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("Variables to pass to the GraphQL operation as a JSON object. " +
|
|
20
|
+
"Keys should match the variable names in your query (without the $ prefix)."),
|
|
21
|
+
},
|
|
22
|
+
annotations: {
|
|
23
|
+
// Cannot determine if read-only since it could be a mutation
|
|
24
|
+
openWorldHint: true,
|
|
25
|
+
},
|
|
26
|
+
}, async ({ query, variables }) => {
|
|
27
|
+
try {
|
|
28
|
+
const response = await client.request(query, {
|
|
29
|
+
variables: variables,
|
|
30
|
+
});
|
|
31
|
+
if (response.errors) {
|
|
32
|
+
return formatGraphQLErrors(response);
|
|
33
|
+
}
|
|
34
|
+
return formatSuccessResponse(response.data);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
return formatErrorResponse(error);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=graphql.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Core tools
|
|
2
|
+
import { registerShopTools } from "./shop.js";
|
|
3
|
+
import { registerGraphQLTools } from "./graphql.js";
|
|
4
|
+
// Smart tools (multi-step workflows)
|
|
5
|
+
import { registerSmartFileTools } from "./smart-files.js";
|
|
6
|
+
import { registerSmartBulkTools } from "./smart-bulk.js";
|
|
7
|
+
import { registerSmartMetaobjectTools } from "./smart-metaobjects.js";
|
|
8
|
+
import { registerSmartSchemaTools } from "./smart-schema.js";
|
|
9
|
+
// Infrastructure tools
|
|
10
|
+
import { registerInfrastructureTools } from "./infrastructure.js";
|
|
11
|
+
export function registerAllTools(server, client, storeDomain) {
|
|
12
|
+
// Core tools (2)
|
|
13
|
+
registerShopTools(server, client); // get_shop_info
|
|
14
|
+
registerGraphQLTools(server, client); // run_graphql_query - the universal escape hatch
|
|
15
|
+
// Smart tools (5) - multi-step workflows that agents would struggle with
|
|
16
|
+
registerSmartFileTools(server, client, storeDomain); // upload_file
|
|
17
|
+
registerSmartBulkTools(server, client, storeDomain); // bulk_export, bulk_import
|
|
18
|
+
registerSmartMetaobjectTools(server, client, storeDomain); // upsert_metaobject
|
|
19
|
+
registerSmartSchemaTools(server, client, storeDomain); // schema_discover
|
|
20
|
+
// Infrastructure tools (3) - config, debugging, stats
|
|
21
|
+
registerInfrastructureTools(server, client, storeDomain); // configure, get_history, get_stats
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infrastructure tools for configuration, history, and stats
|
|
3
|
+
*/
|
|
4
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import type { AdminApiClient } from "../shopify-client.js";
|
|
6
|
+
export declare function registerInfrastructureTools(server: McpServer, client: AdminApiClient, storeDomain: string): void;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infrastructure tools for configuration, history, and stats
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { formatSuccessResponse, formatGraphQLErrors, formatErrorResponse, } from "../errors.js";
|
|
6
|
+
import prisma from "../db.js";
|
|
7
|
+
import { updateQueue, detectTierFromPlan, getCurrentTierInfo, SHOPIFY_TIER_CONFIGS, } from "../queue.js";
|
|
8
|
+
import { getOperationHistory, getOperationStats, getSessionId, } from "../logger.js";
|
|
9
|
+
import { GET_SHOP } from "../graphql/admin/index.js";
|
|
10
|
+
import { enqueue } from "../queue.js";
|
|
11
|
+
export function registerInfrastructureTools(server, client, storeDomain) {
|
|
12
|
+
// CONFIGURE
|
|
13
|
+
server.registerTool("configure", {
|
|
14
|
+
title: "Configure MCP",
|
|
15
|
+
description: "Configure the MCP server settings. " +
|
|
16
|
+
"Set the rate limit tier manually or auto-detect from shop plan. " +
|
|
17
|
+
"Returns current configuration including tier, queue settings, and session info.",
|
|
18
|
+
inputSchema: {
|
|
19
|
+
tier: z
|
|
20
|
+
.enum(["STANDARD", "ADVANCED", "PLUS", "ENTERPRISE"])
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Manually set the rate limit tier. " +
|
|
23
|
+
"STANDARD: 1 req/sec (Basic, Development plans). " +
|
|
24
|
+
"ADVANCED: 2 req/sec (Advanced plans). " +
|
|
25
|
+
"PLUS: 5 req/sec (Shopify Plus). " +
|
|
26
|
+
"ENTERPRISE: 10 req/sec (Commerce Components)."),
|
|
27
|
+
autoDetect: z
|
|
28
|
+
.boolean()
|
|
29
|
+
.optional()
|
|
30
|
+
.describe("Auto-detect tier from shop plan. " +
|
|
31
|
+
"Queries the shop and sets tier based on plan name."),
|
|
32
|
+
},
|
|
33
|
+
annotations: {
|
|
34
|
+
readOnlyHint: false,
|
|
35
|
+
destructiveHint: false,
|
|
36
|
+
idempotentHint: true,
|
|
37
|
+
openWorldHint: false,
|
|
38
|
+
},
|
|
39
|
+
}, async ({ tier, autoDetect }) => {
|
|
40
|
+
try {
|
|
41
|
+
let detectedTier;
|
|
42
|
+
let shopPlan;
|
|
43
|
+
let shopName;
|
|
44
|
+
// Auto-detect from shop plan
|
|
45
|
+
if (autoDetect) {
|
|
46
|
+
const response = await enqueue(() => client.request(GET_SHOP, {}));
|
|
47
|
+
if (response.errors) {
|
|
48
|
+
return formatGraphQLErrors(response);
|
|
49
|
+
}
|
|
50
|
+
const shopData = response.data;
|
|
51
|
+
shopName = shopData.shop.name;
|
|
52
|
+
shopPlan = shopData.shop.plan.displayName;
|
|
53
|
+
detectedTier = detectTierFromPlan({
|
|
54
|
+
shopifyPlus: shopData.shop.plan.shopifyPlus,
|
|
55
|
+
displayName: shopData.shop.plan.displayName,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
// Use provided tier, or detected tier, or keep current
|
|
59
|
+
const newTier = tier || detectedTier;
|
|
60
|
+
if (newTier) {
|
|
61
|
+
updateQueue(newTier);
|
|
62
|
+
// Save to database
|
|
63
|
+
await prisma.storeConfig.upsert({
|
|
64
|
+
where: { storeDomain },
|
|
65
|
+
update: {
|
|
66
|
+
tier: newTier,
|
|
67
|
+
autoDetected: !!autoDetect,
|
|
68
|
+
shopName,
|
|
69
|
+
shopPlan,
|
|
70
|
+
},
|
|
71
|
+
create: {
|
|
72
|
+
storeDomain,
|
|
73
|
+
tier: newTier,
|
|
74
|
+
autoDetected: !!autoDetect,
|
|
75
|
+
shopName,
|
|
76
|
+
shopPlan,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// Get current config
|
|
81
|
+
const currentTier = getCurrentTierInfo();
|
|
82
|
+
const config = await prisma.storeConfig.findUnique({
|
|
83
|
+
where: { storeDomain },
|
|
84
|
+
});
|
|
85
|
+
return formatSuccessResponse({
|
|
86
|
+
success: true,
|
|
87
|
+
config: {
|
|
88
|
+
storeDomain,
|
|
89
|
+
tier: currentTier.tier,
|
|
90
|
+
tierConfig: currentTier.config,
|
|
91
|
+
autoDetected: config?.autoDetected || false,
|
|
92
|
+
shopName: config?.shopName || shopName,
|
|
93
|
+
shopPlan: config?.shopPlan || shopPlan,
|
|
94
|
+
sessionId: getSessionId(),
|
|
95
|
+
},
|
|
96
|
+
availableTiers: Object.keys(SHOPIFY_TIER_CONFIGS),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
return formatErrorResponse(error);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
// GET HISTORY
|
|
104
|
+
server.registerTool("get_history", {
|
|
105
|
+
title: "Get Operation History",
|
|
106
|
+
description: "Query past operations for debugging. " +
|
|
107
|
+
"Filter by tool name, operation type, success/failure, or date range. " +
|
|
108
|
+
"Returns recent operations with query, response, duration, and errors.",
|
|
109
|
+
inputSchema: {
|
|
110
|
+
toolName: z
|
|
111
|
+
.string()
|
|
112
|
+
.optional()
|
|
113
|
+
.describe("Filter by tool name (e.g., 'get_products', 'upload_file')"),
|
|
114
|
+
operationType: z
|
|
115
|
+
.enum(["query", "mutation"])
|
|
116
|
+
.optional()
|
|
117
|
+
.describe("Filter by operation type"),
|
|
118
|
+
success: z
|
|
119
|
+
.boolean()
|
|
120
|
+
.optional()
|
|
121
|
+
.describe("Filter by success (true) or failure (false)"),
|
|
122
|
+
since: z
|
|
123
|
+
.string()
|
|
124
|
+
.optional()
|
|
125
|
+
.describe("ISO date string. Only return operations after this date."),
|
|
126
|
+
limit: z
|
|
127
|
+
.number()
|
|
128
|
+
.int()
|
|
129
|
+
.min(1)
|
|
130
|
+
.max(500)
|
|
131
|
+
.default(50)
|
|
132
|
+
.describe("Maximum number of operations to return (1-500). Default: 50"),
|
|
133
|
+
},
|
|
134
|
+
annotations: {
|
|
135
|
+
readOnlyHint: true,
|
|
136
|
+
destructiveHint: false,
|
|
137
|
+
idempotentHint: true,
|
|
138
|
+
openWorldHint: false,
|
|
139
|
+
},
|
|
140
|
+
}, async ({ toolName, operationType, success, since, limit }) => {
|
|
141
|
+
try {
|
|
142
|
+
const operations = await getOperationHistory({
|
|
143
|
+
storeDomain,
|
|
144
|
+
toolName,
|
|
145
|
+
operationType,
|
|
146
|
+
success,
|
|
147
|
+
since: since ? new Date(since) : undefined,
|
|
148
|
+
limit,
|
|
149
|
+
});
|
|
150
|
+
return formatSuccessResponse({
|
|
151
|
+
success: true,
|
|
152
|
+
count: operations.length,
|
|
153
|
+
operations,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
return formatErrorResponse(error);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
// GET STATS
|
|
161
|
+
server.registerTool("get_stats", {
|
|
162
|
+
title: "Get Usage Statistics",
|
|
163
|
+
description: "Get aggregated statistics for operations. " +
|
|
164
|
+
"Includes total calls, success rate, average duration, and breakdown by tool.",
|
|
165
|
+
inputSchema: {
|
|
166
|
+
period: z
|
|
167
|
+
.enum(["day", "week", "month"])
|
|
168
|
+
.default("day")
|
|
169
|
+
.describe("Time period for statistics. Default: day"),
|
|
170
|
+
},
|
|
171
|
+
annotations: {
|
|
172
|
+
readOnlyHint: true,
|
|
173
|
+
destructiveHint: false,
|
|
174
|
+
idempotentHint: true,
|
|
175
|
+
openWorldHint: false,
|
|
176
|
+
},
|
|
177
|
+
}, async ({ period }) => {
|
|
178
|
+
try {
|
|
179
|
+
// Calculate since date based on period
|
|
180
|
+
const since = new Date();
|
|
181
|
+
switch (period) {
|
|
182
|
+
case "day":
|
|
183
|
+
since.setDate(since.getDate() - 1);
|
|
184
|
+
break;
|
|
185
|
+
case "week":
|
|
186
|
+
since.setDate(since.getDate() - 7);
|
|
187
|
+
break;
|
|
188
|
+
case "month":
|
|
189
|
+
since.setMonth(since.getMonth() - 1);
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
const stats = await getOperationStats({
|
|
193
|
+
storeDomain,
|
|
194
|
+
since,
|
|
195
|
+
});
|
|
196
|
+
return formatSuccessResponse({
|
|
197
|
+
success: true,
|
|
198
|
+
period,
|
|
199
|
+
since: since.toISOString(),
|
|
200
|
+
stats: {
|
|
201
|
+
totalCalls: stats.totalCalls,
|
|
202
|
+
successCount: stats.successCount,
|
|
203
|
+
errorCount: stats.errorCount,
|
|
204
|
+
successRate: stats.totalCalls > 0 ? stats.successCount / stats.totalCalls : 0,
|
|
205
|
+
avgDurationMs: Math.round(stats.avgDurationMs),
|
|
206
|
+
byTool: stats.byTool,
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
return formatErrorResponse(error);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=infrastructure.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { formatSuccessResponse, formatGraphQLErrors, formatErrorResponse, } from "../errors.js";
|
|
2
|
+
import { GET_SHOP } from "../graphql/admin/index.js";
|
|
3
|
+
export function registerShopTools(server, client) {
|
|
4
|
+
server.registerTool("get_shop_info", {
|
|
5
|
+
title: "Get Shop Info",
|
|
6
|
+
description: "Retrieve the store's configuration and settings. Returns store name, email, domain, " +
|
|
7
|
+
"Shopify plan details, primary domain, currency, weight unit, timezone, and billing address. " +
|
|
8
|
+
"Use this to verify store connection or get store-wide settings.",
|
|
9
|
+
inputSchema: {},
|
|
10
|
+
annotations: {
|
|
11
|
+
readOnlyHint: true,
|
|
12
|
+
idempotentHint: true,
|
|
13
|
+
openWorldHint: false,
|
|
14
|
+
},
|
|
15
|
+
}, async () => {
|
|
16
|
+
try {
|
|
17
|
+
const response = await client.request(GET_SHOP);
|
|
18
|
+
if (response.errors) {
|
|
19
|
+
return formatGraphQLErrors(response);
|
|
20
|
+
}
|
|
21
|
+
return formatSuccessResponse(response.data);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return formatErrorResponse(error);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=shop.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart bulk operation tools
|
|
3
|
+
* Handles bulk export and import with polling
|
|
4
|
+
*/
|
|
5
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import type { AdminApiClient } from "../shopify-client.js";
|
|
7
|
+
export declare function registerSmartBulkTools(server: McpServer, client: AdminApiClient, storeDomain: string): void;
|