@shoppexio/mcp-commerce-server 0.5.0 → 0.7.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/README.md +101 -1
- package/package.json +3 -1
- package/src/prompts.mjs +168 -0
- package/src/resources.mjs +112 -0
- package/src/server.mjs +348 -1
package/README.md
CHANGED
|
@@ -1,3 +1,103 @@
|
|
|
1
1
|
# @shoppexio/mcp-commerce-server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Model Context Protocol server for Shoppex commerce operations.
|
|
4
|
+
|
|
5
|
+
Connect Claude Desktop, Claude Code, Cursor, Windsurf, or Codex to your Shoppex store. Read products, orders, customers, and analytics, create payment links, manage coupons, and create or update products — directly from chat.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @shoppexio/mcp-commerce-server
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or run without installing:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx -y @shoppexio/mcp-commerce-server
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Claude Desktop
|
|
20
|
+
|
|
21
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"mcpServers": {
|
|
26
|
+
"shoppex-commerce": {
|
|
27
|
+
"command": "npx",
|
|
28
|
+
"args": ["-y", "@shoppexio/mcp-commerce-server"],
|
|
29
|
+
"env": {
|
|
30
|
+
"SHOPPEX_SHOP_API_KEY": "shx_your_dev_api_key"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Restart Claude Desktop. Tools accept a `shop_api_key` argument on each call; the env var is available for clients that forward env to tool arguments.
|
|
38
|
+
|
|
39
|
+
## Cursor / Windsurf / Other
|
|
40
|
+
|
|
41
|
+
Any MCP client with stdio transport works. Set the same env var and point to `shoppex-mcp-commerce-server` as the command.
|
|
42
|
+
|
|
43
|
+
## Tools (12)
|
|
44
|
+
|
|
45
|
+
### Read
|
|
46
|
+
|
|
47
|
+
| Tool | Purpose |
|
|
48
|
+
|------|---------|
|
|
49
|
+
| `products.list` | Search or list products |
|
|
50
|
+
| `products.get` | Fetch one product by id or uniqid |
|
|
51
|
+
| `orders.list` | List recent orders, optionally filtered |
|
|
52
|
+
| `orders.get` | Fetch one order with line items |
|
|
53
|
+
| `customers.get` | Fetch a customer by id or email |
|
|
54
|
+
| `coupons.list` | List active coupons |
|
|
55
|
+
| `analytics.revenue` | Revenue totals for a date range |
|
|
56
|
+
|
|
57
|
+
### Write
|
|
58
|
+
|
|
59
|
+
| Tool | Purpose |
|
|
60
|
+
|------|---------|
|
|
61
|
+
| `products.create` | Create a product (service, serials, dynamic, file) |
|
|
62
|
+
| `products.update` | Update a product's price, title, gateways, stock, visibility |
|
|
63
|
+
| `coupons.create` | Create a coupon (percentage or fixed discount) |
|
|
64
|
+
| `coupons.update` | Update an existing coupon |
|
|
65
|
+
| `payment_links.create` | Create a payment link |
|
|
66
|
+
|
|
67
|
+
## Required Scopes
|
|
68
|
+
|
|
69
|
+
Your Shoppex Dev API key needs the scopes matching the tools you use (e.g. `products.read`, `products.write`, `orders.read`, `coupons.write`, `analytics.read`, `payment_links.write`).
|
|
70
|
+
|
|
71
|
+
Create one at [dashboard.shoppex.io/developer/api](https://dashboard.shoppex.io/developer/api).
|
|
72
|
+
|
|
73
|
+
## Environment Variables
|
|
74
|
+
|
|
75
|
+
| Variable | Required | Default |
|
|
76
|
+
|----------|----------|---------|
|
|
77
|
+
| `SHOPPEX_SHOP_API_KEY` | passed per call, or set here | — |
|
|
78
|
+
| `SHOPPEX_API_BASE_URL` | no | `https://api.shoppex.io` |
|
|
79
|
+
| `SHOPPEX_DEV_API_TIMEOUT_MS` | no | `10000` |
|
|
80
|
+
|
|
81
|
+
## Example Prompts
|
|
82
|
+
|
|
83
|
+
Once connected in Claude Desktop:
|
|
84
|
+
|
|
85
|
+
- "Create a Discord Nitro 3-month product for $14.99 paid in crypto."
|
|
86
|
+
- "Show me this month's top 10 customers by revenue."
|
|
87
|
+
- "Generate a 20% off coupon for my Discord members, valid until end of month."
|
|
88
|
+
- "Update the price of product `prd_abc123` to $12.99."
|
|
89
|
+
- "Make a payment link for a $50 USDT one-time purchase."
|
|
90
|
+
|
|
91
|
+
## Companion
|
|
92
|
+
|
|
93
|
+
Pair with [`@shoppexio/mcp-theme-server`](https://www.npmjs.com/package/@shoppexio/mcp-theme-server) for storefront theme operations.
|
|
94
|
+
|
|
95
|
+
## Docs
|
|
96
|
+
|
|
97
|
+
- [Developer API Reference](https://docs.shoppex.io/api-reference/introduction)
|
|
98
|
+
- [Quickstart](https://docs.shoppex.io/api-reference/quickstart)
|
|
99
|
+
- [Authentication](https://docs.shoppex.io/api-reference/authentication)
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
Proprietary. © Shoppex.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shoppexio/mcp-commerce-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Shoppex MCP server for commerce operations over the Dev API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -34,6 +34,8 @@
|
|
|
34
34
|
"files": [
|
|
35
35
|
"bin",
|
|
36
36
|
"src/server.mjs",
|
|
37
|
+
"src/prompts.mjs",
|
|
38
|
+
"src/resources.mjs",
|
|
37
39
|
"src/index.d.ts",
|
|
38
40
|
"README.md"
|
|
39
41
|
],
|
package/src/prompts.mjs
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Prompts for Shoppex — curated multi-tool workflows that merchants
|
|
3
|
+
* invoke by name in their MCP client. Each prompt returns a user-message
|
|
4
|
+
* with a full instruction block that primes the LLM to run a pre-planned
|
|
5
|
+
* sequence of tool calls.
|
|
6
|
+
*
|
|
7
|
+
* Registration via `server.registerPrompt(name, {title, description, argsSchema}, cb)`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as z from 'zod/v4';
|
|
11
|
+
|
|
12
|
+
function text(body) {
|
|
13
|
+
return { messages: [{ role: 'user', content: { type: 'text', text: body } }] };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function registerShoppexPrompts(server) {
|
|
17
|
+
server.registerPrompt(
|
|
18
|
+
'shoppex_daily_brief',
|
|
19
|
+
{
|
|
20
|
+
title: 'Daily brief',
|
|
21
|
+
description:
|
|
22
|
+
'Summarise today\'s shop health: revenue, orders, failed payments, open tickets, new disputes. Produces an actionable morning brief.',
|
|
23
|
+
},
|
|
24
|
+
() => text(`Give me a concise morning brief for my Shoppex shop. Follow this order:
|
|
25
|
+
|
|
26
|
+
1. Call analytics_revenue for the current month.
|
|
27
|
+
2. Call orders_list with limit=10 to see the most recent orders.
|
|
28
|
+
3. Call invoices_list with status="pending", limit=5.
|
|
29
|
+
4. Call disputes_list with limit=5 (only flag if status=open).
|
|
30
|
+
5. Call tickets_list with status="open", limit=5.
|
|
31
|
+
|
|
32
|
+
Return a short markdown briefing with these sections:
|
|
33
|
+
- **Revenue today / MTD** (use currency from the analytics response)
|
|
34
|
+
- **Latest orders** (3 most recent, one line each)
|
|
35
|
+
- **Attention items** — pending invoices over 24h, open disputes, open tickets
|
|
36
|
+
- **Suggested next action** — one sentence, concrete
|
|
37
|
+
|
|
38
|
+
Keep it under 150 words. Do not invent numbers — if a tool returned 0 items, say so.`),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
server.registerPrompt(
|
|
42
|
+
'shoppex_launch_product',
|
|
43
|
+
{
|
|
44
|
+
title: 'Launch a product',
|
|
45
|
+
description:
|
|
46
|
+
'End-to-end product launch: create the product, generate a launch coupon, and return a ready-to-send Discord/Telegram announcement.',
|
|
47
|
+
argsSchema: {
|
|
48
|
+
product_name: z.string().min(1),
|
|
49
|
+
price: z.string().min(1),
|
|
50
|
+
coupon_percent: z.string().min(1).optional(),
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
({ product_name, price, coupon_percent }) => text(`Launch a new product called "${product_name}" priced at $${price}. Work in this order:
|
|
54
|
+
|
|
55
|
+
1. Call products_create with { title: "${product_name}", price: ${price}, type: "SERVICE" }. Capture the returned uniqid.
|
|
56
|
+
2. ${coupon_percent ? `Call coupons_create with { code: "LAUNCH${Math.round(Math.random() * 999)}", discount_value: ${coupon_percent}, discount_type: "PERCENTAGE" }.` : 'Skip coupon unless the user explicitly asks for one.'}
|
|
57
|
+
3. Call payment_links_create for the new product so we have a single shareable checkout URL. Use name="${product_name} launch", type="PRODUCT", gateways=["STRIPE","CRYPTO"].
|
|
58
|
+
|
|
59
|
+
After the tools run, produce:
|
|
60
|
+
- A short confirmation in markdown ("Created …").
|
|
61
|
+
- A ready-to-post Discord launch blurb (2-3 sentences, casual tone, include the payment link and the coupon code if created).
|
|
62
|
+
|
|
63
|
+
Do NOT invent the uniqid or URL — use what the tools returned.`),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
server.registerPrompt(
|
|
67
|
+
'shoppex_fraud_check',
|
|
68
|
+
{
|
|
69
|
+
title: 'Fraud check',
|
|
70
|
+
description:
|
|
71
|
+
'Investigate suspicious orders: look at recent high-value orders, match them against the blacklist, and propose block/refund actions.',
|
|
72
|
+
},
|
|
73
|
+
() => text(`Run a fraud-risk scan on the shop. Work in this order:
|
|
74
|
+
|
|
75
|
+
1. Call orders_list with limit=30.
|
|
76
|
+
2. Call disputes_list with limit=20.
|
|
77
|
+
3. Call blacklist_list with limit=50.
|
|
78
|
+
|
|
79
|
+
Analyse the data and answer:
|
|
80
|
+
- Any order whose customer email or IP is already on the blacklist? (exact match)
|
|
81
|
+
- Any order with an unusually high value vs. the shop's typical order size?
|
|
82
|
+
- Any order with a recent dispute against the same customer email?
|
|
83
|
+
- Any order from a country/IP cluster that has disputes (>=2 in the last 30 days)?
|
|
84
|
+
|
|
85
|
+
Return a markdown report:
|
|
86
|
+
- **Risk score**: low / medium / high (overall)
|
|
87
|
+
- **Flagged orders** (table: order id, customer, reason, suggested action)
|
|
88
|
+
- **Recommended blacklist adds** (email/ip/country with reason)
|
|
89
|
+
|
|
90
|
+
Do not execute block/refund automatically. End with: "Want me to add these to the blacklist? Confirm one-by-one."`),
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
server.registerPrompt(
|
|
94
|
+
'shoppex_weekly_recap',
|
|
95
|
+
{
|
|
96
|
+
title: 'Weekly recap',
|
|
97
|
+
description:
|
|
98
|
+
'Seven-day performance recap: revenue, top products, customer additions, operational health. Designed for founder/team standups.',
|
|
99
|
+
},
|
|
100
|
+
() => text(`Give me a 7-day recap for the shop. Work in this order:
|
|
101
|
+
|
|
102
|
+
1. Call analytics_revenue for the last 7 days.
|
|
103
|
+
2. Call orders_list with limit=50 (to estimate order volume).
|
|
104
|
+
3. Call products_list with limit=20 to know the catalog.
|
|
105
|
+
4. Call customers_list with limit=20 (most recent signups).
|
|
106
|
+
5. Call invoices_list with status="paid", limit=20.
|
|
107
|
+
|
|
108
|
+
Return a markdown recap with:
|
|
109
|
+
- **Revenue (7d)** + YoY trend if data allows
|
|
110
|
+
- **Top 3 products by order count**
|
|
111
|
+
- **New customers this week** (count + 2-3 notable emails if VIP-like)
|
|
112
|
+
- **Operational signals** (pending invoices, failed payments if visible)
|
|
113
|
+
- **Focus for next week** — one concrete initiative
|
|
114
|
+
|
|
115
|
+
Tone: factual, founder-facing, under 250 words.`),
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
server.registerPrompt(
|
|
119
|
+
'shoppex_customer_care',
|
|
120
|
+
{
|
|
121
|
+
title: 'Customer care',
|
|
122
|
+
description:
|
|
123
|
+
'Look up a customer end-to-end: orders, invoices, subscriptions, licenses, tickets. Returns a full timeline plus a draft reply.',
|
|
124
|
+
argsSchema: {
|
|
125
|
+
email: z.string().email(),
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
({ email }) => text(`Look up the full history for customer "${email}". Work in this order:
|
|
129
|
+
|
|
130
|
+
1. Call customers_get with email="${email}".
|
|
131
|
+
2. Call orders_list with customer_email="${email}", limit=20.
|
|
132
|
+
3. Call invoices_list with customer_email="${email}", limit=10.
|
|
133
|
+
4. Call licenses_list (then filter locally by the customer's id).
|
|
134
|
+
5. Call subscriptions_list (then filter locally by the customer's id).
|
|
135
|
+
|
|
136
|
+
Return a markdown report:
|
|
137
|
+
- **Summary** (customer name/email, total spend, first-seen / last-seen)
|
|
138
|
+
- **Timeline** (orders + tickets chronologically)
|
|
139
|
+
- **Open issues** (active disputes, unresolved tickets, failed payments)
|
|
140
|
+
- **Draft reply** — a ready-to-send support message that addresses likely concerns, signed as the shop
|
|
141
|
+
|
|
142
|
+
Keep the draft reply polite, short, and actionable. Do not invent details the tools didn't return.`),
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
server.registerPrompt(
|
|
146
|
+
'shoppex_theme_refresh',
|
|
147
|
+
{
|
|
148
|
+
title: 'Theme refresh',
|
|
149
|
+
description:
|
|
150
|
+
'Guided mini-review of the shop\'s active theme: structure, sections, recent changes, publish readiness.',
|
|
151
|
+
},
|
|
152
|
+
() => text(`Do a theme-refresh review. Work in this order:
|
|
153
|
+
|
|
154
|
+
1. Call theme_list to find the active theme for this shop (is_active=true).
|
|
155
|
+
2. Call theme_inspect on the active theme to get sections, settings, tokens.
|
|
156
|
+
3. Call theme_diff on the active theme to see draft vs. published state.
|
|
157
|
+
4. Call theme_latest_run on the active theme to see the last build health.
|
|
158
|
+
|
|
159
|
+
Return a markdown report:
|
|
160
|
+
- **Active theme** (name, scheme, version)
|
|
161
|
+
- **Section inventory** (list of sections + page count)
|
|
162
|
+
- **Draft status** (any pending changes? any build errors?)
|
|
163
|
+
- **Publish readiness** — safe to publish? what risks?
|
|
164
|
+
- **Suggested improvements** — 2-3 concrete section-level tweaks (e.g. "Add a testimonials section above footer"). Keep them specific.
|
|
165
|
+
|
|
166
|
+
Format: founder-facing, under 200 words.`),
|
|
167
|
+
);
|
|
168
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Resources for Shoppex — cacheable read-only context documents the
|
|
3
|
+
* client can attach to a conversation (e.g. Claude Desktop "Attach" menu).
|
|
4
|
+
* Each resource fetches fresh data on read and returns JSON text.
|
|
5
|
+
*
|
|
6
|
+
* Registration via `server.registerResource(name, uri, { title, description, mimeType }, async () => ({ contents: [...] }))`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ShoppexCommerceDevApiClient } from './server.mjs';
|
|
10
|
+
|
|
11
|
+
function resolveShopApiKey() {
|
|
12
|
+
const fromEnv = process.env.SHOPPEX_SHOP_API_KEY?.trim()
|
|
13
|
+
|| process.env.SHOPPEX_API_KEY?.trim()
|
|
14
|
+
|| process.env.SHOP_API_KEY?.trim();
|
|
15
|
+
if (!fromEnv) {
|
|
16
|
+
throw new Error('Shoppex resources require SHOPPEX_SHOP_API_KEY to be set.');
|
|
17
|
+
}
|
|
18
|
+
return fromEnv;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function buildClient() {
|
|
22
|
+
return new ShoppexCommerceDevApiClient({
|
|
23
|
+
shopApiKey: resolveShopApiKey(),
|
|
24
|
+
apiBaseUrl: process.env.SHOPPEX_API_BASE_URL?.trim()
|
|
25
|
+
|| process.env.API_URL?.trim()
|
|
26
|
+
|| 'https://api.shoppex.io',
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function jsonResource(uri, value) {
|
|
31
|
+
return {
|
|
32
|
+
contents: [
|
|
33
|
+
{
|
|
34
|
+
uri,
|
|
35
|
+
mimeType: 'application/json',
|
|
36
|
+
text: JSON.stringify(value, null, 2),
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function registerShoppexResources(server) {
|
|
43
|
+
server.registerResource(
|
|
44
|
+
'shoppex_products_catalog',
|
|
45
|
+
'shoppex://products',
|
|
46
|
+
{
|
|
47
|
+
title: 'Product catalog',
|
|
48
|
+
description: 'Current product catalog for the authenticated shop (up to 50 products, includes variants).',
|
|
49
|
+
mimeType: 'application/json',
|
|
50
|
+
},
|
|
51
|
+
async (uri) => {
|
|
52
|
+
const data = await buildClient().productsList({ limit: 50 });
|
|
53
|
+
return jsonResource(uri.href, data ?? []);
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
server.registerResource(
|
|
58
|
+
'shoppex_recent_orders',
|
|
59
|
+
'shoppex://orders/recent',
|
|
60
|
+
{
|
|
61
|
+
title: 'Recent orders',
|
|
62
|
+
description: '50 most recent orders across all statuses.',
|
|
63
|
+
mimeType: 'application/json',
|
|
64
|
+
},
|
|
65
|
+
async (uri) => {
|
|
66
|
+
const data = await buildClient().ordersList({ limit: 50 });
|
|
67
|
+
return jsonResource(uri.href, data ?? []);
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
server.registerResource(
|
|
72
|
+
'shoppex_analytics_summary',
|
|
73
|
+
'shoppex://analytics/summary',
|
|
74
|
+
{
|
|
75
|
+
title: 'Analytics summary',
|
|
76
|
+
description: 'Month-to-date revenue totals and recent trends.',
|
|
77
|
+
mimeType: 'application/json',
|
|
78
|
+
},
|
|
79
|
+
async (uri) => {
|
|
80
|
+
const data = await buildClient().analyticsRevenue({ currency: 'USD' });
|
|
81
|
+
return jsonResource(uri.href, data ?? {});
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
server.registerResource(
|
|
86
|
+
'shoppex_categories',
|
|
87
|
+
'shoppex://categories',
|
|
88
|
+
{
|
|
89
|
+
title: 'Category tree',
|
|
90
|
+
description: 'Full product category list for the shop.',
|
|
91
|
+
mimeType: 'application/json',
|
|
92
|
+
},
|
|
93
|
+
async (uri) => {
|
|
94
|
+
const data = await buildClient().categoriesList();
|
|
95
|
+
return jsonResource(uri.href, data ?? []);
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
server.registerResource(
|
|
100
|
+
'shoppex_open_tickets',
|
|
101
|
+
'shoppex://tickets/open',
|
|
102
|
+
{
|
|
103
|
+
title: 'Open support tickets',
|
|
104
|
+
description: 'Tickets currently in status=open for the shop.',
|
|
105
|
+
mimeType: 'application/json',
|
|
106
|
+
},
|
|
107
|
+
async (uri) => {
|
|
108
|
+
const data = await buildClient().ticketsList({ limit: 50, status: 'open' });
|
|
109
|
+
return jsonResource(uri.href, data ?? []);
|
|
110
|
+
},
|
|
111
|
+
);
|
|
112
|
+
}
|
package/src/server.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
3
|
import * as z from 'zod/v4';
|
|
4
|
+
import { registerShoppexPrompts } from './prompts.mjs';
|
|
5
|
+
import { registerShoppexResources } from './resources.mjs';
|
|
4
6
|
|
|
5
7
|
const DEV_API_TIMEOUT_MS = Number.parseInt(process.env.SHOPPEX_DEV_API_TIMEOUT_MS ?? '10000', 10);
|
|
6
8
|
|
|
@@ -335,6 +337,102 @@ export class ShoppexCommerceDevApiClient {
|
|
|
335
337
|
body: input,
|
|
336
338
|
});
|
|
337
339
|
}
|
|
340
|
+
|
|
341
|
+
ordersUpdate(orderId, input) {
|
|
342
|
+
return this.request('PATCH', `/dev/v1/orders/${encodeURIComponent(orderId)}`, { body: input });
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
ordersFulfill(orderId, input = {}) {
|
|
346
|
+
return this.request('POST', `/dev/v1/orders/${encodeURIComponent(orderId)}/fulfill`, { body: input });
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
customersWalletCredit(customerId, input) {
|
|
350
|
+
return this.request('POST', `/dev/v1/customers/${encodeURIComponent(customerId)}/wallet/credit`, { body: input });
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
couponsGet(couponId) {
|
|
354
|
+
return this.request('GET', `/dev/v1/coupons/${encodeURIComponent(couponId)}`);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
couponsDelete(couponId) {
|
|
358
|
+
return this.request('DELETE', `/dev/v1/coupons/${encodeURIComponent(couponId)}`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
paymentLinksList() {
|
|
362
|
+
return this.request('GET', '/dev/v1/payment-links');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
paymentLinksGet(linkId) {
|
|
366
|
+
return this.request('GET', `/dev/v1/payment-links/${encodeURIComponent(linkId)}`);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
paymentLinksUpdate(linkId, input) {
|
|
370
|
+
return this.request('PATCH', `/dev/v1/payment-links/${encodeURIComponent(linkId)}`, { body: input });
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
paymentLinksDelete(linkId) {
|
|
374
|
+
return this.request('DELETE', `/dev/v1/payment-links/${encodeURIComponent(linkId)}`);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
paymentLinksToggle(linkId, input = {}) {
|
|
378
|
+
return this.request('POST', `/dev/v1/payment-links/${encodeURIComponent(linkId)}/toggle`, { body: input });
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
ticketsList(input = {}) {
|
|
382
|
+
return this.request('GET', '/dev/v1/tickets', {
|
|
383
|
+
query: { limit: input.limit, status: input.status },
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
ticketsGet(ticketId) {
|
|
388
|
+
return this.request('GET', `/dev/v1/tickets/${encodeURIComponent(ticketId)}`);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
ticketsCreate(input) {
|
|
392
|
+
return this.request('POST', '/dev/v1/tickets', { body: input });
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
ticketsReply(ticketId, input) {
|
|
396
|
+
return this.request('POST', `/dev/v1/tickets/${encodeURIComponent(ticketId)}/messages`, { body: input });
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
ticketsClose(ticketId) {
|
|
400
|
+
return this.request('POST', `/dev/v1/tickets/${encodeURIComponent(ticketId)}/close`);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
blacklistList(input = {}) {
|
|
404
|
+
return this.request('GET', '/dev/v1/blacklist', {
|
|
405
|
+
query: { limit: input.limit, type: input.type },
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
blacklistAdd(input) {
|
|
410
|
+
return this.request('POST', '/dev/v1/blacklist', { body: input });
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
blacklistRemove(entryId) {
|
|
414
|
+
return this.request('DELETE', `/dev/v1/blacklist/${encodeURIComponent(entryId)}`);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
disputesGet(disputeId) {
|
|
418
|
+
return this.request('GET', `/dev/v1/disputes/${encodeURIComponent(disputeId)}`);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
licensesGet(licenseId) {
|
|
422
|
+
return this.request('GET', `/dev/v1/licenses/${encodeURIComponent(licenseId)}`);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
licensesUpdate(licenseId, input) {
|
|
426
|
+
return this.request('PATCH', `/dev/v1/licenses/${encodeURIComponent(licenseId)}`, { body: input });
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
analyticsReportsList() {
|
|
430
|
+
return this.request('GET', '/dev/v1/analytics/reports');
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
analyticsReportGenerate(reportId) {
|
|
434
|
+
return this.request('POST', `/dev/v1/analytics/reports/${encodeURIComponent(reportId)}/generate`);
|
|
435
|
+
}
|
|
338
436
|
}
|
|
339
437
|
|
|
340
438
|
const BaseToolSchema = {
|
|
@@ -800,6 +898,252 @@ export function createCommerceToolCatalog() {
|
|
|
800
898
|
return createClient(args).subscriptionsCancel(args.subscription_id, body);
|
|
801
899
|
},
|
|
802
900
|
},
|
|
901
|
+
{
|
|
902
|
+
name: 'orders_update',
|
|
903
|
+
description: 'Update an order (notes, internal state). Only provided fields change.',
|
|
904
|
+
inputSchema: {
|
|
905
|
+
...BaseToolSchema,
|
|
906
|
+
order_id: z.string().trim().min(1),
|
|
907
|
+
notes: z.string().max(2000).optional().nullable(),
|
|
908
|
+
internal_note: z.string().max(2000).optional().nullable(),
|
|
909
|
+
},
|
|
910
|
+
execute: async (args) => {
|
|
911
|
+
const body = {};
|
|
912
|
+
if (args.notes !== undefined) body.notes = args.notes;
|
|
913
|
+
if (args.internal_note !== undefined) body.internal_note = args.internal_note;
|
|
914
|
+
return createClient(args).ordersUpdate(args.order_id, body);
|
|
915
|
+
},
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
name: 'orders_fulfill',
|
|
919
|
+
description: 'Mark an order as fulfilled. For service products, this confirms delivery; for key-based products, the keys have already been delivered but this marks the order manually.',
|
|
920
|
+
inputSchema: {
|
|
921
|
+
...BaseToolSchema,
|
|
922
|
+
order_id: z.string().trim().min(1),
|
|
923
|
+
message: z.string().max(2000).optional(),
|
|
924
|
+
},
|
|
925
|
+
execute: async (args) => {
|
|
926
|
+
const body = {};
|
|
927
|
+
if (args.message !== undefined) body.message = args.message;
|
|
928
|
+
return createClient(args).ordersFulfill(args.order_id, body);
|
|
929
|
+
},
|
|
930
|
+
},
|
|
931
|
+
{
|
|
932
|
+
name: 'customers_wallet_credit',
|
|
933
|
+
description: 'Credit a customer wallet balance. Useful for refunds, goodwill credits, or promo grants.',
|
|
934
|
+
inputSchema: {
|
|
935
|
+
...BaseToolSchema,
|
|
936
|
+
customer_id: z.string().trim().min(1),
|
|
937
|
+
amount: z.number().positive(),
|
|
938
|
+
currency: z.string().trim().min(3).max(8).default('USD'),
|
|
939
|
+
reason: z.string().max(255).optional(),
|
|
940
|
+
},
|
|
941
|
+
execute: async (args) => createClient(args).customersWalletCredit(args.customer_id, {
|
|
942
|
+
amount: args.amount,
|
|
943
|
+
currency: args.currency,
|
|
944
|
+
reason: args.reason,
|
|
945
|
+
}),
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
name: 'coupons_get',
|
|
949
|
+
description: 'Fetch one coupon by uniqid.',
|
|
950
|
+
inputSchema: { ...BaseToolSchema, coupon_id: z.string().trim().min(1) },
|
|
951
|
+
execute: async (args) => createClient(args).couponsGet(args.coupon_id),
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
name: 'coupons_delete',
|
|
955
|
+
description: 'Delete a coupon by uniqid.',
|
|
956
|
+
inputSchema: { ...BaseToolSchema, coupon_id: z.string().trim().min(1) },
|
|
957
|
+
execute: async (args) => createClient(args).couponsDelete(args.coupon_id),
|
|
958
|
+
},
|
|
959
|
+
{
|
|
960
|
+
name: 'payment_links_list',
|
|
961
|
+
description: 'List payment links for the authenticated shop.',
|
|
962
|
+
inputSchema: { ...BaseToolSchema },
|
|
963
|
+
execute: async (args) => createClient(args).paymentLinksList(),
|
|
964
|
+
},
|
|
965
|
+
{
|
|
966
|
+
name: 'payment_links_get',
|
|
967
|
+
description: 'Fetch one payment link by uniqid.',
|
|
968
|
+
inputSchema: { ...BaseToolSchema, link_id: z.string().trim().min(1) },
|
|
969
|
+
execute: async (args) => createClient(args).paymentLinksGet(args.link_id),
|
|
970
|
+
},
|
|
971
|
+
{
|
|
972
|
+
name: 'payment_links_update',
|
|
973
|
+
description: 'Update a payment link by uniqid. Only provided fields change.',
|
|
974
|
+
inputSchema: {
|
|
975
|
+
...BaseToolSchema,
|
|
976
|
+
link_id: z.string().trim().min(1),
|
|
977
|
+
name: z.string().trim().min(1).max(255).optional(),
|
|
978
|
+
description: z.string().max(255).optional().nullable(),
|
|
979
|
+
price: z.number().positive().optional(),
|
|
980
|
+
currency: z.string().trim().min(3).max(8).optional(),
|
|
981
|
+
gateways: z.array(z.string().trim().min(1)).optional(),
|
|
982
|
+
active: z.boolean().optional(),
|
|
983
|
+
},
|
|
984
|
+
execute: async (args) => {
|
|
985
|
+
const body = {};
|
|
986
|
+
if (args.name !== undefined) body.name = args.name;
|
|
987
|
+
if (args.description !== undefined) body.description = args.description;
|
|
988
|
+
if (args.price !== undefined) body.price = args.price;
|
|
989
|
+
if (args.currency !== undefined) body.currency = args.currency;
|
|
990
|
+
if (args.gateways !== undefined) body.gateways = args.gateways.join(',');
|
|
991
|
+
if (args.active !== undefined) body.status = args.active;
|
|
992
|
+
return createClient(args).paymentLinksUpdate(args.link_id, body);
|
|
993
|
+
},
|
|
994
|
+
},
|
|
995
|
+
{
|
|
996
|
+
name: 'payment_links_delete',
|
|
997
|
+
description: 'Delete a payment link by uniqid.',
|
|
998
|
+
inputSchema: { ...BaseToolSchema, link_id: z.string().trim().min(1) },
|
|
999
|
+
execute: async (args) => createClient(args).paymentLinksDelete(args.link_id),
|
|
1000
|
+
},
|
|
1001
|
+
{
|
|
1002
|
+
name: 'payment_links_toggle',
|
|
1003
|
+
description: 'Activate or deactivate a payment link.',
|
|
1004
|
+
inputSchema: {
|
|
1005
|
+
...BaseToolSchema,
|
|
1006
|
+
link_id: z.string().trim().min(1),
|
|
1007
|
+
active: z.boolean(),
|
|
1008
|
+
},
|
|
1009
|
+
execute: async (args) => createClient(args).paymentLinksToggle(args.link_id, { active: args.active }),
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
name: 'tickets_list',
|
|
1013
|
+
description: 'List customer support tickets. Filter by status (open, closed, etc.).',
|
|
1014
|
+
inputSchema: {
|
|
1015
|
+
...BaseToolSchema,
|
|
1016
|
+
limit: z.number().int().min(1).max(50).optional(),
|
|
1017
|
+
status: z.string().trim().min(1).optional(),
|
|
1018
|
+
},
|
|
1019
|
+
execute: async (args) => createClient(args).ticketsList({
|
|
1020
|
+
limit: args.limit,
|
|
1021
|
+
status: args.status,
|
|
1022
|
+
}),
|
|
1023
|
+
},
|
|
1024
|
+
{
|
|
1025
|
+
name: 'tickets_get',
|
|
1026
|
+
description: 'Fetch one support ticket with all messages.',
|
|
1027
|
+
inputSchema: { ...BaseToolSchema, ticket_id: z.string().trim().min(1) },
|
|
1028
|
+
execute: async (args) => createClient(args).ticketsGet(args.ticket_id),
|
|
1029
|
+
},
|
|
1030
|
+
{
|
|
1031
|
+
name: 'tickets_create',
|
|
1032
|
+
description: 'Create a new support ticket. Used when the merchant wants to proactively open a support conversation with a customer.',
|
|
1033
|
+
inputSchema: {
|
|
1034
|
+
...BaseToolSchema,
|
|
1035
|
+
subject: z.string().trim().min(1).max(255),
|
|
1036
|
+
message: z.string().trim().min(1),
|
|
1037
|
+
customer_id: z.string().trim().min(1).optional(),
|
|
1038
|
+
customer_email: z.string().trim().email().optional(),
|
|
1039
|
+
invoice_id: z.string().trim().min(1).optional(),
|
|
1040
|
+
order_id: z.string().trim().min(1).optional(),
|
|
1041
|
+
},
|
|
1042
|
+
execute: async (args) => {
|
|
1043
|
+
const body = { subject: args.subject, message: args.message };
|
|
1044
|
+
if (args.customer_id !== undefined) body.customer_id = args.customer_id;
|
|
1045
|
+
if (args.customer_email !== undefined) body.customer_email = args.customer_email;
|
|
1046
|
+
if (args.invoice_id !== undefined) body.invoice_id = args.invoice_id;
|
|
1047
|
+
if (args.order_id !== undefined) body.order_id = args.order_id;
|
|
1048
|
+
return createClient(args).ticketsCreate(body);
|
|
1049
|
+
},
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
name: 'tickets_reply',
|
|
1053
|
+
description: 'Post a reply message to a support ticket. The reply is visible to the customer.',
|
|
1054
|
+
inputSchema: {
|
|
1055
|
+
...BaseToolSchema,
|
|
1056
|
+
ticket_id: z.string().trim().min(1),
|
|
1057
|
+
message: z.string().trim().min(1),
|
|
1058
|
+
},
|
|
1059
|
+
execute: async (args) => createClient(args).ticketsReply(args.ticket_id, {
|
|
1060
|
+
message: args.message,
|
|
1061
|
+
}),
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
name: 'tickets_close',
|
|
1065
|
+
description: 'Close a support ticket.',
|
|
1066
|
+
inputSchema: { ...BaseToolSchema, ticket_id: z.string().trim().min(1) },
|
|
1067
|
+
execute: async (args) => createClient(args).ticketsClose(args.ticket_id),
|
|
1068
|
+
},
|
|
1069
|
+
{
|
|
1070
|
+
name: 'blacklist_list',
|
|
1071
|
+
description: 'List blacklist entries (blocked emails, IPs, countries, etc.) for the authenticated shop. Use for fraud prevention.',
|
|
1072
|
+
inputSchema: {
|
|
1073
|
+
...BaseToolSchema,
|
|
1074
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
1075
|
+
type: z.enum(['email', 'ip', 'country', 'domain', 'phone']).optional(),
|
|
1076
|
+
},
|
|
1077
|
+
execute: async (args) => createClient(args).blacklistList({
|
|
1078
|
+
limit: args.limit,
|
|
1079
|
+
type: args.type,
|
|
1080
|
+
}),
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
name: 'blacklist_add',
|
|
1084
|
+
description: 'Add an entry to the shop blacklist. Blocks future checkouts from this email/IP/country/domain.',
|
|
1085
|
+
inputSchema: {
|
|
1086
|
+
...BaseToolSchema,
|
|
1087
|
+
type: z.enum(['email', 'ip', 'country', 'domain', 'phone']),
|
|
1088
|
+
value: z.string().trim().min(1),
|
|
1089
|
+
reason: z.string().max(255).optional(),
|
|
1090
|
+
},
|
|
1091
|
+
execute: async (args) => {
|
|
1092
|
+
const body = { type: args.type, value: args.value };
|
|
1093
|
+
if (args.reason !== undefined) body.reason = args.reason;
|
|
1094
|
+
return createClient(args).blacklistAdd(body);
|
|
1095
|
+
},
|
|
1096
|
+
},
|
|
1097
|
+
{
|
|
1098
|
+
name: 'blacklist_remove',
|
|
1099
|
+
description: 'Remove a blacklist entry by uniqid.',
|
|
1100
|
+
inputSchema: { ...BaseToolSchema, entry_id: z.string().trim().min(1) },
|
|
1101
|
+
execute: async (args) => createClient(args).blacklistRemove(args.entry_id),
|
|
1102
|
+
},
|
|
1103
|
+
{
|
|
1104
|
+
name: 'disputes_get',
|
|
1105
|
+
description: 'Fetch one chargeback dispute with all evidence and history.',
|
|
1106
|
+
inputSchema: { ...BaseToolSchema, dispute_id: z.string().trim().min(1) },
|
|
1107
|
+
execute: async (args) => createClient(args).disputesGet(args.dispute_id),
|
|
1108
|
+
},
|
|
1109
|
+
{
|
|
1110
|
+
name: 'licenses_get',
|
|
1111
|
+
description: 'Fetch one software license by uniqid with its activations and lock history.',
|
|
1112
|
+
inputSchema: { ...BaseToolSchema, license_id: z.string().trim().min(1) },
|
|
1113
|
+
execute: async (args) => createClient(args).licensesGet(args.license_id),
|
|
1114
|
+
},
|
|
1115
|
+
{
|
|
1116
|
+
name: 'licenses_update',
|
|
1117
|
+
description: 'Update a license (revoke, extend validity, reset HWID/IP locks). Set active=false to revoke access.',
|
|
1118
|
+
inputSchema: {
|
|
1119
|
+
...BaseToolSchema,
|
|
1120
|
+
license_id: z.string().trim().min(1),
|
|
1121
|
+
active: z.boolean().optional(),
|
|
1122
|
+
valid_until: z.string().trim().optional().nullable(),
|
|
1123
|
+
hwid_lock: z.string().trim().optional().nullable(),
|
|
1124
|
+
ip_lock: z.string().trim().optional().nullable(),
|
|
1125
|
+
},
|
|
1126
|
+
execute: async (args) => {
|
|
1127
|
+
const body = {};
|
|
1128
|
+
if (args.active !== undefined) body.active = args.active;
|
|
1129
|
+
if (args.valid_until !== undefined) body.valid_until = args.valid_until;
|
|
1130
|
+
if (args.hwid_lock !== undefined) body.hwid_lock = args.hwid_lock;
|
|
1131
|
+
if (args.ip_lock !== undefined) body.ip_lock = args.ip_lock;
|
|
1132
|
+
return createClient(args).licensesUpdate(args.license_id, body);
|
|
1133
|
+
},
|
|
1134
|
+
},
|
|
1135
|
+
{
|
|
1136
|
+
name: 'analytics_reports_list',
|
|
1137
|
+
description: 'List custom analytics reports configured for the shop.',
|
|
1138
|
+
inputSchema: { ...BaseToolSchema },
|
|
1139
|
+
execute: async (args) => createClient(args).analyticsReportsList(),
|
|
1140
|
+
},
|
|
1141
|
+
{
|
|
1142
|
+
name: 'analytics_report_generate',
|
|
1143
|
+
description: 'Re-generate a custom analytics report by id. Useful to get fresh data for PDF/CSV exports.',
|
|
1144
|
+
inputSchema: { ...BaseToolSchema, report_id: z.string().trim().min(1) },
|
|
1145
|
+
execute: async (args) => createClient(args).analyticsReportGenerate(args.report_id),
|
|
1146
|
+
},
|
|
803
1147
|
];
|
|
804
1148
|
}
|
|
805
1149
|
|
|
@@ -820,7 +1164,7 @@ export async function executeCommerceTool(toolName, args) {
|
|
|
820
1164
|
export function createCommerceMcpServer() {
|
|
821
1165
|
const server = new McpServer({
|
|
822
1166
|
name: 'shoppex-commerce',
|
|
823
|
-
version: '0.
|
|
1167
|
+
version: '0.7.0',
|
|
824
1168
|
});
|
|
825
1169
|
|
|
826
1170
|
for (const tool of createCommerceToolCatalog()) {
|
|
@@ -833,6 +1177,9 @@ export function createCommerceMcpServer() {
|
|
|
833
1177
|
});
|
|
834
1178
|
}
|
|
835
1179
|
|
|
1180
|
+
registerShoppexResources(server);
|
|
1181
|
+
registerShoppexPrompts(server);
|
|
1182
|
+
|
|
836
1183
|
return server;
|
|
837
1184
|
}
|
|
838
1185
|
|