jd-selection-mcp 0.1.0__tar.gz
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.
- jd_selection_mcp-0.1.0/PKG-INFO +6 -0
- jd_selection_mcp-0.1.0/README.md +71 -0
- jd_selection_mcp-0.1.0/pyproject.toml +18 -0
- jd_selection_mcp-0.1.0/setup.cfg +4 -0
- jd_selection_mcp-0.1.0/src/jd_selection_mcp/__init__.py +439 -0
- jd_selection_mcp-0.1.0/src/jd_selection_mcp.egg-info/PKG-INFO +6 -0
- jd_selection_mcp-0.1.0/src/jd_selection_mcp.egg-info/SOURCES.txt +9 -0
- jd_selection_mcp-0.1.0/src/jd_selection_mcp.egg-info/dependency_links.txt +1 -0
- jd_selection_mcp-0.1.0/src/jd_selection_mcp.egg-info/entry_points.txt +2 -0
- jd_selection_mcp-0.1.0/src/jd_selection_mcp.egg-info/requires.txt +1 -0
- jd_selection_mcp-0.1.0/src/jd_selection_mcp.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# 京东精选 MCP
|
|
2
|
+
|
|
3
|
+
帮消费者找到京东好货的MCP服务,频道式购物推荐。
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
- **频道好货推荐** - 20+精选频道(9.9包邮、秒杀、数码、超市、母婴等)
|
|
8
|
+
- **猜你喜欢** - 千人千面个性化推荐
|
|
9
|
+
- **类目浏览** - 京东全品类分类导航
|
|
10
|
+
- **促销活动** - 当前京东联盟促销活动
|
|
11
|
+
|
|
12
|
+
## 工具列表
|
|
13
|
+
|
|
14
|
+
| 工具 | 功能 | 接口 |
|
|
15
|
+
|------|------|------|
|
|
16
|
+
| `search_channel` | 频道好货推荐 | jingfen.query |
|
|
17
|
+
| `recommend_goods` | 猜你喜欢/个性化推荐 | material.query |
|
|
18
|
+
| `get_categories` | 商品类目浏览 | category.goods.get |
|
|
19
|
+
| `get_activities` | 促销活动查询 | activity.query |
|
|
20
|
+
|
|
21
|
+
## 频道列表
|
|
22
|
+
|
|
23
|
+
| 频道名 | eliteId | 特点 |
|
|
24
|
+
|--------|---------|------|
|
|
25
|
+
| 好券商品 | 1 | 有最优券商品 |
|
|
26
|
+
| 9.9包邮 | 10 | 低价爆品 |
|
|
27
|
+
| 实时热销 | 22 | 2小时热销 |
|
|
28
|
+
| 数码家电 | 24 | 3C数码 |
|
|
29
|
+
| 超市 | 25 | 日用百货 |
|
|
30
|
+
| 母婴玩具 | 26 | 品类丰富 |
|
|
31
|
+
| 美妆穿搭 | 28 | 美妆服饰 |
|
|
32
|
+
| 京东好物 | 32 | 高佣+好评 |
|
|
33
|
+
| 京东秒杀 | 33 | 限时秒杀 |
|
|
34
|
+
| 高收益榜 | 40 | 佣金>30% |
|
|
35
|
+
| 自营热卖 | 41 | 京东自营 |
|
|
36
|
+
| 自营 | 110 | 全自营商品 |
|
|
37
|
+
|
|
38
|
+
## 安装
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install jd-selection-mcp
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 使用
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
jd-selection-mcp
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
或在MCP客户端配置:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"mcpServers": {
|
|
55
|
+
"jd-selection": {
|
|
56
|
+
"command": "jd-selection-mcp"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 技术架构
|
|
63
|
+
|
|
64
|
+
- 直接调用京东联盟API(宙斯端点),密钥内置
|
|
65
|
+
- MD5签名认证
|
|
66
|
+
- 无需额外代理或配置
|
|
67
|
+
|
|
68
|
+
## 版本规划
|
|
69
|
+
|
|
70
|
+
- v0.1.0: 频道推荐+类目+活动(当前)
|
|
71
|
+
- v0.2.0: +关键词搜索(goods.query) + 转链(bysubunionid.get)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "jd-selection-mcp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "京东精选MCP服务 - 频道式购物推荐,帮消费者找到京东好货"
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"mcp>=1.0.0",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[project.scripts]
|
|
15
|
+
jd-selection-mcp = "jd_selection_mcp:main"
|
|
16
|
+
|
|
17
|
+
[tool.setuptools.packages.find]
|
|
18
|
+
where = ["src"]
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
"""
|
|
2
|
+
京东精选 MCP 服务 - 直接调用京东联盟API
|
|
3
|
+
帮消费者找到京东好货,频道式购物推荐
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import hashlib
|
|
7
|
+
import json
|
|
8
|
+
import time
|
|
9
|
+
import urllib.request
|
|
10
|
+
import urllib.parse
|
|
11
|
+
from typing import Optional
|
|
12
|
+
from mcp.server.fastmcp import FastMCP
|
|
13
|
+
|
|
14
|
+
# 京东联盟凭证(内置)
|
|
15
|
+
APP_KEY = "8b9ebb96e4cc29965db18075a70f2a57"
|
|
16
|
+
APP_SECRET = "4743932a6e434f228edc1873c39ae9db"
|
|
17
|
+
SITE_ID = "4105317833"
|
|
18
|
+
PID = "2018138106_4105317833_3105782773"
|
|
19
|
+
|
|
20
|
+
API_URL = "https://api.jd.com/routerjson"
|
|
21
|
+
|
|
22
|
+
# 频道ID映射
|
|
23
|
+
CHANNEL_MAP = {
|
|
24
|
+
"好券商品": 1,
|
|
25
|
+
"精选卖场": 2,
|
|
26
|
+
"9.9包邮": 10,
|
|
27
|
+
"京东配送": 15,
|
|
28
|
+
"实时热销": 22,
|
|
29
|
+
"为你推荐": 23,
|
|
30
|
+
"数码家电": 24,
|
|
31
|
+
"超市": 25,
|
|
32
|
+
"母婴玩具": 26,
|
|
33
|
+
"家具日用": 27,
|
|
34
|
+
"美妆穿搭": 28,
|
|
35
|
+
"医药保健": 29,
|
|
36
|
+
"图书文具": 30,
|
|
37
|
+
"今日必推": 31,
|
|
38
|
+
"京东好物": 32,
|
|
39
|
+
"京东秒杀": 33,
|
|
40
|
+
"拼购商品": 34,
|
|
41
|
+
"高收益榜": 40,
|
|
42
|
+
"自营热卖": 41,
|
|
43
|
+
"新品首发": 109,
|
|
44
|
+
"自营": 110,
|
|
45
|
+
"京东爆品": 112,
|
|
46
|
+
"首购商品": 125,
|
|
47
|
+
"高佣榜单": 129,
|
|
48
|
+
"视频商品": 130,
|
|
49
|
+
"历史最低价": 153,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# material.query的频道ID(和jingfen不同)
|
|
53
|
+
MATERIAL_CHANNEL_MAP = {
|
|
54
|
+
"猜你喜欢": 1,
|
|
55
|
+
"实时热销": 2,
|
|
56
|
+
"大额券": 3,
|
|
57
|
+
"9.9包邮": 4,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# 排序字段映射
|
|
61
|
+
SORT_NAME_MAP = {
|
|
62
|
+
"价格": "price",
|
|
63
|
+
"佣金比例": "commissionShare",
|
|
64
|
+
"佣金": "commission",
|
|
65
|
+
"销量": "inOrderCount30DaysSku",
|
|
66
|
+
"评论数": "comments",
|
|
67
|
+
"好评数": "goodComments",
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
mcp = FastMCP("jd-selection")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def md5_sign(params: dict, app_secret: str) -> str:
|
|
74
|
+
"""京东联盟API签名: MD5(secret + key1value1key2value2... + secret)"""
|
|
75
|
+
sorted_keys = sorted(params.keys())
|
|
76
|
+
raw = app_secret
|
|
77
|
+
for k in sorted_keys:
|
|
78
|
+
raw += str(k) + str(params[k])
|
|
79
|
+
raw += app_secret
|
|
80
|
+
return hashlib.md5(raw.encode("utf-8")).hexdigest().upper()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def call_jd_api(method: str, biz_params: dict) -> dict:
|
|
84
|
+
"""调用京东联盟API"""
|
|
85
|
+
timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
|
86
|
+
params = {
|
|
87
|
+
"method": method,
|
|
88
|
+
"app_key": APP_KEY,
|
|
89
|
+
"timestamp": timestamp,
|
|
90
|
+
"format": "json",
|
|
91
|
+
"v": "1.0",
|
|
92
|
+
"sign_method": "md5",
|
|
93
|
+
"360buy_param_json": json.dumps(biz_params, ensure_ascii=False, separators=(",", ":")),
|
|
94
|
+
}
|
|
95
|
+
params["sign"] = md5_sign(params, APP_SECRET)
|
|
96
|
+
|
|
97
|
+
# POST请求
|
|
98
|
+
post_data = urllib.parse.urlencode(params).encode("utf-8")
|
|
99
|
+
req = urllib.request.Request(
|
|
100
|
+
API_URL, data=post_data, method="POST",
|
|
101
|
+
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
102
|
+
)
|
|
103
|
+
try:
|
|
104
|
+
with urllib.request.urlopen(req, timeout=30) as resp:
|
|
105
|
+
return json.loads(resp.read().decode("utf-8"))
|
|
106
|
+
except Exception as e:
|
|
107
|
+
return {"error": str(e)}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def extract_biz_data(result: dict, method: str) -> dict:
|
|
111
|
+
"""提取业务数据,兼容response/responce拼写"""
|
|
112
|
+
# 京东的response key有时拼成"responce"
|
|
113
|
+
response_key = method.replace(".", "_") + "_response"
|
|
114
|
+
response_key2 = method.replace(".", "_") + "_responce"
|
|
115
|
+
biz = result.get(response_key) or result.get(response_key2)
|
|
116
|
+
|
|
117
|
+
if not biz:
|
|
118
|
+
if "error_response" in result:
|
|
119
|
+
err = result["error_response"]
|
|
120
|
+
return {"code": -1, "message": f"API错误: {err.get('zh_desc', err.get('en_desc', ''))}"}
|
|
121
|
+
return {"code": -1, "message": f"未知返回格式"}
|
|
122
|
+
|
|
123
|
+
if biz.get("code") != "0":
|
|
124
|
+
return {"code": -1, "message": f"系统错误: {biz.get('errorMessage', '')}"}
|
|
125
|
+
|
|
126
|
+
# 提取业务结果
|
|
127
|
+
qr = biz.get("queryResult") or biz.get("getResult")
|
|
128
|
+
if qr and isinstance(qr, str):
|
|
129
|
+
try:
|
|
130
|
+
qr = json.loads(qr)
|
|
131
|
+
except:
|
|
132
|
+
return {"code": -1, "message": "JSON解析失败"}
|
|
133
|
+
|
|
134
|
+
if isinstance(qr, dict):
|
|
135
|
+
return qr
|
|
136
|
+
|
|
137
|
+
return {"code": -1, "message": "无业务数据"}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def format_goods_list(data: dict, channel_name: str = "", page: int = 1) -> str:
|
|
141
|
+
"""格式化商品列表为可读文本(不暴露佣金字段)"""
|
|
142
|
+
if data.get("code") == -1:
|
|
143
|
+
return f"查询失败:{data.get('message', '未知错误')}"
|
|
144
|
+
|
|
145
|
+
code = data.get("code")
|
|
146
|
+
if code == 403:
|
|
147
|
+
return "该接口暂无访问权限,请在京东联盟后台申请权限。"
|
|
148
|
+
if code == 400:
|
|
149
|
+
return "参数错误,请检查输入。"
|
|
150
|
+
if code != 200:
|
|
151
|
+
return f"查询失败(code={code}): {data.get('message', '')}"
|
|
152
|
+
|
|
153
|
+
items = data.get("data", [])
|
|
154
|
+
total = data.get("totalCount", 0)
|
|
155
|
+
|
|
156
|
+
if not items:
|
|
157
|
+
return "暂无商品数据。"
|
|
158
|
+
|
|
159
|
+
header = f"共 {total} 件好货(第{page}页)"
|
|
160
|
+
if channel_name:
|
|
161
|
+
header = f"【{channel_name}】{header}"
|
|
162
|
+
|
|
163
|
+
lines = [header + "\n"]
|
|
164
|
+
|
|
165
|
+
for i, item in enumerate(items[:10], 1):
|
|
166
|
+
# 基本信息
|
|
167
|
+
sku_name = item.get("skuName", "")
|
|
168
|
+
price_info = item.get("priceInfo", {})
|
|
169
|
+
coupon_info = item.get("couponInfo", {})
|
|
170
|
+
shop_info = item.get("shopInfo", {})
|
|
171
|
+
category_info = item.get("categoryInfo", {})
|
|
172
|
+
image_info = item.get("imageInfo", {})
|
|
173
|
+
is_hot = item.get("isHot", 0)
|
|
174
|
+
owner = item.get("owner", "")
|
|
175
|
+
|
|
176
|
+
# 自营标识
|
|
177
|
+
self_tag = "京东自营" if owner == "g" or (shop_info and shop_info.get("shopName", "").endswith("自营旗舰店")) else ""
|
|
178
|
+
|
|
179
|
+
# 价格
|
|
180
|
+
lowest_price = price_info.get("lowestPrice", "")
|
|
181
|
+
coupon_price = price_info.get("lowestCouponPrice", "")
|
|
182
|
+
if coupon_price and coupon_price != lowest_price:
|
|
183
|
+
price_str = f"到手¥{coupon_price}(原价¥{lowest_price})"
|
|
184
|
+
elif lowest_price:
|
|
185
|
+
price_str = f"¥{lowest_price}"
|
|
186
|
+
else:
|
|
187
|
+
price_str = "价格未知"
|
|
188
|
+
|
|
189
|
+
# 最优优惠券
|
|
190
|
+
best_coupon = None
|
|
191
|
+
coupon_list = coupon_info.get("couponList", []) if coupon_info else []
|
|
192
|
+
for c in coupon_list:
|
|
193
|
+
if c.get("isBest") == 1:
|
|
194
|
+
best_coupon = c
|
|
195
|
+
break
|
|
196
|
+
coupon_str = ""
|
|
197
|
+
if best_coupon:
|
|
198
|
+
discount = best_coupon.get("discount", 0)
|
|
199
|
+
quota = best_coupon.get("quota", 0)
|
|
200
|
+
if discount:
|
|
201
|
+
coupon_str = f" | 券¥{discount}" + (f"(满{quota}可用)" if quota else "")
|
|
202
|
+
|
|
203
|
+
# 销量
|
|
204
|
+
sales = item.get("inOrderCount30Days", 0) or item.get("inOrderCount30DaysSku", 0)
|
|
205
|
+
sales_str = f" | 月销{sales}" if sales else ""
|
|
206
|
+
|
|
207
|
+
# 好评率
|
|
208
|
+
good_rate = item.get("goodCommentsShare", 0)
|
|
209
|
+
rate_str = f" | 好评{good_rate}%" if good_rate else ""
|
|
210
|
+
|
|
211
|
+
# 评论数
|
|
212
|
+
comments = item.get("comments", 0)
|
|
213
|
+
comments_str = f" | {comments}条评论" if comments else ""
|
|
214
|
+
|
|
215
|
+
# 店铺
|
|
216
|
+
shop_name = shop_info.get("shopName", "") if shop_info else ""
|
|
217
|
+
|
|
218
|
+
# 类目
|
|
219
|
+
cat = category_info.get("cid3Name", "") if category_info else ""
|
|
220
|
+
|
|
221
|
+
# 商品链接
|
|
222
|
+
material_url = item.get("materialUrl", "")
|
|
223
|
+
if material_url and not material_url.startswith("http"):
|
|
224
|
+
material_url = f"https://{material_url}"
|
|
225
|
+
|
|
226
|
+
# 热门标识
|
|
227
|
+
hot_tag = " 🔥" if is_hot else ""
|
|
228
|
+
|
|
229
|
+
line = f"{i}. {'[' + self_tag + '] ' if self_tag else ''}{sku_name}{hot_tag}\n {price_str}{coupon_str}{sales_str}{rate_str}"
|
|
230
|
+
if shop_name:
|
|
231
|
+
line += f" | {shop_name}"
|
|
232
|
+
if cat:
|
|
233
|
+
line += f" | {cat}"
|
|
234
|
+
if material_url:
|
|
235
|
+
line += f"\n 🔗{material_url}"
|
|
236
|
+
lines.append(line)
|
|
237
|
+
|
|
238
|
+
return "\n\n".join(lines)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def resolve_channel(channel: str) -> int:
|
|
242
|
+
"""解析频道名或ID为eliteId"""
|
|
243
|
+
# 先尝试直接匹配频道名
|
|
244
|
+
if channel in CHANNEL_MAP:
|
|
245
|
+
return CHANNEL_MAP[channel]
|
|
246
|
+
# 尝试数字ID
|
|
247
|
+
try:
|
|
248
|
+
elite_id = int(channel)
|
|
249
|
+
return elite_id
|
|
250
|
+
except:
|
|
251
|
+
pass
|
|
252
|
+
# 模糊匹配
|
|
253
|
+
for name, eid in CHANNEL_MAP.items():
|
|
254
|
+
if channel in name or name in channel:
|
|
255
|
+
return eid
|
|
256
|
+
return None
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@mcp.tool()
|
|
260
|
+
def search_channel(channel: str = "好券商品", sort: str = None,
|
|
261
|
+
has_coupon: bool = None, page: int = 1) -> str:
|
|
262
|
+
"""
|
|
263
|
+
京东频道好货推荐 - 按频道浏览京东精选商品,无需关键词
|
|
264
|
+
|
|
265
|
+
这是京东最核心的购物推荐能力,涵盖20+精选频道,由京东联盟运营团队维护商品池。
|
|
266
|
+
不知道买什么的时候,用这个工具逛一逛!
|
|
267
|
+
|
|
268
|
+
可选频道:
|
|
269
|
+
- 低价好货:9.9包邮、京东秒杀、历史最低价
|
|
270
|
+
- 热门榜单:实时热销、自营热卖、高收益榜、高佣榜单
|
|
271
|
+
- 品类精选:数码家电、超市、母婴玩具、美妆穿搭、家具日用、医药保健、图书文具
|
|
272
|
+
- 优惠推荐:好券商品、今日必推、京东好物、大额券
|
|
273
|
+
- 特色频道:自营、新品首发、京东爆品、视频商品
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
channel: 频道名称(如"9.9包邮"、"数码家电"、"实时热销")或数字ID,默认"好券商品"
|
|
277
|
+
sort: 排序方式(可选):价格、佣金比例、销量、评论数
|
|
278
|
+
has_coupon: 是否只看有券商品,默认不限
|
|
279
|
+
page: 页码,默认1
|
|
280
|
+
"""
|
|
281
|
+
elite_id = resolve_channel(channel)
|
|
282
|
+
if elite_id is None:
|
|
283
|
+
available = "、".join(list(CHANNEL_MAP.keys())[:15])
|
|
284
|
+
return f"未找到频道「{channel}」,可选频道:{available}等"
|
|
285
|
+
|
|
286
|
+
# 构建参数
|
|
287
|
+
params = {
|
|
288
|
+
"eliteId": elite_id,
|
|
289
|
+
"pageIndex": page,
|
|
290
|
+
"pageSize": 10,
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
# 排序
|
|
294
|
+
if sort and sort in SORT_NAME_MAP:
|
|
295
|
+
params["sortName"] = SORT_NAME_MAP[sort]
|
|
296
|
+
params["sort"] = "desc"
|
|
297
|
+
|
|
298
|
+
# 只看有券
|
|
299
|
+
if has_coupon:
|
|
300
|
+
params["hasBestCoupon"] = 1
|
|
301
|
+
|
|
302
|
+
# 查找频道名
|
|
303
|
+
channel_name = channel
|
|
304
|
+
for name, eid in CHANNEL_MAP.items():
|
|
305
|
+
if eid == elite_id:
|
|
306
|
+
channel_name = name
|
|
307
|
+
break
|
|
308
|
+
|
|
309
|
+
result = call_jd_api("jd.union.open.goods.jingfen.query", {"goodsReq": params})
|
|
310
|
+
data = extract_biz_data(result, "jd.union.open.goods.jingfen.query")
|
|
311
|
+
return format_goods_list(data, channel_name, page)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
@mcp.tool()
|
|
315
|
+
def recommend_goods(channel: str = "猜你喜欢", page: int = 1) -> str:
|
|
316
|
+
"""
|
|
317
|
+
京东猜你喜欢/个性化推荐 - 获取千人千面的商品推荐
|
|
318
|
+
|
|
319
|
+
适合不知道买什么、想随便看看的场景。通过不同频道获取个性化推荐结果。
|
|
320
|
+
|
|
321
|
+
可选频道:
|
|
322
|
+
- 猜你喜欢:个性化推荐
|
|
323
|
+
- 实时热销:当前热卖商品
|
|
324
|
+
- 大额券:大额优惠券商品
|
|
325
|
+
- 9.9包邮:低价包邮好货
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
channel: 频道名称(猜你喜欢/实时热销/大额券/9.9包邮),默认"猜你喜欢"
|
|
329
|
+
page: 页码,默认1
|
|
330
|
+
"""
|
|
331
|
+
# 解析material频道ID
|
|
332
|
+
elite_id = MATERIAL_CHANNEL_MAP.get(channel)
|
|
333
|
+
if elite_id is None:
|
|
334
|
+
# 尝试数字
|
|
335
|
+
try:
|
|
336
|
+
elite_id = int(channel)
|
|
337
|
+
except:
|
|
338
|
+
available = "、".join(MATERIAL_CHANNEL_MAP.keys())
|
|
339
|
+
return f"未找到频道「{channel}」,可选:{available}"
|
|
340
|
+
|
|
341
|
+
params = {
|
|
342
|
+
"eliteId": elite_id,
|
|
343
|
+
"pageIndex": page,
|
|
344
|
+
"pageSize": 10,
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
result = call_jd_api("jd.union.open.goods.material.query", {"goodsReq": params})
|
|
348
|
+
data = extract_biz_data(result, "jd.union.open.goods.material.query")
|
|
349
|
+
return format_goods_list(data, channel, page)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
@mcp.tool()
|
|
353
|
+
def get_categories(parent_id: int = 0) -> str:
|
|
354
|
+
"""
|
|
355
|
+
京东商品类目浏览 - 查看京东商品分类,可逐级浏览
|
|
356
|
+
|
|
357
|
+
传入0查看所有一级类目,传入一级类目ID查看其子类目,以此类推。
|
|
358
|
+
获取类目后可用类目信息配合频道推荐做精准筛选。
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
parent_id: 父类目ID,0表示查询一级类目,默认0
|
|
362
|
+
"""
|
|
363
|
+
params = {"parentId": parent_id, "grade": 0}
|
|
364
|
+
if parent_id != 0:
|
|
365
|
+
params["grade"] = 1 # 非根节点查子类目
|
|
366
|
+
|
|
367
|
+
result = call_jd_api("jd.union.open.category.goods.get", {"req": params})
|
|
368
|
+
data = extract_biz_data(result, "jd.union.open.category.goods.get")
|
|
369
|
+
|
|
370
|
+
if data.get("code") != 200:
|
|
371
|
+
return f"查询失败:{data.get('message', '未知错误')}"
|
|
372
|
+
|
|
373
|
+
categories = data.get("data", [])
|
|
374
|
+
if not categories:
|
|
375
|
+
return "未找到子类目。"
|
|
376
|
+
|
|
377
|
+
lines = [f"共 {len(categories)} 个类目:\n"]
|
|
378
|
+
for cat in categories:
|
|
379
|
+
cat_id = cat.get("id", "")
|
|
380
|
+
name = cat.get("name", "")
|
|
381
|
+
grade = cat.get("grade", 0)
|
|
382
|
+
pid = cat.get("parentId", 0)
|
|
383
|
+
indent = " " * grade
|
|
384
|
+
lines.append(f"{indent}- {name} (ID:{cat_id})")
|
|
385
|
+
|
|
386
|
+
return "\n".join(lines)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
@mcp.tool()
|
|
390
|
+
def get_activities() -> str:
|
|
391
|
+
"""
|
|
392
|
+
京东促销活动查询 - 查看当前京东联盟的促销活动
|
|
393
|
+
|
|
394
|
+
返回正在进行或即将开始的促销活动,包括活动名称、时间和活动页面链接。
|
|
395
|
+
适合推荐给用户参与限时优惠。
|
|
396
|
+
"""
|
|
397
|
+
result = call_jd_api("jd.union.open.activity.query", {"activityReq": {}})
|
|
398
|
+
data = extract_biz_data(result, "jd.union.open.activity.query")
|
|
399
|
+
|
|
400
|
+
if data.get("code") != 200:
|
|
401
|
+
return f"查询失败:{data.get('message', '未知错误')}"
|
|
402
|
+
|
|
403
|
+
activities = data.get("data", [])
|
|
404
|
+
if not activities:
|
|
405
|
+
return "暂无促销活动。"
|
|
406
|
+
|
|
407
|
+
# 只展示前15个活跃活动
|
|
408
|
+
lines = [f"共 {len(activities)} 个活动,展示前15个:\n"]
|
|
409
|
+
for i, act in enumerate(activities[:15], 1):
|
|
410
|
+
name = act.get("name", "") or act.get("title", "")
|
|
411
|
+
status = act.get("actStatus", 0)
|
|
412
|
+
# status: 1=未开始, 2=进行中, 3=已结束
|
|
413
|
+
status_str = {1: "未开始", 2: "进行中", 3: "已结束"}.get(status, "未知")
|
|
414
|
+
advantage = act.get("advantage", "")
|
|
415
|
+
end_time = act.get("endTime", 0)
|
|
416
|
+
|
|
417
|
+
time_str = ""
|
|
418
|
+
if end_time:
|
|
419
|
+
try:
|
|
420
|
+
time_str = time.strftime("%Y-%m-%d", time.localtime(end_time / 1000))
|
|
421
|
+
except:
|
|
422
|
+
pass
|
|
423
|
+
|
|
424
|
+
line = f"{i}. {name} [{status_str}]"
|
|
425
|
+
if advantage:
|
|
426
|
+
line += f" | {advantage}"
|
|
427
|
+
if time_str:
|
|
428
|
+
line += f" | 截止{time_str}"
|
|
429
|
+
lines.append(line)
|
|
430
|
+
|
|
431
|
+
return "\n".join(lines)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def main():
|
|
435
|
+
mcp.run()
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
if __name__ == "__main__":
|
|
439
|
+
main()
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/jd_selection_mcp/__init__.py
|
|
4
|
+
src/jd_selection_mcp.egg-info/PKG-INFO
|
|
5
|
+
src/jd_selection_mcp.egg-info/SOURCES.txt
|
|
6
|
+
src/jd_selection_mcp.egg-info/dependency_links.txt
|
|
7
|
+
src/jd_selection_mcp.egg-info/entry_points.txt
|
|
8
|
+
src/jd_selection_mcp.egg-info/requires.txt
|
|
9
|
+
src/jd_selection_mcp.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mcp>=1.0.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jd_selection_mcp
|