@lofder/dsers-mcp-product 1.3.7 → 1.3.8

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 (141) hide show
  1. package/README.md +121 -55
  2. package/dist/auth/browser-finder.d.ts.map +1 -1
  3. package/dist/auth/browser-finder.js +7 -2
  4. package/dist/auth/browser-finder.js.map +1 -1
  5. package/dist/auth/cdp-session.d.ts.map +1 -1
  6. package/dist/auth/cdp-session.js +89 -38
  7. package/dist/auth/cdp-session.js.map +1 -1
  8. package/dist/auth/safari-fallback.d.ts.map +1 -1
  9. package/dist/auth/safari-fallback.js +7 -2
  10. package/dist/auth/safari-fallback.js.map +1 -1
  11. package/dist/auth/terminal-prompt.d.ts.map +1 -1
  12. package/dist/auth/terminal-prompt.js +6 -3
  13. package/dist/auth/terminal-prompt.js.map +1 -1
  14. package/dist/auth/token-store.d.ts.map +1 -1
  15. package/dist/auth/token-store.js +5 -3
  16. package/dist/auth/token-store.js.map +1 -1
  17. package/dist/cli.js +14 -6
  18. package/dist/cli.js.map +1 -1
  19. package/dist/dsers/account.d.ts.map +1 -1
  20. package/dist/dsers/account.js.map +1 -1
  21. package/dist/dsers/auth.d.ts.map +1 -1
  22. package/dist/dsers/auth.js +6 -2
  23. package/dist/dsers/auth.js.map +1 -1
  24. package/dist/dsers/client.d.ts.map +1 -1
  25. package/dist/dsers/client.js +1 -6
  26. package/dist/dsers/client.js.map +1 -1
  27. package/dist/dsers/config.d.ts.map +1 -1
  28. package/dist/dsers/config.js +4 -3
  29. package/dist/dsers/config.js.map +1 -1
  30. package/dist/dsers/product.d.ts.map +1 -1
  31. package/dist/dsers/product.js +5 -10
  32. package/dist/dsers/product.js.map +1 -1
  33. package/dist/dsers/settings.d.ts.map +1 -1
  34. package/dist/dsers/settings.js.map +1 -1
  35. package/dist/error-map.d.ts.map +1 -1
  36. package/dist/error-map.js +4 -6
  37. package/dist/error-map.js.map +1 -1
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +7 -5
  40. package/dist/index.js.map +1 -1
  41. package/dist/instructions.d.ts.map +1 -1
  42. package/dist/instructions.js +12 -1
  43. package/dist/instructions.js.map +1 -1
  44. package/dist/job-store.d.ts.map +1 -1
  45. package/dist/job-store.js.map +1 -1
  46. package/dist/oauth/crypto.d.ts.map +1 -1
  47. package/dist/oauth/crypto.js.map +1 -1
  48. package/dist/provider/helpers.d.ts +28 -0
  49. package/dist/provider/helpers.d.ts.map +1 -0
  50. package/dist/provider/helpers.js +265 -0
  51. package/dist/provider/helpers.js.map +1 -0
  52. package/dist/provider/import-ops.d.ts +23 -0
  53. package/dist/provider/import-ops.d.ts.map +1 -0
  54. package/dist/provider/import-ops.js +350 -0
  55. package/dist/provider/import-ops.js.map +1 -0
  56. package/dist/provider/index.d.ts +38 -0
  57. package/dist/provider/index.d.ts.map +1 -0
  58. package/dist/provider/index.js +172 -0
  59. package/dist/provider/index.js.map +1 -0
  60. package/dist/provider/normalize.d.ts +7 -0
  61. package/dist/provider/normalize.d.ts.map +1 -0
  62. package/dist/provider/normalize.js +239 -0
  63. package/dist/provider/normalize.js.map +1 -0
  64. package/dist/provider/push.d.ts +15 -0
  65. package/dist/provider/push.d.ts.map +1 -0
  66. package/dist/provider/push.js +420 -0
  67. package/dist/provider/push.js.map +1 -0
  68. package/dist/provider/store.d.ts +7 -0
  69. package/dist/provider/store.d.ts.map +1 -0
  70. package/dist/provider/store.js +152 -0
  71. package/dist/provider/store.js.map +1 -0
  72. package/dist/provider.d.ts.map +1 -1
  73. package/dist/provider.js +82 -241
  74. package/dist/provider.js.map +1 -1
  75. package/dist/push-guard.d.ts.map +1 -1
  76. package/dist/push-guard.js +3 -7
  77. package/dist/push-guard.js.map +1 -1
  78. package/dist/push-options.js.map +1 -1
  79. package/dist/resolver.d.ts.map +1 -1
  80. package/dist/resolver.js +1 -2
  81. package/dist/resolver.js.map +1 -1
  82. package/dist/rules.d.ts.map +1 -1
  83. package/dist/rules.js +39 -13
  84. package/dist/rules.js.map +1 -1
  85. package/dist/service/browse-shared.d.ts +6 -0
  86. package/dist/service/browse-shared.d.ts.map +1 -0
  87. package/dist/service/browse-shared.js +26 -0
  88. package/dist/service/browse-shared.js.map +1 -0
  89. package/dist/service/browse.d.ts +20 -0
  90. package/dist/service/browse.d.ts.map +1 -0
  91. package/dist/service/browse.js +159 -0
  92. package/dist/service/browse.js.map +1 -0
  93. package/dist/service/find-product.d.ts +12 -0
  94. package/dist/service/find-product.d.ts.map +1 -0
  95. package/dist/service/find-product.js +51 -0
  96. package/dist/service/find-product.js.map +1 -0
  97. package/dist/service/helpers.d.ts +20 -0
  98. package/dist/service/helpers.d.ts.map +1 -0
  99. package/dist/service/helpers.js +163 -0
  100. package/dist/service/helpers.js.map +1 -0
  101. package/dist/service/import-flow.d.ts +6 -0
  102. package/dist/service/import-flow.d.ts.map +1 -0
  103. package/dist/service/import-flow.js +141 -0
  104. package/dist/service/import-flow.js.map +1 -0
  105. package/dist/service/import-list.d.ts +6 -0
  106. package/dist/service/import-list.d.ts.map +1 -0
  107. package/dist/service/import-list.js +103 -0
  108. package/dist/service/import-list.js.map +1 -0
  109. package/dist/service/index.d.ts +37 -0
  110. package/dist/service/index.d.ts.map +1 -0
  111. package/dist/service/index.js +132 -0
  112. package/dist/service/index.js.map +1 -0
  113. package/dist/service/my-products.d.ts +7 -0
  114. package/dist/service/my-products.d.ts.map +1 -0
  115. package/dist/service/my-products.js +51 -0
  116. package/dist/service/my-products.js.map +1 -0
  117. package/dist/service/preview.d.ts +7 -0
  118. package/dist/service/preview.d.ts.map +1 -0
  119. package/dist/service/preview.js +235 -0
  120. package/dist/service/preview.js.map +1 -0
  121. package/dist/service/push-flow.d.ts +12 -0
  122. package/dist/service/push-flow.d.ts.map +1 -0
  123. package/dist/service/push-flow.js +251 -0
  124. package/dist/service/push-flow.js.map +1 -0
  125. package/dist/service/status.d.ts +6 -0
  126. package/dist/service/status.d.ts.map +1 -0
  127. package/dist/service/status.js +90 -0
  128. package/dist/service/status.js.map +1 -0
  129. package/dist/service.d.ts +1 -1
  130. package/dist/service.d.ts.map +1 -1
  131. package/dist/service.js +23 -32
  132. package/dist/service.js.map +1 -1
  133. package/dist/tools.d.ts +1 -1
  134. package/dist/tools.d.ts.map +1 -1
  135. package/dist/tools.js +162 -114
  136. package/dist/tools.js.map +1 -1
  137. package/dist/types/dsers-api.d.ts +133 -0
  138. package/dist/types/dsers-api.d.ts.map +1 -0
  139. package/dist/types/dsers-api.js +2 -0
  140. package/dist/types/dsers-api.js.map +1 -0
  141. package/package.json +15 -5
package/README.md CHANGED
@@ -1,11 +1,12 @@
1
- # DSers MCP Product — Automate Dropshipping AI tools/AliExpress to Shopify & Wix Import
1
+ # DSers MCP Product — Dropshipping Automation: AliExpress to Shopify & Wix with AI
2
2
 
3
+ [![SafeSkill](https://img.shields.io/badge/SafeSkill-92%2F100-brightgreen)](https://safeskill.dev/scan/@lofder/dsers-mcp-product)
3
4
  [![Smithery](https://smithery.ai/badge/@dsersx/product-mcp)](https://smithery.ai/server/@dsersx/product-mcp)
4
5
  [![npm](https://img.shields.io/npm/v/@lofder/dsers-mcp-product)](https://www.npmjs.com/package/@lofder/dsers-mcp-product)
5
6
  [![MCP Registry](https://img.shields.io/badge/MCP_Registry-listed-blue)](https://registry.modelcontextprotocol.io/servers/io.github.lofder/dsers-mcp-product)
6
7
  [![Glama](https://glama.ai/mcp/servers/lofder/dsers-mcp-product/badges/score.svg)](https://glama.ai/mcp/servers/lofder/dsers-mcp-product)
7
8
 
8
- > An open-source MCP server to automate DSers product import, bulk edit variants, and push to Shopify or Wix using AI.
9
+ > An open-source MCP server to automate DSers product sourcing, import, bulk edit, and push to Shopify or Wix using AI.
9
10
 
10
11
  > [English](#english) | [中文](#中文)
11
12
 
@@ -15,13 +16,13 @@
15
16
 
16
17
  ## English
17
18
 
18
- > **⚠️ Rapid iteration phase:** I only have limited DSers accounts and stores to test with, so it's hard to cover every edge case. I'm testing as many scenarios as I can, fixing issues as I find them though fixes sometimes introduce new ones. Releases will be very frequent during this period. Please always use the latest version, and I'd really appreciate it if you could [help me test](https://github.com/lofder/dsers-mcp-product/issues)!
19
-
20
- **DSers MCP Product** is an open-source [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that lets AI Agents automate the entire DSers import workflow — from AliExpress / Alibaba / [Accio.com](https://www.accio.com/) product URL to Shopify or Wix store listing. Bulk import, batch edit variants, clean AliExpress titles, apply pricing rules, and push to multiple stores — all with a single sentence to your AI agent.
19
+ **DSers MCP Product** is an open-source [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that lets AI Agents automate the entire DSers import workflow from product sourcing to Shopify or Wix store listing. Search the DSers product pool, import from AliExpress / Alibaba / [Accio.com](https://www.accio.com/), bulk edit variants, apply pricing rules, and push to multiple stores all with a single sentence to your AI agent.
21
20
 
22
21
  #### What can it do?
23
22
 
23
+ - **Product sourcing** — search the DSers product pool by keyword or image, find products to sell
24
24
  - **One-click import** — paste a product link, your AI agent imports it into DSers automatically
25
+ - **Browse your catalog** — view your import staging list and products already pushed to stores
25
26
  - **Clean up titles** — strips the messy keyword-stuffed AliExpress titles into something readable
26
27
  - **Pricing rules** — markup multiplier (e.g. 2.5x), fixed markup (e.g. +$5), compare-at / sale prices
27
28
  - **Batch import** — import multiple products at once with a list of URLs
@@ -44,6 +45,7 @@ The server is hosted on [Vercel](https://dsers-mcp-product.vercel.app/api/mcp) a
44
45
  | MCP Marketplace | [mcp-marketplace.io](https://mcp-marketplace.io/server/io-github-lofder-dsers-mcp-product) |
45
46
  | awesome-mcp-servers | [punkpeye/awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers) |
46
47
  | Dev.to | [Article: I Built an MCP Server to Automate Dropshipping](https://dev.to/_95a3e57463e6442feacd0/i-built-an-mcp-server-to-automate-dropshipping-product-imports-3m5b) |
48
+ | Dev.to | [Tutorial: How to Automate AliExpress to Shopify with AI](https://dev.to/_95a3e57463e6442feacd0/how-to-automate-aliexpress-to-shopify-product-import-with-ai-step-by-step-guide-3f5a) |
47
49
 
48
50
  ### Supported product sources
49
51
 
@@ -71,7 +73,6 @@ This works for both AliExpress and Alibaba products found on Accio.
71
73
  | [ARCHITECTURE.md](ARCHITECTURE.md) | Three-layer architecture, directory structure, data flow |
72
74
  | [USAGE.md](USAGE.md) | Installation, client config (Cursor, Claude Desktop), scenario examples |
73
75
  | [SKILL.md](SKILL.md) | AI agent instruction file — workflow, rules, push options, error handling |
74
- | [SKILL-CN.md](SKILL-CN.md) | Chinese human-readable guide for SKILL.md |
75
76
 
76
77
  ### What You Need
77
78
 
@@ -139,6 +140,12 @@ Once set up, just talk to your AI agent in plain language:
139
140
 
140
141
  > "Batch import these 3 products and push them all to my store: [URL1] [URL2] [URL3]"
141
142
 
143
+ > "Search for phone cases under $5 on the DSers product pool"
144
+
145
+ > "Show me what's in my import list"
146
+
147
+ > "What products have I pushed to my store?"
148
+
142
149
  > "Push this product to all my connected stores"
143
150
 
144
151
  > "Rewrite the title and description for SEO, then push to my store"
@@ -156,21 +163,11 @@ Or browse at [smithery.ai/server/@dsersx/product-mcp](https://smithery.ai/server
156
163
  ### Install from Source
157
164
 
158
165
  ```bash
159
- # Clone
160
166
  git clone https://github.com/lofder/dsers-mcp-product.git
161
167
  cd dsers-mcp-product
162
-
163
- # Install
164
168
  npm install
165
-
166
- # Configure (copy and fill in your DSers credentials)
167
- cp .env.example .env
168
-
169
- # Type check
170
- npx tsc --noEmit
171
-
172
- # Run with Smithery dev
173
- npx @smithery/cli dev ./src/index.ts
169
+ npm run build
170
+ npm test
174
171
  ```
175
172
 
176
173
  ### Project Structure
@@ -178,32 +175,48 @@ npx @smithery/cli dev ./src/index.ts
178
175
  ```
179
176
  dsers-mcp-product/
180
177
  ├── src/
181
- │ ├── index.ts # MCP server entry — tool registration
182
- │ ├── service.ts # Import flow orchestration (7 operations)
183
- │ ├── provider.ts # DSers API adapter
178
+ │ ├── cli.ts # npx entry — stdio transport, login/logout
179
+ │ ├── index.ts # MCP server init, tool registration
180
+ │ ├── instructions.ts # Server-level prompts (agent instructions)
181
+ │ ├── tools.ts # 12 MCP tools — schema + handler
184
182
  │ ├── rules.ts # Rule validation & application engine
183
+ │ ├── push-guard.ts # Pre-push safety checks
185
184
  │ ├── push-options.ts # Push option normalization
186
185
  │ ├── resolver.ts # URL normalization (AliExpress/Alibaba/Accio)
187
- │ ├── job-store.ts # File-based job persistence
188
- └── dsers/ # Low-level DSers API wrappers
189
- ├── config.ts # Configuration & environment
190
- ├── auth.ts # Login, session cache, auto-refresh
191
- ├── client.ts # Authenticated HTTP client
192
- ├── account.ts # Store & user management APIs
193
- ├── product.ts # Import list & push APIs
194
- └── settings.ts # Shipping, pricing, billing APIs
195
- ├── test/ # Smoke tests
196
- ├── smithery.yaml # Smithery runtime config
186
+ │ ├── provider/ # DSers API adapter (split by concern)
187
+ │ ├── index.ts # Provider class + interface
188
+ ├── store.ts # Store discovery, platform detection
189
+ ├── import-ops.ts # Import, save draft, delete
190
+ ├── push.ts # Push execution, shipping logistics
191
+ ├── normalize.ts # Data normalization
192
+ │ └── helpers.ts # Shared utilities
193
+ ├── service/ # Business logic orchestration
194
+ │ │ ├── index.ts # ImportFlowService class
195
+ │ │ ├── import-flow.ts # Import workflows
196
+ │ │ ├── push-flow.ts # Push workflows
197
+ │ │ ├── preview.ts # Preview & visibility
198
+ │ │ ├── status.ts # Job status & deletion
199
+ │ │ ├── import-list.ts # Import list browsing (with enrich)
200
+ │ │ ├── my-products.ts # Pushed products browsing
201
+ │ │ └── find-product.ts # Product pool search
202
+ │ ├── dsers/ # Low-level DSers API wrappers
203
+ │ │ ├── config.ts # Configuration
204
+ │ │ ├── auth.ts # Session management
205
+ │ │ ├── client.ts # Authenticated HTTP client
206
+ │ │ ├── account.ts # Store & user APIs
207
+ │ │ ├── product.ts # Product & import APIs
208
+ │ │ └── settings.ts # Shipping, pricing, billing APIs
209
+ │ └── auth/ # Browser login (CDP)
210
+ ├── test/ # Vitest unit tests (298 tests)
197
211
  ├── package.json
198
- ├── tsconfig.json
199
- └── .env.example
212
+ └── tsconfig.json
200
213
  ```
201
214
 
202
- ### Nine Tools
215
+ ### Twelve Tools
203
216
 
204
217
  | # | Tool | What it does |
205
218
  |---|------|-------------|
206
- | 1 | `dsers_store_discover` | See your connected stores, available shipping methods, pricing rules, and what rules you can apply |
219
+ | 1 | `dsers_store_discover` | See your connected stores, shipping methods, pricing rules, and capabilities |
207
220
  | 2 | `dsers_rules_validate` | Test your pricing or title rules before applying — catches mistakes early |
208
221
  | 3 | `dsers_product_import` | Paste a product URL, optionally apply pricing/title rules, and get a preview before pushing |
209
222
  | 4 | `dsers_product_preview` | Review a product you already imported — title, price, variants, stock at a glance |
@@ -212,8 +225,11 @@ dsers-mcp-product/
212
225
  | 7 | `dsers_store_push` | Send products to your Shopify or Wix store — one at a time, in bulk, or to all stores at once |
213
226
  | 8 | `dsers_job_status` | Check if a push finished and whether it succeeded |
214
227
  | 9 | `dsers_product_delete` | Delete a product from the DSers import list (irreversible, requires confirmation) |
228
+ | 10 | `dsers_import_list` | Browse your import staging list with cost & sell price, stock, markup status |
229
+ | 11 | `dsers_my_products` | See products already pushed to a store, with supplier links for re-import |
230
+ | 12 | `dsers_find_product` | Search the DSers product pool by keyword or image — results link directly to import |
215
231
 
216
- All tools return clear error messages so your AI agent knows what went wrong and what to do next — no cryptic error codes.
232
+ All tools return clear error messages so your AI agent knows what went wrong and what to do next.
217
233
 
218
234
  ### Pre-Push Safety Checks
219
235
 
@@ -252,11 +268,38 @@ Ready-made workflows your AI client can use directly:
252
268
 
253
269
  ### What's Next
254
270
 
271
+ - Product pool search enhancements (category filters, URL-based search, product detail view)
255
272
  - Support more store platforms that DSers already connects to (eBay, Wish, etc.)
256
273
  - Smarter pricing rule templates
257
274
  - More granular inventory sync options
258
275
 
259
- Got an idea or feature request? [Open an issue](https://github.com/lofder/dsers-mcp-product/issues) — suggestions and contributions from other developers are very welcome.
276
+ Got an idea or feature request? [Open an issue](https://github.com/lofder/dsers-mcp-product/issues) — contributions are very welcome.
277
+
278
+ ### FAQ
279
+
280
+ **What is DSers MCP Product?**
281
+ DSers MCP Product is a free, open-source MCP server that automates dropshipping product imports from AliExpress, Alibaba, and 1688 to Shopify and Wix stores using AI agents like Claude and Cursor. Instead of manually copying product data, you tell your AI agent what to do in plain English.
282
+
283
+ **How does it work?**
284
+ You type a command like "Import this product, mark up 2.5x, push to my store" in Cursor or Claude Desktop. The MCP server handles the entire workflow: fetching product data, applying pricing rules, editing variants, and pushing to your connected stores.
285
+
286
+ **Is it free?**
287
+ Yes. The tool is open-source (MIT license) and completely free to use. You only need a free DSers account and a Shopify or Wix store connected in DSers.
288
+
289
+ **Is it safe? Do I need to share my password?**
290
+ No passwords are stored or transmitted. Authentication uses a zero-password browser login — you log in on DSers's own website, and the tool picks up the session token. Your credentials never touch the MCP server. The project scored 92/100 on [SafeSkill](https://safeskill.dev/scan/@lofder/dsers-mcp-product) security scanning.
291
+
292
+ **What AI clients does it support?**
293
+ Cursor, Claude Desktop, Claude Code, Windsurf, and any MCP-compatible client that supports stdio transport.
294
+
295
+ **How is this different from AliDropify, AutoDS, or other dropshipping tools?**
296
+ Most dropshipping tools have their own UI and require you to click through web interfaces. DSers MCP Product takes a fundamentally different approach — it connects directly to your AI agent, so you automate workflows through conversation instead of clicking buttons. It's also open-source and free, with no subscription tiers.
297
+
298
+ **What product sources does it support?**
299
+ AliExpress, Alibaba.com, Accio.com (Alibaba's AI sourcing tool), and 1688 (requires DSers authorization).
300
+
301
+ **Can I push to multiple stores at once?**
302
+ Yes. The `dsers_store_push` tool supports pushing a single product to all your connected Shopify and Wix stores in one command, with per-store pricing and visibility options.
260
303
 
261
304
  ### Also Available
262
305
 
@@ -272,13 +315,13 @@ MIT
272
315
 
273
316
  ## 中文
274
317
 
275
- > **⚠️ 快速迭代阶段:** 我手上只有有限的店铺和 DSers 账号,很难囊括所有场景。我在尽可能测试各种情况下可能出现的问题,发现一个修一个,修的过程中也可能连带引出其他问题,因此近期更新迭代速度会非常快。请时刻关注最新版本,也非常欢迎大家来[帮我测试](https://github.com/lofder/dsers-mcp-product/issues)
276
-
277
- **DSers MCP Product** 是一个开源的 [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) 服务器,让 AI Agent 自动完成 DSers 的整个商品导入流程 —— 从速卖通 / Alibaba / [Accio.com](https://www.accio.com/) 商品链接到 Shopify 或 Wix 店铺上架。批量导入、批量编辑变体、清理速卖通标题、应用定价规则、推送到多个店铺 —— 只需一句话给你的 AI agent。
318
+ **DSers MCP Product** 是一个开源的 [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) 服务器,让 AI Agent 自动完成 DSers 的整个商品流程 —— 从选品搜索到 Shopify 或 Wix 店铺上架。搜索 DSers 商品池、从速卖通 / Alibaba / [Accio.com](https://www.accio.com/) 导入商品、批量编辑变体、应用定价规则、推送到多个店铺 —— 只需一句话给你的 AI agent。
278
319
 
279
320
  #### 能做什么?
280
321
 
322
+ - **选品搜索** — 在 DSers 商品池里按关键词或图片搜索,找到值得卖的商品
281
323
  - **一句话导入** — 贴个商品链接,AI 助手自动导入到 DSers
324
+ - **浏览商品库** — 查看导入待推送列表和已上架商品,包含成本价、售价、加价状态
282
325
  - **标题清理** — 把速卖通那些关键词堆砌的乱标题整理成人话
283
326
  - **定价规则** — 加价倍率(比如 2.5 倍)、固定加价(比如 +5 美金)、划线价
284
327
  - **批量导入** — 一次丢一堆链接,全部导入
@@ -301,6 +344,7 @@ MIT
301
344
  | MCP Marketplace | [mcp-marketplace.io](https://mcp-marketplace.io/server/io-github-lofder-dsers-mcp-product) |
302
345
  | awesome-mcp-servers | [punkpeye/awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers) |
303
346
  | Dev.to | [技术文章:I Built an MCP Server to Automate Dropshipping](https://dev.to/_95a3e57463e6442feacd0/i-built-an-mcp-server-to-automate-dropshipping-product-imports-3m5b) |
347
+ | Dev.to | [教程:How to Automate AliExpress to Shopify with AI](https://dev.to/_95a3e57463e6442feacd0/how-to-automate-aliexpress-to-shopify-product-import-with-ai-step-by-step-guide-3f5a) |
304
348
 
305
349
  ### 支持的商品来源
306
350
 
@@ -328,7 +372,6 @@ Accio 上搜出来的速卖通和阿里巴巴商品都能用。
328
372
  | [ARCHITECTURE.md](ARCHITECTURE.md) | 三层架构、目录结构、数据流 |
329
373
  | [USAGE.md](USAGE.md) | 安装、客户端配置(Cursor、Claude Desktop)、使用场景 |
330
374
  | [SKILL.md](SKILL.md) | AI agent 指令文件 — 工作流、规则、推送选项、错误处理 |
331
- | [SKILL-CN.md](SKILL-CN.md) | SKILL.md 的中文说明 |
332
375
 
333
376
  ### 使用前提
334
377
 
@@ -396,6 +439,12 @@ npx @lofder/dsers-mcp-product login
396
439
 
397
440
  > "批量导入这 3 个商品,全部推到店铺:[链接1] [链接2] [链接3]"
398
441
 
442
+ > "帮我搜一下 5 美金以下的手机壳"
443
+
444
+ > "看下我的导入列表里有什么"
445
+
446
+ > "我已经推送了哪些商品到店铺?"
447
+
399
448
  > "把这个商品推到我所有店铺"
400
449
 
401
450
  > "帮我把标题和描述重写一下做 SEO 优化,然后推送"
@@ -413,24 +462,14 @@ npx @smithery/cli mcp add @dsersx/product-mcp --client cursor
413
462
  ### 从源码安装
414
463
 
415
464
  ```bash
416
- # 克隆
417
465
  git clone https://github.com/lofder/dsers-mcp-product.git
418
466
  cd dsers-mcp-product
419
-
420
- # 安装
421
467
  npm install
422
-
423
- # 配置(复制并填写你的 DSers 账户信息)
424
- cp .env.example .env
425
-
426
- # 类型检查
427
- npx tsc --noEmit
428
-
429
- # 开发运行
430
- npx @smithery/cli dev ./src/index.ts
468
+ npm run build
469
+ npm test
431
470
  ```
432
471
 
433
- ### 九个工具
472
+ ### 十二个工具
434
473
 
435
474
  | # | 工具 | 干什么的 |
436
475
  |---|------|---------|
@@ -443,8 +482,11 @@ npx @smithery/cli dev ./src/index.ts
443
482
  | 7 | `dsers_store_push` | 把商品推到你的 Shopify 或 Wix 店铺 — 单个推、批量推、或一次推到所有店铺 |
444
483
  | 8 | `dsers_job_status` | 看看推送完了没、成功了没 |
445
484
  | 9 | `dsers_product_delete` | 从 DSers 导入列表中删除商品(不可恢复,需确认) |
485
+ | 10 | `dsers_import_list` | 浏览导入待推送列表,含成本价、售价、库存、加价状态 |
486
+ | 11 | `dsers_my_products` | 查看已推到店铺的商品,带供应商链接方便重新导入 |
487
+ | 12 | `dsers_find_product` | 在 DSers 商品池搜索,支持关键词和以图搜图,结果可直接导入 |
446
488
 
447
- 报错时会返回清晰的消息,AI 助手能看懂出了什么问题、该怎么办 — 不会给你一串看不懂的错误码。
489
+ 报错时会返回清晰的消息,AI 助手能看懂出了什么问题、该怎么办。
448
490
 
449
491
  ### 推送前安全校验
450
492
 
@@ -475,12 +517,36 @@ MCP 客户端可直接展示给用户的工作流模板:
475
517
 
476
518
  ### 后续计划
477
519
 
520
+ - 商品池搜索增强(分类过滤、URL 搜索、商品详情查看)
478
521
  - 支持更多 DSers 已接入的店铺平台(eBay、Wish 等)
479
522
  - 更智能的定价规则模板
480
523
  - 更精细的库存同步选项
481
524
 
482
525
  有想法或需求?欢迎 [提 issue](https://github.com/lofder/dsers-mcp-product/issues) —— 非常欢迎其他开发者的建议和贡献。
483
526
 
527
+ ### 常见问题
528
+
529
+ **DSers MCP Product 是什么?**
530
+ 一个免费开源的 MCP 服务器,让 AI Agent(如 Claude、Cursor)自动完成从速卖通/Alibaba/1688 到 Shopify/Wix 店铺的商品导入全流程。不用手动复制商品数据,用自然语言对话就能操作。
531
+
532
+ **怎么用?**
533
+ 在 Cursor 或 Claude Desktop 里输入"导入这个商品,加价 2.5 倍,推送到我的店铺",MCP 服务器自动完成抓取商品数据、应用定价规则、编辑变体、推送到店铺的全部流程。
534
+
535
+ **收费吗?**
536
+ 完全免费。项目开源(MIT 协议),你只需要一个免费的 DSers 账号和已连接的 Shopify 或 Wix 店铺。
537
+
538
+ **安全吗?需要提供密码吗?**
539
+ 不需要。登录采用零密码浏览器认证——你在 DSers 官网登录,工具只接收会话令牌,密码永远不会经过 MCP 服务器。项目在 [SafeSkill](https://safeskill.dev/scan/@lofder/dsers-mcp-product) 安全扫描中获得 92/100 分。
540
+
541
+ **支持哪些 AI 客户端?**
542
+ Cursor、Claude Desktop、Claude Code、Windsurf,以及任何支持 stdio 传输的 MCP 兼容客户端。
543
+
544
+ **和 AliDropify、AutoDS 等 Dropshipping 工具有什么区别?**
545
+ 大多数 Dropshipping 工具有自己的界面,需要点来点去。DSers MCP Product 完全不同——它直接连接你的 AI Agent,用对话代替点击来自动化工作流。而且完全开源免费,没有订阅分级。
546
+
547
+ **能同时推送到多个店铺吗?**
548
+ 可以。`dsers_store_push` 支持一条命令将商品推送到所有已连接的 Shopify 和 Wix 店铺,支持按店铺设置不同的定价和可见性。
549
+
484
550
  ### 其他版本
485
551
 
486
552
  也提供 [Python 版本](https://github.com/lofder/dsers-mcp-product-py),适用于本地 stdio 部署。
@@ -1 +1 @@
1
- {"version":3,"file":"browser-finder.d.ts","sourceRoot":"","sources":["../../src/auth/browser-finder.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AA4FD,wBAAgB,mBAAmB,IAAI,YAAY,GAAG,IAAI,CAMzD"}
1
+ {"version":3,"file":"browser-finder.d.ts","sourceRoot":"","sources":["../../src/auth/browser-finder.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AA8FD,wBAAgB,mBAAmB,IAAI,YAAY,GAAG,IAAI,CAMzD"}
@@ -48,7 +48,10 @@ function findOnMacOS() {
48
48
  }
49
49
  // mdfind fallback for non-standard install locations
50
50
  try {
51
- const result = execSync('mdfind "kMDItemCFBundleIdentifier == com.google.Chrome" 2>/dev/null', { encoding: "utf-8", timeout: 3000 }).trim();
51
+ const result = execSync('mdfind "kMDItemCFBundleIdentifier == com.google.Chrome" 2>/dev/null', {
52
+ encoding: "utf-8",
53
+ timeout: 3000,
54
+ }).trim();
52
55
  if (result) {
53
56
  const appPath = result.split("\n")[0];
54
57
  const execPath = join(appPath, "Contents/MacOS/Google Chrome");
@@ -56,7 +59,9 @@ function findOnMacOS() {
56
59
  return { name: "Google Chrome", path: execPath };
57
60
  }
58
61
  }
59
- catch { /* mdfind unavailable or timed out */ }
62
+ catch {
63
+ /* mdfind unavailable or timed out */
64
+ }
60
65
  return null;
61
66
  }
62
67
  function findOnLinux() {
@@ -1 +1 @@
1
- {"version":3,"file":"browser-finder.js","sourceRoot":"","sources":["../../src/auth/browser-finder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browser-finder.js","sourceRoot":"","sources":["../../src/auth/browser-finder.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"cdp-session.d.ts","sourceRoot":"","sources":["../../src/auth/cdp-session.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAoND,wBAAsB,WAAW,CAC/B,WAAW,EAAE,MAAM,EACnB,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAe,GACpC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAmEhC"}
1
+ {"version":3,"file":"cdp-session.d.ts","sourceRoot":"","sources":["../../src/auth/cdp-session.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAyQD,wBAAsB,WAAW,CAC/B,WAAW,EAAE,MAAM,EACnB,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAe,GACpC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAqGhC"}
@@ -5,11 +5,7 @@ import { mkdtempSync, rmSync } from "node:fs";
5
5
  import { tmpdir } from "node:os";
6
6
  import { join } from "node:path";
7
7
  const DSERS_LOGIN_URL = "https://accounts.dsers.com/accounts/login";
8
- const COOKIE_CHECK_URLS = [
9
- "https://accounts.dsers.com",
10
- "https://app.dsers.com",
11
- "https://www.dsers.com",
12
- ];
8
+ const COOKIE_CHECK_URLS = ["https://accounts.dsers.com", "https://app.dsers.com", "https://www.dsers.com"];
13
9
  const POLL_MS = 2_000;
14
10
  const PORT_TIMEOUT_MS = 30_000;
15
11
  const TARGET_RETRY_MS = 500;
@@ -29,7 +25,9 @@ class CDPClient {
29
25
  sock.on("close", () => this.shutdown());
30
26
  sock.on("error", () => this.shutdown());
31
27
  }
32
- get closed() { return this._closed; }
28
+ get closed() {
29
+ return this._closed;
30
+ }
33
31
  send(method, params) {
34
32
  if (this._closed)
35
33
  return Promise.reject(new Error("CDP closed"));
@@ -50,11 +48,15 @@ class CDPClient {
50
48
  try {
51
49
  this.sock.write(f);
52
50
  }
53
- catch { /* ignore */ }
51
+ catch {
52
+ /* ignore */
53
+ }
54
54
  try {
55
55
  this.sock.end();
56
56
  }
57
- catch { /* ignore */ }
57
+ catch {
58
+ /* ignore */
59
+ }
58
60
  this._closed = true;
59
61
  }
60
62
  /* ---- framing ---- */
@@ -82,7 +84,9 @@ class CDPClient {
82
84
  }
83
85
  onData(chunk) {
84
86
  this.buf = Buffer.concat([this.buf, chunk]);
85
- while (this.parseFrame()) { /* keep consuming */ }
87
+ while (this.parseFrame()) {
88
+ /* keep consuming */
89
+ }
86
90
  }
87
91
  parseFrame() {
88
92
  if (this.buf.length < 2)
@@ -117,7 +121,9 @@ class CDPClient {
117
121
  }
118
122
  }
119
123
  }
120
- catch { /* malformed */ }
124
+ catch {
125
+ /* malformed */
126
+ }
121
127
  }
122
128
  else if (opcode === 0x08) {
123
129
  this.shutdown();
@@ -138,16 +144,23 @@ function httpGetJson(port, path) {
138
144
  return new Promise((resolve, reject) => {
139
145
  const r = httpReq({ hostname: "127.0.0.1", port, path, method: "GET" }, (res) => {
140
146
  let d = "";
141
- res.on("data", (c) => { d += c; });
142
- res.on("end", () => { try {
143
- resolve(JSON.parse(d));
144
- }
145
- catch {
146
- reject(new Error("bad json"));
147
- } });
147
+ res.on("data", (c) => {
148
+ d += c;
149
+ });
150
+ res.on("end", () => {
151
+ try {
152
+ resolve(JSON.parse(d));
153
+ }
154
+ catch {
155
+ reject(new Error("bad json"));
156
+ }
157
+ });
148
158
  });
149
159
  r.on("error", reject);
150
- r.setTimeout(5000, () => { r.destroy(); reject(new Error("http timeout")); });
160
+ r.setTimeout(5000, () => {
161
+ r.destroy();
162
+ reject(new Error("http timeout"));
163
+ });
151
164
  r.end();
152
165
  });
153
166
  }
@@ -156,13 +169,18 @@ function wsConnect(url) {
156
169
  const u = new URL(url.replace(/^ws:/, "http:"));
157
170
  const key = randomBytes(16).toString("base64");
158
171
  const r = httpReq({
159
- hostname: u.hostname, port: u.port,
160
- path: u.pathname + u.search, method: "GET",
172
+ hostname: u.hostname,
173
+ port: u.port,
174
+ path: u.pathname + u.search,
175
+ method: "GET",
161
176
  headers: { Upgrade: "websocket", Connection: "Upgrade", "Sec-WebSocket-Key": key, "Sec-WebSocket-Version": "13" },
162
177
  });
163
178
  r.on("upgrade", (_res, socket) => resolve(new CDPClient(socket)));
164
179
  r.on("error", reject);
165
- r.setTimeout(5000, () => { r.destroy(); reject(new Error("ws timeout")); });
180
+ r.setTimeout(5000, () => {
181
+ r.destroy();
182
+ reject(new Error("ws timeout"));
183
+ });
166
184
  r.end();
167
185
  });
168
186
  }
@@ -170,8 +188,13 @@ function waitForPort(proc) {
170
188
  return new Promise((resolve, reject) => {
171
189
  const timer = setTimeout(() => reject(new Error("Browser did not expose debug port within 30 s")), PORT_TIMEOUT_MS);
172
190
  let settled = false;
173
- const fail = (msg) => { if (settled)
174
- return; settled = true; clearTimeout(timer); reject(new Error(msg)); };
191
+ const fail = (msg) => {
192
+ if (settled)
193
+ return;
194
+ settled = true;
195
+ clearTimeout(timer);
196
+ reject(new Error(msg));
197
+ };
175
198
  let acc = "";
176
199
  proc.stderr?.on("data", (c) => {
177
200
  acc += c.toString();
@@ -194,7 +217,9 @@ async function findPageWs(port) {
194
217
  if (pg?.webSocketDebuggerUrl)
195
218
  return pg.webSocketDebuggerUrl;
196
219
  }
197
- catch { /* retry */ }
220
+ catch {
221
+ /* retry */
222
+ }
198
223
  await new Promise((r) => setTimeout(r, TARGET_RETRY_MS));
199
224
  }
200
225
  throw new Error("No page target found in browser");
@@ -212,7 +237,9 @@ async function extractSession(cdp) {
212
237
  return { session_id: sid.value, state: st?.value ?? "" };
213
238
  }
214
239
  }
215
- catch { /* domain not enabled or page navigating */ }
240
+ catch {
241
+ /* domain not enabled or page navigating */
242
+ }
216
243
  // 2) JS document.cookie (non-HttpOnly)
217
244
  try {
218
245
  const { result } = await cdp.send("Runtime.evaluate", { expression: "document.cookie" });
@@ -223,7 +250,9 @@ async function extractSession(cdp) {
223
250
  return { session_id: sm[1].trim(), state: tm?.[1]?.trim() ?? "" };
224
251
  }
225
252
  }
226
- catch { /* context destroyed during navigation */ }
253
+ catch {
254
+ /* context destroyed during navigation */
255
+ }
227
256
  // 3) localStorage / sessionStorage
228
257
  try {
229
258
  const expr = 'JSON.stringify({s:localStorage.getItem("session_id")||localStorage.getItem("sessionId")||sessionStorage.getItem("session_id")||"",t:localStorage.getItem("state")||sessionStorage.getItem("state")||""})';
@@ -232,7 +261,9 @@ async function extractSession(cdp) {
232
261
  if (d.s)
233
262
  return { session_id: d.s, state: d.t ?? "" };
234
263
  }
235
- catch { /* not available */ }
264
+ catch {
265
+ /* not available */
266
+ }
236
267
  return null;
237
268
  }
238
269
  /* ------------------------------------------------------------------ */
@@ -250,19 +281,30 @@ export async function loginViaCDP(browserPath, log = () => { }) {
250
281
  try {
251
282
  cdp?.close();
252
283
  }
253
- catch { /* ignore */ }
284
+ catch {
285
+ /* ignore */
286
+ }
254
287
  if (proc && !proc.killed) {
255
288
  try {
256
289
  proc.kill();
257
290
  }
258
- catch { /* ignore */ }
259
- }
260
- setTimeout(() => { try {
261
- rmSync(tempDir, { recursive: true, force: true });
291
+ catch {
292
+ /* ignore */
293
+ }
262
294
  }
263
- catch { /* ignore */ } }, 500);
295
+ setTimeout(() => {
296
+ try {
297
+ rmSync(tempDir, { recursive: true, force: true });
298
+ }
299
+ catch {
300
+ /* ignore */
301
+ }
302
+ }, 500);
303
+ };
304
+ const onSig = () => {
305
+ cleanup();
306
+ process.exit(130);
264
307
  };
265
- const onSig = () => { cleanup(); process.exit(130); };
266
308
  process.on("SIGINT", onSig);
267
309
  process.on("SIGTERM", onSig);
268
310
  try {
@@ -278,18 +320,25 @@ export async function loginViaCDP(browserPath, log = () => { }) {
278
320
  "--mute-audio",
279
321
  DSERS_LOGIN_URL,
280
322
  ], { stdio: ["ignore", "pipe", "pipe"], detached: false });
281
- proc.on("error", () => { });
323
+ proc.on("error", () => {
324
+ /* handled by waitForPort exit handler */
325
+ });
282
326
  const port = await waitForPort(proc);
283
327
  const wsUrl = await findPageWs(port);
284
328
  cdp = await wsConnect(wsUrl);
285
329
  try {
286
330
  await cdp.send("Network.enable");
287
331
  }
288
- catch { /* optional */ }
332
+ catch {
333
+ /* optional */
334
+ }
289
335
  log("Waiting for DSers login... (close browser or Ctrl+C to cancel)");
290
336
  const result = await new Promise((resolve) => {
291
337
  let exited = false;
292
- proc.on("exit", () => { exited = true; resolve(null); });
338
+ proc.on("exit", () => {
339
+ exited = true;
340
+ resolve(null);
341
+ });
293
342
  const tick = async () => {
294
343
  if (exited || cdp.closed)
295
344
  return resolve(null);
@@ -298,7 +347,9 @@ export async function loginViaCDP(browserPath, log = () => { }) {
298
347
  if (session)
299
348
  return resolve(session);
300
349
  }
301
- catch { /* polling error, retry */ }
350
+ catch {
351
+ /* polling error, retry */
352
+ }
302
353
  setTimeout(tick, POLL_MS);
303
354
  };
304
355
  tick();