yuqing-fullsearch-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/README.md +64 -0
- package/package.json +32 -0
- package/server.js +411 -0
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# yuqing-fullsearch-mcp
|
|
2
|
+
|
|
3
|
+
国内舆情全文检索 MCP Server,供 Cursor、Kilo Code 等支持 MCP 的客户端调用。
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
- **fullsearch_information_list**:全文检索舆情,支持 keyword、时间范围、行业/事件/省份/城市等筛选。
|
|
8
|
+
- **yuqing_industry_list**:获取行业列表,用于结果过多时按行业二次筛选。
|
|
9
|
+
- **yuqing_event_list**:获取事件列表,用于结果过多时按事件二次筛选。
|
|
10
|
+
|
|
11
|
+
## 安装
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g yuqing-fullsearch-mcp
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
或使用 npx 直接运行(无需全局安装):
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx yuqing-fullsearch-mcp
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 在 MCP 客户端中配置
|
|
24
|
+
|
|
25
|
+
### Cursor / Kilo Code(mcp.json)
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"yuqing-fullsearch": {
|
|
31
|
+
"command": "npx",
|
|
32
|
+
"args": ["-y", "yuqing-fullsearch-mcp"],
|
|
33
|
+
"env": {},
|
|
34
|
+
"alwaysAllow": ["*", "fullsearch_information_list", "yuqing_industry_list", "yuqing_event_list"]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
若使用本地项目路径方式(不发布到 npm 时):
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"yuqing-fullsearch": {
|
|
45
|
+
"command": "npm",
|
|
46
|
+
"args": ["run", "start", "--prefix", "path/to/yuqing-mcp"],
|
|
47
|
+
"alwaysAllow": ["*"]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 接口说明
|
|
53
|
+
|
|
54
|
+
- 检索接口基础地址:`https://stonedtapi-w.bu3skq6y.hqxin.net`
|
|
55
|
+
- **keyword** 中不要包含省份、城市,省份填 `province`,城市填 `city`。
|
|
56
|
+
- 调用 **fullsearch_information_list** 时建议适当加大 `page_size`(如 30),写入采集文档前请先筛选有用内容。
|
|
57
|
+
|
|
58
|
+
## 要求
|
|
59
|
+
|
|
60
|
+
- Node.js >= 18
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "yuqing-fullsearch-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for 国内舆情全文检索:fullsearch_information_list、yuqing_industry_list、yuqing_event_list",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "server.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"yuqing-fullsearch-mcp": "server.js",
|
|
9
|
+
"yuqing-mcp": "server.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"start": "node server.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"model-context-protocol",
|
|
17
|
+
"yuqing",
|
|
18
|
+
"舆情",
|
|
19
|
+
"全文检索",
|
|
20
|
+
"fullsearch"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"server.js"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/server.js
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 国内舆情全文检索 MCP Server (Node.js)
|
|
4
|
+
* 暴露 fullsearch_information_list 工具
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Server } from "@modelcontextprotocol/sdk/server";
|
|
8
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
+
import {
|
|
10
|
+
ListToolsRequestSchema,
|
|
11
|
+
CallToolRequestSchema,
|
|
12
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
13
|
+
|
|
14
|
+
const BASE_URL = "https://stonedtapi-w.bu3skq6y.hqxin.net";
|
|
15
|
+
|
|
16
|
+
async function fetchApi(path, options = {}) {
|
|
17
|
+
const url = `${BASE_URL}${path}`;
|
|
18
|
+
const { method = "GET", body } = options;
|
|
19
|
+
try {
|
|
20
|
+
const init = {
|
|
21
|
+
method,
|
|
22
|
+
headers: {
|
|
23
|
+
"User-Agent":
|
|
24
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
25
|
+
},
|
|
26
|
+
signal: AbortSignal.timeout(15000),
|
|
27
|
+
};
|
|
28
|
+
if (body !== undefined && (method === "POST" || method === "PUT")) {
|
|
29
|
+
init.headers["Content-Type"] = "application/json";
|
|
30
|
+
init.body = typeof body === "string" ? body : JSON.stringify(body || {});
|
|
31
|
+
}
|
|
32
|
+
const res = await fetch(url, init);
|
|
33
|
+
const text = await res.text();
|
|
34
|
+
if (!text || !text.trim()) return { code: res.status, message: "Empty response", data: null };
|
|
35
|
+
const first = text.trim()[0];
|
|
36
|
+
if (first === "{" || first === "[") {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(text);
|
|
39
|
+
} catch (_) {
|
|
40
|
+
return { code: -1, message: "Invalid JSON", data: null };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { code: res.status, message: "Response is not JSON (e.g. HTML)", data: null };
|
|
44
|
+
} catch (e) {
|
|
45
|
+
return { code: -1, message: e.message, data: null };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** 获取行业列表。BASE_URL + /industry,POST,请求体格式以实际接口为准 */
|
|
50
|
+
async function getIndustryList() {
|
|
51
|
+
const body = {
|
|
52
|
+
times: "",
|
|
53
|
+
timee: "",
|
|
54
|
+
page: 1,
|
|
55
|
+
searchType: 1,
|
|
56
|
+
similar: 0,
|
|
57
|
+
matchingmode: 1,
|
|
58
|
+
emotionalIndex: [1, 2, 3],
|
|
59
|
+
province: [],
|
|
60
|
+
city: [],
|
|
61
|
+
eventIndex: [],
|
|
62
|
+
industryIndex: [],
|
|
63
|
+
searchword: "",
|
|
64
|
+
timeType: 4,
|
|
65
|
+
organizationtype: ["0"],
|
|
66
|
+
categorylabledata: ["0"],
|
|
67
|
+
enterprisetypelist: ["0"],
|
|
68
|
+
hightechtypelist: ["0"],
|
|
69
|
+
policylableflag: ["0"],
|
|
70
|
+
classify: [],
|
|
71
|
+
};
|
|
72
|
+
return fetchApi("/industry", { method: "POST", body });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** 获取事件列表。BASE_URL + /getevent,POST,请求体格式以实际接口为准 */
|
|
76
|
+
async function getEventList() {
|
|
77
|
+
const body = {
|
|
78
|
+
times: "",
|
|
79
|
+
timee: "",
|
|
80
|
+
page: 1,
|
|
81
|
+
searchType: 1,
|
|
82
|
+
similar: 0,
|
|
83
|
+
matchingmode: 1,
|
|
84
|
+
emotionalIndex: [1, 2, 3],
|
|
85
|
+
province: [],
|
|
86
|
+
city: [],
|
|
87
|
+
eventIndex: [],
|
|
88
|
+
industryIndex: [],
|
|
89
|
+
searchword: "",
|
|
90
|
+
timeType: 4,
|
|
91
|
+
organizationtype: ["0"],
|
|
92
|
+
categorylabledata: ["0"],
|
|
93
|
+
enterprisetypelist: ["0"],
|
|
94
|
+
hightechtypelist: ["0"],
|
|
95
|
+
policylableflag: ["0"],
|
|
96
|
+
classify: [],
|
|
97
|
+
};
|
|
98
|
+
return fetchApi("/getevent", { method: "POST", body });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** 将逗号分隔或数组转为数组,过滤空串 */
|
|
102
|
+
function toList(val) {
|
|
103
|
+
if (val == null || val === "") return [];
|
|
104
|
+
if (Array.isArray(val)) return val.map(String).filter(Boolean);
|
|
105
|
+
return String(val).split(/[,,]/).map((s) => s.trim()).filter(Boolean);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function searchYuqing(options) {
|
|
109
|
+
const {
|
|
110
|
+
keyword,
|
|
111
|
+
timeType = 5,
|
|
112
|
+
pageNum = 1,
|
|
113
|
+
pageSize = 20,
|
|
114
|
+
mergeType = 1,
|
|
115
|
+
sortType = 1,
|
|
116
|
+
matchType = 1,
|
|
117
|
+
emotionalIndex = [],
|
|
118
|
+
industryIndex = [],
|
|
119
|
+
eventIndex = [],
|
|
120
|
+
province = [],
|
|
121
|
+
city = [],
|
|
122
|
+
organization = [],
|
|
123
|
+
categorylable = [],
|
|
124
|
+
enterprisetype = [],
|
|
125
|
+
hightechtype = [],
|
|
126
|
+
policylableflag = [],
|
|
127
|
+
} = options;
|
|
128
|
+
const industry = toList(industryIndex);
|
|
129
|
+
const event = toList(eventIndex);
|
|
130
|
+
const prov = toList(province);
|
|
131
|
+
const cityList = toList(city);
|
|
132
|
+
const org = toList(organization);
|
|
133
|
+
const category = toList(categorylable);
|
|
134
|
+
const enterprise = toList(enterprisetype);
|
|
135
|
+
const hightech = toList(hightechtype);
|
|
136
|
+
const policy = toList(policylableflag);
|
|
137
|
+
const emotional = toList(emotionalIndex);
|
|
138
|
+
|
|
139
|
+
const params = new URLSearchParams();
|
|
140
|
+
params.set("searchWord", String(keyword));
|
|
141
|
+
params.set("timeType", String(timeType));
|
|
142
|
+
params.set("pageNum", String(pageNum));
|
|
143
|
+
params.set("pageSize", String(pageSize));
|
|
144
|
+
params.set("matchType", String(matchType >= 1 && matchType <= 3 ? matchType : 1));
|
|
145
|
+
params.set("sortType", String(sortType >= 1 && sortType <= 3 ? sortType : 1));
|
|
146
|
+
params.set("mergeType", String(mergeType === 1 ? 1 : 0));
|
|
147
|
+
emotional.forEach((v) => params.append("emotionalIndex", v));
|
|
148
|
+
industry.forEach((v) => params.append("industryIndex", v));
|
|
149
|
+
event.forEach((v) => params.append("eventIndex", v));
|
|
150
|
+
prov.forEach((v) => params.append("province", v));
|
|
151
|
+
cityList.forEach((v) => params.append("city", v));
|
|
152
|
+
org.forEach((v) => params.append("organization", v));
|
|
153
|
+
category.forEach((v) => params.append("categorylable", v));
|
|
154
|
+
enterprise.forEach((v) => params.append("enterprisetype", v));
|
|
155
|
+
hightech.forEach((v) => params.append("hightechtype", v));
|
|
156
|
+
policy.forEach((v) => params.append("policylableflag", v));
|
|
157
|
+
|
|
158
|
+
const url = `${BASE_URL}/fullsearch/informationList?${params}`;
|
|
159
|
+
try {
|
|
160
|
+
const res = await fetch(url, {
|
|
161
|
+
headers: {
|
|
162
|
+
"User-Agent":
|
|
163
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
164
|
+
},
|
|
165
|
+
signal: AbortSignal.timeout(30000),
|
|
166
|
+
});
|
|
167
|
+
const data = await res.json();
|
|
168
|
+
return data;
|
|
169
|
+
} catch (e) {
|
|
170
|
+
return { code: -1, message: e.message, data: null };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function formatEmotional(index) {
|
|
175
|
+
const m = { 1: "正面", 2: "中性", 3: "负面" };
|
|
176
|
+
return m[index] ?? "未知";
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function stripHtml(html) {
|
|
180
|
+
if (typeof html !== "string") return "";
|
|
181
|
+
return html.replace(/<[^>]+>/g, "");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const server = new Server(
|
|
185
|
+
{
|
|
186
|
+
name: "yuqing-fullsearch",
|
|
187
|
+
version: "1.0.0",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
capabilities: { tools: {} },
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
195
|
+
return {
|
|
196
|
+
tools: [
|
|
197
|
+
{
|
|
198
|
+
name: "yuqing_industry_list",
|
|
199
|
+
description: "获取国内舆情检索可用的行业列表,用于 fullsearch_information_list 的 industry_index 参数。无需入参。",
|
|
200
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: "yuqing_event_list",
|
|
204
|
+
description: "获取国内舆情检索可用的事件列表,用于 fullsearch_information_list 的 event_index 参数。无需入参。",
|
|
205
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "fullsearch_information_list",
|
|
209
|
+
description:
|
|
210
|
+
"国内舆情检索,`keyword`中禁止涉及省份、城市内容。例如:`四川地震`,`四川`设置到`province`中,`地震`才作为`keyword`.",
|
|
211
|
+
inputSchema: {
|
|
212
|
+
type: "object",
|
|
213
|
+
properties: {
|
|
214
|
+
keyword: {
|
|
215
|
+
type: "string",
|
|
216
|
+
description: "【必填】检索关键词,禁止使用city和province内容",
|
|
217
|
+
},
|
|
218
|
+
time_type: {
|
|
219
|
+
type: "integer",
|
|
220
|
+
default: 5,
|
|
221
|
+
description: "【必填】时间范围:1=24h,2=一年,3=昨天,4=3天,5=最近7天(常用),6=15天,7=30天,8=自定义",
|
|
222
|
+
},
|
|
223
|
+
page_num: {
|
|
224
|
+
type: "integer",
|
|
225
|
+
default: 1,
|
|
226
|
+
description: "【必填】页码,要更多结果时递增",
|
|
227
|
+
},
|
|
228
|
+
page_size: {
|
|
229
|
+
type: "integer",
|
|
230
|
+
default: 20,
|
|
231
|
+
description: "【必填】每页条数,可填 20 或 30",
|
|
232
|
+
},
|
|
233
|
+
merge_type: {
|
|
234
|
+
type: "integer",
|
|
235
|
+
default: 1,
|
|
236
|
+
description: "【必填】相似文章:0=取消合并,1=合并文章。按需选,默认1",
|
|
237
|
+
},
|
|
238
|
+
sort_type: {
|
|
239
|
+
type: "integer",
|
|
240
|
+
default: 1,
|
|
241
|
+
description: "【必填】信息排序:1=时间降序,2=时间升序,3=相似数倒序。按需选,默认1",
|
|
242
|
+
},
|
|
243
|
+
match_type: {
|
|
244
|
+
type: "integer",
|
|
245
|
+
default: 1,
|
|
246
|
+
description: "【必填】匹配方式:1=全文匹配,2=标题匹配,3=正文匹配。按需选,默认1",
|
|
247
|
+
},
|
|
248
|
+
emotional_index: {
|
|
249
|
+
type: "string",
|
|
250
|
+
description: "【必填】情感属性,多选用逗号分隔。1=正面,2=中性,3=负面。不传则不限情感;传如 1,2,3 则只查这些",
|
|
251
|
+
},
|
|
252
|
+
industry_index: {
|
|
253
|
+
type: "string",
|
|
254
|
+
description: "【必填】涉及行业,多选用逗号分隔。例:旅游行业,互联网,文化传媒。不需要筛选时不填",
|
|
255
|
+
},
|
|
256
|
+
event_index: {
|
|
257
|
+
type: "string",
|
|
258
|
+
description: "【必填】涉及事件,多选用逗号分隔。例:历史事件,突发事件,行业研究。不需要时不填",
|
|
259
|
+
},
|
|
260
|
+
province: {
|
|
261
|
+
type: "string",
|
|
262
|
+
description: "【必填】涉及省份,多选用逗号分隔。例:河南,北京,广东。不需要时不填",
|
|
263
|
+
},
|
|
264
|
+
city: {
|
|
265
|
+
type: "string",
|
|
266
|
+
description: "【必填】涉及城市,多选用逗号分隔。例:上海,北京,武汉。不需要时不填",
|
|
267
|
+
},
|
|
268
|
+
organization: {
|
|
269
|
+
type: "string",
|
|
270
|
+
description: "【必填】涉及机构,多选用逗号分隔。不需要时不填",
|
|
271
|
+
},
|
|
272
|
+
categorylable: {
|
|
273
|
+
type: "string",
|
|
274
|
+
description: "【必填】文章分类,多选用逗号分隔。不需要时不填",
|
|
275
|
+
},
|
|
276
|
+
enterprisetype: {
|
|
277
|
+
type: "string",
|
|
278
|
+
description: "【必填】涉及企业,多选用逗号分隔。不需要时不填",
|
|
279
|
+
},
|
|
280
|
+
hightechtype: {
|
|
281
|
+
type: "string",
|
|
282
|
+
description: "【必填】涉及高新企业,多选用逗号分隔。不需要时不填",
|
|
283
|
+
},
|
|
284
|
+
policylableflag: {
|
|
285
|
+
type: "string",
|
|
286
|
+
description: "【必填】涉及政策,多选用逗号分隔。不需要时不填",
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
required: ["keyword","time_type","page_num","page_size","merge_type","sort_type","match_type","emotional_index","industry_index","event_index","province","city","organization","categorylable","enterprisetype","hightechtype","policylableflag","keyword"],
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
};
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
297
|
+
const toolName = request.params.name;
|
|
298
|
+
|
|
299
|
+
if (toolName === "yuqing_industry_list") {
|
|
300
|
+
const result = await getIndustryList();
|
|
301
|
+
const text =
|
|
302
|
+
result.code === 200
|
|
303
|
+
? JSON.stringify(result, null, 2)
|
|
304
|
+
: JSON.stringify({ error: result.message || "请求失败", code: result.code }, null, 2);
|
|
305
|
+
return { content: [{ type: "text", text }] };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (toolName === "yuqing_event_list") {
|
|
309
|
+
const result = await getEventList();
|
|
310
|
+
const text =
|
|
311
|
+
result.code === 200
|
|
312
|
+
? JSON.stringify(result, null, 2)
|
|
313
|
+
: JSON.stringify({ error: result.message || "请求失败", code: result.code }, null, 2);
|
|
314
|
+
return { content: [{ type: "text", text }] };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (toolName !== "fullsearch_information_list") {
|
|
318
|
+
return {
|
|
319
|
+
content: [
|
|
320
|
+
{
|
|
321
|
+
type: "text",
|
|
322
|
+
text: JSON.stringify({
|
|
323
|
+
error: "Unknown tool: " + toolName,
|
|
324
|
+
}),
|
|
325
|
+
},
|
|
326
|
+
],
|
|
327
|
+
isError: true,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const args = request.params.arguments ?? {};
|
|
332
|
+
const keyword = args.keyword ?? "";
|
|
333
|
+
const timeType = Number(args.time_type) || 5;
|
|
334
|
+
const pageNum = Number(args.page_num) || 1;
|
|
335
|
+
const pageSize = Number(args.page_size) || 20;
|
|
336
|
+
const mergeType = Number(args.merge_type) === 0 ? 0 : 1;
|
|
337
|
+
const sortType = Number(args.sort_type) || 1;
|
|
338
|
+
const matchType = Number(args.match_type) || 1;
|
|
339
|
+
const emotionalIndex = args.emotional_index;
|
|
340
|
+
const industryIndex = args.industry_index;
|
|
341
|
+
const eventIndex = args.event_index;
|
|
342
|
+
const province = args.province;
|
|
343
|
+
const city = args.city;
|
|
344
|
+
const organization = args.organization;
|
|
345
|
+
const categorylable = args.categorylable;
|
|
346
|
+
const enterprisetype = args.enterprisetype;
|
|
347
|
+
const hightechtype = args.hightechtype;
|
|
348
|
+
const policylableflag = args.policylableflag;
|
|
349
|
+
|
|
350
|
+
const result = await searchYuqing({
|
|
351
|
+
keyword,
|
|
352
|
+
timeType,
|
|
353
|
+
pageNum,
|
|
354
|
+
pageSize,
|
|
355
|
+
mergeType,
|
|
356
|
+
sortType,
|
|
357
|
+
matchType,
|
|
358
|
+
emotionalIndex,
|
|
359
|
+
industryIndex,
|
|
360
|
+
eventIndex,
|
|
361
|
+
province,
|
|
362
|
+
city,
|
|
363
|
+
organization,
|
|
364
|
+
categorylable,
|
|
365
|
+
enterprisetype,
|
|
366
|
+
hightechtype,
|
|
367
|
+
policylableflag,
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
if (result.code !== 200) {
|
|
371
|
+
return {
|
|
372
|
+
content: [
|
|
373
|
+
{
|
|
374
|
+
type: "text",
|
|
375
|
+
text: JSON.stringify(
|
|
376
|
+
{ error: result.message || "请求失败", code: result.code },
|
|
377
|
+
null,
|
|
378
|
+
2
|
|
379
|
+
),
|
|
380
|
+
},
|
|
381
|
+
],
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const data = result.data || {};
|
|
386
|
+
const items = data.data || [];
|
|
387
|
+
const out = items.map((item, i) => ({
|
|
388
|
+
序号: i + 1,
|
|
389
|
+
标题: item.title ?? "",
|
|
390
|
+
来源: item.source_name ?? "",
|
|
391
|
+
日期: item.publish_time ?? "",
|
|
392
|
+
情感: formatEmotional(item.emotionalIndex),
|
|
393
|
+
URL: item.source_url ?? "",
|
|
394
|
+
内容摘要:
|
|
395
|
+
stripHtml(item.content ?? "").slice(0, 500) +
|
|
396
|
+
(stripHtml(item.content ?? "").length > 500 ? "..." : ""),
|
|
397
|
+
}));
|
|
398
|
+
|
|
399
|
+
const text = JSON.stringify(
|
|
400
|
+
{ keyword, total: out.length, items: out },
|
|
401
|
+
null,
|
|
402
|
+
2
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
return {
|
|
406
|
+
content: [{ type: "text", text }],
|
|
407
|
+
};
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
const transport = new StdioServerTransport();
|
|
411
|
+
await server.connect(transport);
|