cyberquant-mcp 0.1.0 → 0.1.2
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 +5 -3
- package/dist/index.js +126 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,12 +10,13 @@ CyberQuant 数据共享平台的 MCP(Model Context Protocol)服务器,让
|
|
|
10
10
|
|
|
11
11
|
## 功能特性
|
|
12
12
|
|
|
13
|
-
### MCP Tools(
|
|
13
|
+
### MCP Tools(4 个)
|
|
14
14
|
|
|
15
15
|
| 工具 | 说明 |
|
|
16
16
|
|------|------|
|
|
17
17
|
| `configure` | 配置 API Key,首次使用时调用 |
|
|
18
|
-
| `list_routes` |
|
|
18
|
+
| `list_routes` | 列出当前用户可用的数据路由**目录**(仅元信息,不含入参/返回字段) |
|
|
19
|
+
| `get_route_detail` | 查询单个路由的入参/返回字段详情,由大模型据此自行组织 `query_data` 参数 |
|
|
19
20
|
| `query_data` | 查询指定路由数据,返回 **CSV 格式**(节省 token) |
|
|
20
21
|
|
|
21
22
|
### MCP Resources(2 个)
|
|
@@ -28,6 +29,7 @@ CyberQuant 数据共享平台的 MCP(Model Context Protocol)服务器,让
|
|
|
28
29
|
### 设计亮点
|
|
29
30
|
|
|
30
31
|
- **CSV 输出**:相比 JSON 节省 40–60% token,表格结构天然适合 AI 分析
|
|
32
|
+
- **路由目录 + 按需详情**:`list_routes` 仅输出目录级元信息,`get_route_detail` 按需取详情,避免上百路由全量入参一次性占满上下文(路由列表内存缓存 2 分钟,list→detail 连贯调用零重复网络)
|
|
31
33
|
- **自然语言引导**:数据返回附带提示,引导 AI 缩小查询范围而非暴力翻页
|
|
32
34
|
- **pageSize 上限保护**:超过 1000 条自动拦截,避免 AI 处理超大数据集
|
|
33
35
|
- **运行时配置**:通过 `configure` 工具动态更新 API Key,无需重启
|
|
@@ -85,7 +87,7 @@ MCP Server 与 `cyberquant-cli` 共用配置文件 `~/.cyberquant/config.json`
|
|
|
85
87
|
帮我查一下平安银行最近一周的日K线数据
|
|
86
88
|
```
|
|
87
89
|
|
|
88
|
-
AI 会依次调用 `list_routes` → `query_data`,并以表格形式返回分析结果。更多场景见 [使用示例](./examples/usage-examples.md)。
|
|
90
|
+
AI 会依次调用 `list_routes` → `get_route_detail` → `query_data`,并以表格形式返回分析结果。更多场景见 [使用示例](./examples/usage-examples.md)。
|
|
89
91
|
|
|
90
92
|
## 文档
|
|
91
93
|
|
package/dist/index.js
CHANGED
|
@@ -112,10 +112,13 @@ function mapApiError(status, detail) {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
// src/lib/api-client.ts
|
|
115
|
+
var ROUTES_CACHE_TTL_MS = 12e4;
|
|
115
116
|
var ApiClient = class {
|
|
116
117
|
endpoint;
|
|
117
118
|
apiKey;
|
|
118
119
|
timeout;
|
|
120
|
+
/** 路由列表内存缓存(仅成功响应才回填,失败不污染) */
|
|
121
|
+
routesCache = null;
|
|
119
122
|
constructor(config) {
|
|
120
123
|
this.endpoint = config.endpoint;
|
|
121
124
|
this.apiKey = config.apiKey;
|
|
@@ -159,9 +162,17 @@ var ApiClient = class {
|
|
|
159
162
|
async getProfile() {
|
|
160
163
|
return this.request("/api/v1/me");
|
|
161
164
|
}
|
|
162
|
-
/**
|
|
165
|
+
/** 获取可用路由列表(命中 2 分钟内存缓存时零网络) */
|
|
163
166
|
async listRoutes() {
|
|
164
|
-
|
|
167
|
+
const now = Date.now();
|
|
168
|
+
if (this.routesCache && now < this.routesCache.expiresAt) {
|
|
169
|
+
return this.routesCache.data;
|
|
170
|
+
}
|
|
171
|
+
const res = await this.request("/api/v1/api-list");
|
|
172
|
+
if (res.success) {
|
|
173
|
+
this.routesCache = { data: res, expiresAt: now + ROUTES_CACHE_TTL_MS };
|
|
174
|
+
}
|
|
175
|
+
return res;
|
|
165
176
|
}
|
|
166
177
|
/** 查询数据 */
|
|
167
178
|
async queryData(routeSlug, params, pageSize) {
|
|
@@ -232,30 +243,22 @@ var NO_CONFIG_HINT = "\u672A\u68C0\u6D4B\u5230 API Key \u914D\u7F6E\u3002\u8BF7\
|
|
|
232
243
|
|
|
233
244
|
// src/tools/list.ts
|
|
234
245
|
function formatRoute(route) {
|
|
235
|
-
|
|
246
|
+
return [
|
|
236
247
|
`\u3010${route.displayName}\u3011routeSlug: ${route.routeSlug}`,
|
|
237
248
|
`\u8BF4\u660E\uFF1A${route.description}`,
|
|
238
249
|
`\u5206\u7C7B\uFF1A${route.category}`
|
|
239
|
-
];
|
|
240
|
-
if (route.queryParams.length > 0) {
|
|
241
|
-
lines.push("\u67E5\u8BE2\u53C2\u6570\uFF1A");
|
|
242
|
-
for (const p of route.queryParams) {
|
|
243
|
-
const required = p.required ? "\u5FC5\u586B" : "\u53EF\u9009";
|
|
244
|
-
lines.push(` - ${p.name} (${p.type}, ${required}): ${p.desc}`);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
if (route.responseParams.length > 0) {
|
|
248
|
-
lines.push("\u8FD4\u56DE\u5B57\u6BB5\uFF1A");
|
|
249
|
-
for (const f of route.responseParams) {
|
|
250
|
-
lines.push(` - ${f.name} (${f.type}): ${f.desc}`);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
return lines.join("\n");
|
|
250
|
+
].join("\n");
|
|
254
251
|
}
|
|
252
|
+
var NEXT_STEP_HINT = [
|
|
253
|
+
"\u4EE5\u4E0A\u4E3A\u8DEF\u7531\u76EE\u5F55\uFF0C**\u4E0D\u542B\u5165\u53C2/\u8FD4\u56DE\u5B57\u6BB5\u8BE6\u60C5**\u3002",
|
|
254
|
+
"\u8BF7\u6309\u4EE5\u4E0B\u6D41\u7A0B\u64CD\u4F5C\uFF1A",
|
|
255
|
+
'1. \u8C03\u7528 get_route_detail(routeSlug="...") \u83B7\u53D6\u76EE\u6807\u8DEF\u7531\u7684\u67E5\u8BE2\u53C2\u6570\u4E0E\u8FD4\u56DE\u5B57\u6BB5\u8BF4\u660E',
|
|
256
|
+
"2. \u6839\u636E get_route_detail \u8FD4\u56DE\u7684\u53C2\u6570\u8BF4\u660E\u4E0E\u4F20\u503C\u683C\u5F0F\uFF0C\u81EA\u884C\u7EC4\u7EC7 params \u540E\u8C03\u7528 query_data \u67E5\u8BE2\u6570\u636E"
|
|
257
|
+
].join("\n");
|
|
255
258
|
function registerListRoutesTool(server, state) {
|
|
256
259
|
server.tool(
|
|
257
260
|
"list_routes",
|
|
258
|
-
"\u5217\u51FA\u5F53\u524D\u7528\u6237\u53EF\u7528\u7684\u6570\u636E\u8DEF\u7531\
|
|
261
|
+
"\u5217\u51FA\u5F53\u524D\u7528\u6237\u53EF\u7528\u7684\u6570\u636E\u8DEF\u7531\u76EE\u5F55\uFF08\u4EC5\u542B routeSlug\u3001\u540D\u79F0\u3001\u8BF4\u660E\u3001\u5206\u7C7B\uFF0C\u4E0D\u542B\u5165\u53C2/\u8FD4\u56DE\u5B57\u6BB5\uFF09\u3002\u5982\u9700\u5165\u53C2/\u8FD4\u56DE\u5B57\u6BB5\u8BE6\u60C5\uFF0C\u8BF7\u4F7F\u7528 get_route_detail \u5DE5\u5177\u3002",
|
|
259
262
|
{},
|
|
260
263
|
async () => {
|
|
261
264
|
if (!state.client) {
|
|
@@ -278,7 +281,9 @@ function registerListRoutesTool(server, state) {
|
|
|
278
281
|
const header = `\u53EF\u7528\u6570\u636E\u8DEF\u7531\uFF08\u5171 ${total} \u4E2A\uFF09\uFF1A
|
|
279
282
|
`;
|
|
280
283
|
const body = routes.map(formatRoute).join("\n\n");
|
|
281
|
-
const footer = total > 0 ?
|
|
284
|
+
const footer = total > 0 ? `
|
|
285
|
+
|
|
286
|
+
${NEXT_STEP_HINT}` : "";
|
|
282
287
|
return {
|
|
283
288
|
content: [{ type: "text", text: header + body + footer }]
|
|
284
289
|
};
|
|
@@ -294,8 +299,103 @@ function registerListRoutesTool(server, state) {
|
|
|
294
299
|
);
|
|
295
300
|
}
|
|
296
301
|
|
|
297
|
-
// src/tools/
|
|
302
|
+
// src/tools/detail.ts
|
|
298
303
|
import { z as z2 } from "zod";
|
|
304
|
+
var PARAM_FORMAT_GUIDE = [
|
|
305
|
+
"\u4F20\u503C\u683C\u5F0F\uFF08\u6309\u53C2\u6570 type\uFF09\uFF1A",
|
|
306
|
+
'- string\uFF1A\u5355\u503C\u4F20\u5B57\u7B26\u4E32 "v"\uFF1B\u591A\u503C\u4F20\u5B57\u7B26\u4E32\u6570\u7EC4 ["v1","v2"]\uFF0C\u6216\u9017\u53F7\u5206\u9694\u5B57\u7B26\u4E32 "v1,v2,v3"\uFF08\u2264100 \u9879\uFF09',
|
|
307
|
+
'- number\uFF1A\u5355\u503C\u4F20\u6570\u5B57 1\uFF1B\u591A\u503C\u4F20\u6570\u5B57\u6570\u7EC4 [1,5,30]\uFF0C\u6216\u9017\u53F7\u5206\u9694\u5B57\u7B26\u4E32 "1,5,30"\uFF08\u2264100 \u9879\uFF09',
|
|
308
|
+
'- date\uFF1A\u5355\u503C\u4F20\u5B57\u7B26\u4E32 "2026-05-01"\uFF1B\u8303\u56F4\u4F20\u4E24\u5143\u7D20\u6570\u7EC4 ["2026-05-01","2026-05-07"]\uFF08\u8BED\u4E49 >= AND <=\uFF09',
|
|
309
|
+
" \u652F\u6301\u683C\u5F0F\uFF1Ayyyy-MM-dd \u6216 yyyy-MM-dd HH:mm:ss"
|
|
310
|
+
].join("\n");
|
|
311
|
+
var CALL_HINT = [
|
|
312
|
+
"\u8BF7\u6839\u636E\u4E0A\u8FF0\u67E5\u8BE2\u53C2\u6570\u8BF4\u660E\u4E0E\u901A\u7528\u4F20\u503C\u683C\u5F0F\uFF0C\u7ED3\u5408\u7528\u6237\u5B9E\u9645\u610F\u56FE\uFF08\u4F8B\u5982\u6307\u5B9A\u7684\u4EE3\u7801\u3001\u65F6\u95F4\u8303\u56F4\u7B49\uFF09\uFF1A",
|
|
313
|
+
"1. \u81EA\u884C\u5224\u65AD\u9700\u8981\u4F20\u54EA\u4E9B\u53C2\u6570\uFF08\u5FC5\u586B\u7684\u52A1\u5FC5\u5E26\u4E0A\uFF0C\u53EF\u9009\u7684\u6309\u9700\uFF09",
|
|
314
|
+
"2. \u6309\u5404\u53C2\u6570\u5BF9\u5E94\u7684 type \u9009\u62E9\u6B63\u786E\u7684\u4F20\u503C\u5F62\u5F0F\uFF08\u5355\u503C / \u591A\u503C / \u8303\u56F4\uFF09",
|
|
315
|
+
"3. \u8C03\u7528 query_data(routeSlug, params) \u83B7\u53D6\u6570\u636E",
|
|
316
|
+
"\u6CE8\uFF1ApageSize \u7531 mcp.pageSize \u914D\u7F6E\u63A7\u5236\uFF0C\u4E0D\u8981\u5199\u5165 params\u3002"
|
|
317
|
+
].join("\n");
|
|
318
|
+
function formatQueryParams(params) {
|
|
319
|
+
if (params.length === 0) return "\u67E5\u8BE2\u53C2\u6570\uFF1A\uFF08\u65E0\uFF09";
|
|
320
|
+
const lines = ["\u67E5\u8BE2\u53C2\u6570\uFF1A"];
|
|
321
|
+
for (const p of params) {
|
|
322
|
+
const required = p.required ? "\u5FC5\u586B" : "\u53EF\u9009";
|
|
323
|
+
lines.push(` - ${p.name} (${p.type}, ${required}): ${p.desc}`);
|
|
324
|
+
}
|
|
325
|
+
return lines.join("\n");
|
|
326
|
+
}
|
|
327
|
+
function formatResponseParams(params) {
|
|
328
|
+
if (params.length === 0) return "\u8FD4\u56DE\u5B57\u6BB5\uFF1A\uFF08\u65E0\uFF09";
|
|
329
|
+
const lines = ["\u8FD4\u56DE\u5B57\u6BB5\uFF1A"];
|
|
330
|
+
for (const f of params) {
|
|
331
|
+
lines.push(` - ${f.name} (${f.type}): ${f.desc}`);
|
|
332
|
+
}
|
|
333
|
+
return lines.join("\n");
|
|
334
|
+
}
|
|
335
|
+
function registerGetRouteDetailTool(server, state) {
|
|
336
|
+
server.tool(
|
|
337
|
+
"get_route_detail",
|
|
338
|
+
"\u67E5\u8BE2\u6307\u5B9A\u8DEF\u7531\u7684\u5165\u53C2\u4E0E\u8FD4\u56DE\u5B57\u6BB5\u8BE6\u60C5\u3002\u5148\u7528 list_routes \u627E\u5230 routeSlug\uFF0C\u518D\u7528\u672C\u5DE5\u5177\u62FF\u53C2\u6570\u8BF4\u660E\uFF0C\u6700\u540E\u7531\u5927\u6A21\u578B\u6839\u636E\u8BF4\u660E\u81EA\u884C\u7EC4\u7EC7 params \u8C03\u7528 query_data\u3002",
|
|
339
|
+
{
|
|
340
|
+
routeSlug: z2.string().describe('\u8DEF\u7531\u6807\u8BC6\uFF0C\u5982 "daily-stock"\u3002\u901A\u8FC7 list_routes \u83B7\u53D6\u53EF\u7528 routeSlug\u3002')
|
|
341
|
+
},
|
|
342
|
+
async ({ routeSlug }) => {
|
|
343
|
+
if (!state.client) {
|
|
344
|
+
return { content: [{ type: "text", text: NO_CONFIG_HINT }] };
|
|
345
|
+
}
|
|
346
|
+
try {
|
|
347
|
+
const res = await state.client.listRoutes();
|
|
348
|
+
if (!res.success || !res.data) {
|
|
349
|
+
return {
|
|
350
|
+
content: [
|
|
351
|
+
{
|
|
352
|
+
type: "text",
|
|
353
|
+
text: `\u83B7\u53D6\u8DEF\u7531\u8BE6\u60C5\u5931\u8D25\uFF1A${res.error ?? "\u672A\u77E5\u9519\u8BEF"}\u3002\u8BF7\u68C0\u67E5 API Key \u914D\u7F6E\u662F\u5426\u6B63\u786E\u3002`
|
|
354
|
+
}
|
|
355
|
+
]
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
const route = res.data.find((r) => r.routeSlug === routeSlug);
|
|
359
|
+
if (!route) {
|
|
360
|
+
return {
|
|
361
|
+
content: [
|
|
362
|
+
{
|
|
363
|
+
type: "text",
|
|
364
|
+
text: `\u672A\u627E\u5230 routeSlug="${routeSlug}" \u5BF9\u5E94\u7684\u8DEF\u7531\u3002\u8BF7\u8C03\u7528 list_routes \u5DE5\u5177\u786E\u8BA4 routeSlug \u662F\u5426\u6B63\u786E\uFF08\u6CE8\u610F\u5927\u5C0F\u5199\u4E0E\u8FDE\u5B57\u7B26\uFF09\u3002`
|
|
365
|
+
}
|
|
366
|
+
]
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
const sections = [
|
|
370
|
+
`\u3010${route.displayName}\u3011routeSlug: ${route.routeSlug}`,
|
|
371
|
+
`\u8BF4\u660E\uFF1A${route.description}`,
|
|
372
|
+
`\u5206\u7C7B\uFF1A${route.category}`,
|
|
373
|
+
"",
|
|
374
|
+
formatQueryParams(route.queryParams),
|
|
375
|
+
"",
|
|
376
|
+
formatResponseParams(route.responseParams),
|
|
377
|
+
"",
|
|
378
|
+
PARAM_FORMAT_GUIDE,
|
|
379
|
+
"",
|
|
380
|
+
CALL_HINT
|
|
381
|
+
];
|
|
382
|
+
return {
|
|
383
|
+
content: [{ type: "text", text: sections.join("\n") }]
|
|
384
|
+
};
|
|
385
|
+
} catch (err) {
|
|
386
|
+
const msg = err instanceof Error ? err.message : "\u672A\u77E5\u9519\u8BEF";
|
|
387
|
+
return {
|
|
388
|
+
content: [
|
|
389
|
+
{ type: "text", text: `\u83B7\u53D6\u8DEF\u7531\u8BE6\u60C5\u5931\u8D25\uFF1A${msg}` }
|
|
390
|
+
]
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// src/tools/query.ts
|
|
398
|
+
import { z as z3 } from "zod";
|
|
299
399
|
function toCsv(rows) {
|
|
300
400
|
if (rows.length === 0) return "";
|
|
301
401
|
const headers = Object.keys(rows[0]);
|
|
@@ -318,14 +418,14 @@ var PAGE_SIZE_WARNING = (n) => `\u26A0\uFE0F \u5F53\u524D\u914D\u7F6E\u7684\u535
|
|
|
318
418
|
2. \u4F7F\u7528\u7F16\u7A0B\u811A\u672C\uFF08Python/Node.js\uFF09\u914D\u5408 cyberquant-cli \u5904\u7406\u5927\u6570\u636E\u91CF\u4EFB\u52A1
|
|
319
419
|
3. \u7F29\u5C0F\u67E5\u8BE2\u53C2\u6570\u8303\u56F4\uFF0C\u83B7\u53D6\u66F4\u7CBE\u51C6\u7684\u6570\u636E\u5B50\u96C6`;
|
|
320
420
|
var HAS_MORE_HINT = "\u26A0\uFE0F \u5F53\u524D\u67E5\u8BE2\u8303\u56F4\u4E0B\u8FD8\u6709\u66F4\u591A\u6570\u636E\u672A\u8FD4\u56DE\u3002\u5EFA\u8BAE\u7F29\u5C0F\u67E5\u8BE2\u53C2\u6570\u8303\u56F4\uFF08\u5982\u7F29\u5C0F\u65E5\u671F\u533A\u95F4\u3001\u6307\u5B9A\u5177\u4F53\u4EE3\u7801\u7B49\uFF09\u4EE5\u83B7\u53D6\u7CBE\u786E\u7684\u6570\u636E\u5B50\u96C6\uFF0C\u5927\u6A21\u578B\u66F4\u9002\u5408\u5206\u6790\u7CBE\u51C6\u7684\u5C0F\u6570\u636E\u96C6\u3002";
|
|
321
|
-
var NO_DATA_HINT = "\u672A\u67E5\u8BE2\u5230\u7B26\u5408\u6761\u4EF6\u7684\u6570\u636E\u3002\u8BF7\u68C0\u67E5\u67E5\u8BE2\u53C2\u6570\u662F\u5426\u6B63\u786E\uFF0C\u53EF\u901A\u8FC7
|
|
421
|
+
var NO_DATA_HINT = "\u672A\u67E5\u8BE2\u5230\u7B26\u5408\u6761\u4EF6\u7684\u6570\u636E\u3002\u8BF7\u68C0\u67E5\u67E5\u8BE2\u53C2\u6570\u662F\u5426\u6B63\u786E\uFF0C\u53EF\u901A\u8FC7 get_route_detail \u5DE5\u5177\u67E5\u770B\u8BE5\u8DEF\u7531\u652F\u6301\u7684\u53C2\u6570\u8BF4\u660E\u3002";
|
|
322
422
|
function registerQueryDataTool(server, state) {
|
|
323
423
|
server.tool(
|
|
324
424
|
"query_data",
|
|
325
|
-
"\u67E5\u8BE2\u6307\u5B9A\u8DEF\u7531\u7684\u6570\u636E\uFF0C\u8FD4\u56DE CSV \u683C\u5F0F\u3002\
|
|
425
|
+
"\u67E5\u8BE2\u6307\u5B9A\u8DEF\u7531\u7684\u6570\u636E\uFF0C\u8FD4\u56DE CSV \u683C\u5F0F\u3002\u5148\u7528 list_routes \u627E\u8DEF\u7531\uFF0C\u518D\u7528 get_route_detail \u83B7\u53D6\u5165\u53C2/\u8FD4\u56DE\u5B57\u6BB5\u8BF4\u660E\uFF0C\u6700\u540E\u8C03\u7528\u672C\u5DE5\u5177\u67E5\u8BE2\u3002",
|
|
326
426
|
{
|
|
327
|
-
routeSlug:
|
|
328
|
-
params:
|
|
427
|
+
routeSlug: z3.string().describe('\u8DEF\u7531\u6807\u8BC6\uFF0C\u5982 "daily-stock"\u3002\u901A\u8FC7 list_routes \u83B7\u53D6\u53EF\u7528\u8DEF\u7531\u3002'),
|
|
428
|
+
params: z3.record(z3.union([z3.string(), z3.number(), z3.boolean(), z3.array(z3.union([z3.string(), z3.number()]))])).optional().describe("\u67E5\u8BE2\u53C2\u6570\uFF0C\u952E\u503C\u5BF9\u900F\u4F20\u7ED9 API\u3002\u5177\u4F53\u53C2\u6570\u4E0E\u4F20\u503C\u683C\u5F0F\u8BF7\u5148\u8C03\u7528 get_route_detail(routeSlug) \u83B7\u53D6\uFF0C\u518D\u636E\u6B64\u7EC4\u7EC7\u672C\u5BF9\u8C61\u3002")
|
|
329
429
|
},
|
|
330
430
|
async ({ routeSlug, params }) => {
|
|
331
431
|
if (!state.client || !state.config) {
|
|
@@ -472,6 +572,7 @@ function createServer(state) {
|
|
|
472
572
|
});
|
|
473
573
|
registerConfigureTool(server, state);
|
|
474
574
|
registerListRoutesTool(server, state);
|
|
575
|
+
registerGetRouteDetailTool(server, state);
|
|
475
576
|
registerQueryDataTool(server, state);
|
|
476
577
|
createUserProfileResource(server, state);
|
|
477
578
|
createRouteListResource(server, state);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/lib/config.ts","../src/types/index.ts","../src/lib/errors.ts","../src/lib/api-client.ts","../src/server.ts","../src/tools/configure.ts","../src/lib/state.ts","../src/tools/list.ts","../src/tools/query.ts","../src/resources/user-profile.ts","../src/resources/route-list.ts"],"sourcesContent":["// ============================================================\n// cyberquant-mcp 入口 —— stdio 传输\n// ============================================================\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig } from './lib/config.js';\nimport { ApiClient } from './lib/api-client.js';\nimport type { ServerState } from './lib/state.js';\nimport { createServer } from './server.js';\n\nasync function main() {\n const config = loadConfig();\n\n const state: ServerState = {\n config,\n client: config ? new ApiClient(config) : null,\n };\n\n if (config) {\n process.stderr.write(\n `[cyberquant-mcp] 启动中... endpoint=${config.endpoint}, pageSize=${config.mcp.pageSize}, timeout=${config.mcp.timeout}\\n`,\n );\n } else {\n process.stderr.write(\n '[cyberquant-mcp] 未检测到配置文件,请通过 configure 工具配置 API Key\\n',\n );\n }\n\n const server = createServer(state);\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n process.stderr.write('[cyberquant-mcp] 已启动,通过 stdio 等待 MCP 连接\\n');\n}\n\nmain().catch((err) => {\n process.stderr.write(`[cyberquant-mcp] 启动失败:${err}\\n`);\n process.exit(1);\n});\n","// ============================================================\n// 配置管理 —— 共用 ~/.cyberquant/config.json\n// ============================================================\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport type { AppConfig, McpConfig } from '../types/index.js';\nimport { MCP_DEFAULTS, PAGE_SIZE_MAX } from '../types/index.js';\n\nconst CONFIG_DIR = path.join(os.homedir(), '.cyberquant');\nconst CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\n\n/** 默认 API Gateway 端点 */\nexport const DEFAULT_ENDPOINT = 'https://api.cyberspace2077.com';\n\ninterface RawConfig {\n endpoint?: string;\n apiKey?: string;\n mcp?: Partial<McpConfig>;\n}\n\n/** 读取原始配置文件 */\nfunction readRawConfig(): RawConfig | null {\n try {\n if (!fs.existsSync(CONFIG_FILE)) return null;\n const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');\n return JSON.parse(raw) as RawConfig;\n } catch {\n // 配置文件格式错误,视为无配置\n return null;\n }\n}\n\n/** 自动补全 mcp 字段(缺失时写回文件) */\nfunction ensureMcpField(raw: RawConfig): void {\n if (raw.mcp && raw.mcp.pageSize !== undefined && raw.mcp.timeout !== undefined) {\n return;\n }\n raw.mcp = { ...MCP_DEFAULTS, ...raw.mcp };\n try {\n if (!fs.existsSync(CONFIG_DIR)) {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n }\n fs.writeFileSync(CONFIG_FILE, JSON.stringify(raw, null, 2), 'utf-8');\n } catch {\n // 写入失败不影响运行,使用默认值即可\n }\n}\n\n/** 从原始配置构建 AppConfig(不抛异常) */\nfunction buildConfig(raw: RawConfig): AppConfig | null {\n if (!raw.endpoint || !raw.apiKey) return null;\n\n ensureMcpField(raw);\n\n const mcp: McpConfig = {\n pageSize: raw.mcp?.pageSize ?? MCP_DEFAULTS.pageSize,\n timeout: raw.mcp?.timeout ?? MCP_DEFAULTS.timeout,\n };\n\n if (mcp.pageSize > PAGE_SIZE_MAX) {\n process.stderr.write(\n `[cyberquant-mcp] 警告:mcp.pageSize=${mcp.pageSize} 超过上限 ${PAGE_SIZE_MAX},查询时将返回警告提示\\n`,\n );\n }\n\n return {\n endpoint: raw.endpoint.replace(/\\/+$/, ''),\n apiKey: raw.apiKey,\n mcp,\n };\n}\n\n/** 加载配置,无配置或缺少必填字段时返回 null */\nexport function loadConfig(): AppConfig | null {\n const raw = readRawConfig();\n if (!raw) return null;\n return buildConfig(raw);\n}\n\n/** 保存配置并返回 AppConfig(用于 configure tool) */\nexport function saveConfig(apiKey: string, endpoint?: string): AppConfig {\n const config: RawConfig = {\n endpoint: (endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, ''),\n apiKey,\n mcp: { ...MCP_DEFAULTS },\n };\n\n if (!fs.existsSync(CONFIG_DIR)) {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n }\n fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');\n\n return {\n endpoint: config.endpoint!,\n apiKey: config.apiKey,\n mcp: { ...MCP_DEFAULTS },\n };\n}\n","// ============================================================\n// cyberquant-mcp 类型定义\n// ============================================================\n\n/** MCP 专有配置 */\nexport interface McpConfig {\n pageSize: number;\n timeout: number;\n}\n\n/** 应用配置(共用 ~/.cyberquant/config.json) */\nexport interface AppConfig {\n endpoint: string;\n apiKey: string;\n mcp: McpConfig;\n}\n\n/** MCP 配置默认值 */\nexport const MCP_DEFAULTS: McpConfig = {\n pageSize: 200,\n timeout: 30000,\n};\n\n/** pageSize 上限 */\nexport const PAGE_SIZE_MAX = 1000;\n\n// ---- API Gateway 响应类型 ----\n\nexport interface Pagination {\n nextCursor: string | null;\n hasMore: boolean;\n pageSize: number;\n}\n\nexport interface ApiMeta {\n route?: string;\n count?: number;\n total?: number;\n pagination?: Pagination;\n userTier?: number;\n userMarkets?: string[];\n}\n\nexport interface ApiResponse<T = unknown> {\n success: boolean;\n data?: T;\n error?: string;\n code?: string;\n meta?: ApiMeta;\n}\n\n// ---- 用户信息 ----\n\nexport interface UserProfile {\n email: string;\n tier: { level: number; name: string };\n markets: { code: string; name: string }[];\n expiresAt: string | null;\n isActive: boolean;\n rateLimit: { windowMs: number; maxRequests: number };\n}\n\n// ---- 路由列表 ----\n\nexport interface QueryParam {\n name: string;\n type: 'string' | 'number' | 'date' | 'boolean';\n required: boolean;\n desc: string;\n}\n\nexport interface ResponseParam {\n name: string;\n type: 'string' | 'number' | 'date' | 'boolean';\n desc: string;\n}\n\nexport interface RouteInfo {\n routeSlug: string;\n displayName: string;\n description: string;\n category: string;\n marketType: string;\n requiredTier: number;\n queryParams: QueryParam[];\n responseParams: ResponseParam[];\n}\n\nexport interface RouteListMeta {\n total: number;\n userTier: number;\n userMarkets: string[];\n}\n","// ============================================================\n// 错误类型定义\n// ============================================================\n\nexport class McpError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'McpError';\n }\n}\n\nexport class ConfigError extends McpError {\n constructor(message: string) {\n super(message);\n this.name = 'ConfigError';\n }\n}\n\nexport class ApiError extends McpError {\n constructor(\n public readonly statusCode: number,\n message: string,\n ) {\n super(message);\n this.name = 'ApiError';\n }\n}\n\n/** HTTP 状态码 → 中文错误消息 */\nconst ERROR_MESSAGES: Record<number, string> = {\n 400: '参数校验失败',\n 401: 'API Key 无效或已过期',\n 403: '权限不足',\n 404: '未找到 API 路由',\n 429: '请求频率超限',\n 500: '服务器内部错误',\n 503: '服务暂不可用',\n};\n\n/** 将 HTTP 状态码映射为 ApiError */\nexport function mapApiError(status: number, detail?: string): ApiError {\n const base = ERROR_MESSAGES[status] ?? `请求失败 (${status})`;\n return new ApiError(status, detail ? `${base}: ${detail}` : base);\n}\n","// ============================================================\n// API Gateway HTTP 客户端(含重试 + 超时)\n// ============================================================\n\nimport type {\n AppConfig,\n ApiResponse,\n UserProfile,\n RouteInfo,\n RouteListMeta,\n} from '../types/index.js';\nimport { mapApiError } from './errors.js';\n\nexport class ApiClient {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly timeout: number;\n\n constructor(config: AppConfig) {\n this.endpoint = config.endpoint;\n this.apiKey = config.apiKey;\n this.timeout = config.mcp.timeout;\n }\n\n /** 通用请求(含 429/503 重试) */\n private async request<T>(path: string): Promise<T> {\n const url = `${this.endpoint}${path}`;\n const maxRetries = 3;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const res = await fetch(url, {\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n 'X-Client-Type': 'mcp',\n },\n signal: AbortSignal.timeout(this.timeout),\n });\n\n if (res.ok) {\n return (await res.json()) as T;\n }\n\n // 429 / 503 自动重试\n if ((res.status === 429 || res.status === 503) && attempt < maxRetries) {\n // 消耗响应体以避免 Socket 泄漏\n await res.body?.cancel().catch(() => {});\n const retryAfter = res.headers.get('Retry-After');\n const delay = retryAfter\n ? Number(retryAfter) * 1000\n : Math.pow(2, attempt) * 1000;\n await new Promise((r) => setTimeout(r, Math.min(delay, 10000)));\n continue;\n }\n\n // 其他错误:提取错误详情\n let detail = '';\n try {\n const body = (await res.json()) as ApiResponse;\n detail = body.error ?? '';\n } catch {\n // JSON 解析失败,使用空详情\n }\n throw mapApiError(res.status, detail);\n }\n\n // 重试耗尽\n throw mapApiError(503, '重试次数已用完,请稍后再试');\n }\n\n /** 获取用户信息 */\n async getProfile(): Promise<ApiResponse<UserProfile>> {\n return this.request('/api/v1/me');\n }\n\n /** 获取可用路由列表 */\n async listRoutes(): Promise<ApiResponse<RouteInfo[]> & { meta?: RouteListMeta }> {\n return this.request('/api/v1/api-list');\n }\n\n /** 查询数据 */\n async queryData(\n routeSlug: string,\n params: Record<string, string | number | boolean | undefined | (string | number)[]>,\n pageSize: number,\n ): Promise<ApiResponse<Record<string, unknown>[]>> {\n const qs = new URLSearchParams();\n qs.set('pageSize', String(pageSize));\n\n for (const [k, v] of Object.entries(params)) {\n if (v === undefined || v === '') continue;\n if (Array.isArray(v)) {\n for (const item of v) {\n if (item !== undefined && item !== '') qs.append(k, String(item));\n }\n } else {\n qs.set(k, String(v));\n }\n }\n\n const query = qs.toString();\n return this.request(`/api/v1/data/${routeSlug}${query ? '?' + query : ''}`);\n }\n}\n","// ============================================================\n// MCP Server —— 注册 Tools + Resources\n// ============================================================\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerState } from './lib/state.js';\nimport { registerConfigureTool } from './tools/configure.js';\nimport { registerListRoutesTool } from './tools/list.js';\nimport { registerQueryDataTool } from './tools/query.js';\nimport { createUserProfileResource } from './resources/user-profile.js';\nimport { createRouteListResource } from './resources/route-list.js';\n\nexport function createServer(state: ServerState): McpServer {\n const server = new McpServer({\n name: 'cyberquant-mcp',\n version: '0.1.0',\n });\n\n // 注册 Tools\n registerConfigureTool(server, state);\n registerListRoutesTool(server, state);\n registerQueryDataTool(server, state);\n\n // 注册 Resources\n createUserProfileResource(server, state);\n createRouteListResource(server, state);\n\n return server;\n}\n","// ============================================================\n// Tool: configure — 配置 API Key(首次使用时调用)\n// ============================================================\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerState } from '../lib/state.js';\nimport { saveConfig, DEFAULT_ENDPOINT } from '../lib/config.js';\nimport { ApiClient } from '../lib/api-client.js';\n\nexport function registerConfigureTool(server: McpServer, state: ServerState): void {\n server.tool(\n 'configure',\n '配置 API Key 以访问数据服务。首次使用时必须调用此工具完成配置。endpoint 默认为 https://api.cyberspace2077.com。',\n {\n apiKey: z.string().describe('API Key,格式为 sk_live_xxx 或 sk_test_xxx'),\n endpoint: z\n .string()\n .optional()\n .describe(`API Gateway 地址,默认 ${DEFAULT_ENDPOINT}`),\n },\n async ({ apiKey, endpoint }) => {\n try {\n const config = saveConfig(apiKey, endpoint);\n\n // 更新共享状态,后续调用立即可用\n state.config = config;\n state.client = new ApiClient(config);\n\n process.stderr.write(\n `[cyberquant-mcp] 配置已更新:endpoint=${config.endpoint}, pageSize=${config.mcp.pageSize}\\n`,\n );\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `配置成功!endpoint: ${config.endpoint},现在可以使用 list_routes 查看可用数据路由,或使用 query_data 查询数据。`,\n },\n ],\n };\n } catch (err) {\n const msg = err instanceof Error ? err.message : '未知错误';\n return {\n content: [\n {\n type: 'text' as const,\n text: `配置保存失败:${msg}。请检查是否有写入 ~/.cyberquant/ 目录的权限。`,\n },\n ],\n };\n }\n },\n );\n}\n","// ============================================================\n// 共享可变状态 —— Tools/Resources 通过此对象访问 ApiClient\n// ============================================================\n\nimport type { AppConfig } from '../types/index.js';\nimport type { ApiClient } from './api-client.js';\n\nexport interface ServerState {\n config: AppConfig | null;\n client: ApiClient | null;\n}\n\n/** 无配置时的统一提示文案 */\nexport const NO_CONFIG_HINT =\n '未检测到 API Key 配置。请先调用 configure 工具,传入 apiKey 参数完成配置。endpoint 默认为 https://api.cyberspace2077.com,也可自定义。';\n","// ============================================================\n// Tool: list_routes — 列出可用路由及参数 Schema\n// ============================================================\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerState } from '../lib/state.js';\nimport { NO_CONFIG_HINT } from '../lib/state.js';\nimport type { RouteInfo } from '../types/index.js';\n\n/** 格式化单个路由为自然语言描述 */\nfunction formatRoute(route: RouteInfo): string {\n const lines: string[] = [\n `【${route.displayName}】routeSlug: ${route.routeSlug}`,\n `说明:${route.description}`,\n `分类:${route.category}`,\n ];\n\n if (route.queryParams.length > 0) {\n lines.push('查询参数:');\n for (const p of route.queryParams) {\n const required = p.required ? '必填' : '可选';\n lines.push(` - ${p.name} (${p.type}, ${required}): ${p.desc}`);\n }\n }\n\n if (route.responseParams.length > 0) {\n lines.push('返回字段:');\n for (const f of route.responseParams) {\n lines.push(` - ${f.name} (${f.type}): ${f.desc}`);\n }\n }\n\n return lines.join('\\n');\n}\n\nexport function registerListRoutesTool(server: McpServer, state: ServerState): void {\n server.tool(\n 'list_routes',\n '列出当前用户可用的数据路由及其参数 Schema,包含每个路由的查询参数和返回字段说明',\n {},\n async () => {\n if (!state.client) {\n return { content: [{ type: 'text' as const, text: NO_CONFIG_HINT }] };\n }\n\n try {\n const res = await state.client.listRoutes();\n\n if (!res.success || !res.data) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `获取路由列表失败:${res.error ?? '未知错误'}。请检查 API Key 配置是否正确。`,\n },\n ],\n };\n }\n\n const routes = res.data;\n const total = res.meta?.total ?? routes.length;\n\n const header = `可用数据路由(共 ${total} 个):\\n`;\n const body = routes.map(formatRoute).join('\\n\\n');\n const footer = total > 0\n ? '\\n\\n使用 query_data 工具查询指定路由的数据,传入 routeSlug 和查询参数。'\n : '';\n\n return {\n content: [{ type: 'text' as const, text: header + body + footer }],\n };\n } catch (err) {\n const msg = err instanceof Error ? err.message : '未知错误';\n return {\n content: [\n { type: 'text' as const, text: `获取路由列表失败:${msg}` },\n ],\n };\n }\n },\n );\n}\n","// ============================================================\n// Tool: query_data — 数据查询(CSV 格式输出)\n// ============================================================\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerState } from '../lib/state.js';\nimport { NO_CONFIG_HINT } from '../lib/state.js';\nimport { PAGE_SIZE_MAX } from '../types/index.js';\n\n/** 将 JSON 数组转为 CSV 字符串(表头取首条数据字段名) */\nfunction toCsv(rows: Record<string, unknown>[]): string {\n if (rows.length === 0) return '';\n const headers = Object.keys(rows[0]);\n const headerLine = headers.join(',');\n const dataLines = rows.map((row) =>\n headers\n .map((h) => {\n const val = row[h];\n if (val === null || val === undefined) return '';\n const str = String(val);\n // CSV 转义:含逗号、引号、换行时用双引号包裹\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n })\n .join(','),\n );\n return [headerLine, ...dataLines].join('\\n');\n}\n\n/** pageSize 超限警告 */\nconst PAGE_SIZE_WARNING = (n: number) =>\n `⚠️ 当前配置的单次查询数量为 ${n} 条,超过上限 ${PAGE_SIZE_MAX} 条。大模型不擅长直接在庞大的数值矩阵中做复杂的数学运算(如精确计算长期均线、RSI、布林带等),建议:\n1. 将 mcp.pageSize 调整为 ${PAGE_SIZE_MAX} 以内(推荐 200)\n2. 使用编程脚本(Python/Node.js)配合 cyberquant-cli 处理大数据量任务\n3. 缩小查询参数范围,获取更精准的数据子集`;\n\n/** hasMore 提示 */\nconst HAS_MORE_HINT =\n '⚠️ 当前查询范围下还有更多数据未返回。建议缩小查询参数范围(如缩小日期区间、指定具体代码等)以获取精确的数据子集,大模型更适合分析精准的小数据集。';\n\n/** 无数据提示 */\nconst NO_DATA_HINT =\n '未查询到符合条件的数据。请检查查询参数是否正确,可通过 list_routes 工具查看该路由支持的参数说明。';\n\nexport function registerQueryDataTool(server: McpServer, state: ServerState): void {\n server.tool(\n 'query_data',\n '查询指定路由的数据,返回 CSV 格式。使用 list_routes 查看可用路由和参数说明。',\n {\n routeSlug: z.string().describe('路由标识,如 \"daily-stock\"。通过 list_routes 获取可用路由。'),\n params: z\n .record(z.union([z.string(), z.number(), z.boolean(), z.array(z.union([z.string(), z.number()]))]))\n .optional()\n .describe('查询参数,键值对透传给 API。具体参数参考 list_routes 返回的路由说明。'),\n },\n async ({ routeSlug, params }) => {\n if (!state.client || !state.config) {\n return { content: [{ type: 'text' as const, text: NO_CONFIG_HINT }] };\n }\n\n const pageSize = state.config.mcp.pageSize;\n\n // 前置校验:pageSize 上限拦截\n if (pageSize > PAGE_SIZE_MAX) {\n return {\n content: [{ type: 'text' as const, text: PAGE_SIZE_WARNING(pageSize) }],\n };\n }\n\n try {\n const res = await state.client.queryData(routeSlug, params ?? {}, pageSize);\n\n if (!res.success) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `查询失败:${res.error ?? '未知错误'}。请检查路由标识和参数是否正确。`,\n },\n ],\n };\n }\n\n const rows = res.data;\n const count = res.meta?.count ?? (Array.isArray(rows) ? rows.length : 0);\n const hasMore = res.meta?.pagination?.hasMore ?? false;\n\n // 无数据\n if (!Array.isArray(rows) || rows.length === 0) {\n return {\n content: [{ type: 'text' as const, text: NO_DATA_HINT }],\n };\n }\n\n // 拼接结果:CSV + 统计 + 引导\n const parts: string[] = [toCsv(rows), '', `本次查询返回 ${count} 条数据。`];\n if (hasMore) {\n parts.push(HAS_MORE_HINT);\n }\n\n return {\n content: [{ type: 'text' as const, text: parts.join('\\n') }],\n };\n } catch (err) {\n const msg = err instanceof Error ? err.message : '未知错误';\n return {\n content: [{ type: 'text' as const, text: `查询失败:${msg}` }],\n };\n }\n },\n );\n}\n","// ============================================================\n// Resource: cyberquant://user/profile\n// ============================================================\n\nimport type { URL } from 'node:url';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerState } from '../lib/state.js';\n\nexport function createUserProfileResource(server: McpServer, state: ServerState): void {\n server.resource(\n 'user-profile',\n 'cyberquant://user/profile',\n {\n description: '当前用户信息(等级、市场权限、到期时间、速率限制)',\n mimeType: 'application/json',\n },\n async (uri: URL) => {\n if (!state.client) {\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify({\n error: '未检测到 API Key 配置,请先调用 configure 工具完成配置。',\n }),\n },\n ],\n };\n }\n\n const res = await state.client.getProfile();\n if (!res.success || !res.data) {\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify({ error: res.error ?? '获取用户信息失败' }),\n },\n ],\n };\n }\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify(res.data, null, 2),\n },\n ],\n };\n },\n );\n}\n","// ============================================================\n// Resource: cyberquant://routes\n// ============================================================\n\nimport type { URL } from 'node:url';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerState } from '../lib/state.js';\n\nexport function createRouteListResource(server: McpServer, state: ServerState): void {\n server.resource(\n 'routes',\n 'cyberquant://routes',\n {\n description: '当前用户可用的数据路由列表',\n mimeType: 'application/json',\n },\n async (uri: URL) => {\n if (!state.client) {\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify({\n error: '未检测到 API Key 配置,请先调用 configure 工具完成配置。',\n }),\n },\n ],\n };\n }\n\n const res = await state.client.listRoutes();\n if (!res.success || !res.data) {\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify({ error: res.error ?? '获取路由列表失败' }),\n },\n ],\n };\n }\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify(res.data, null, 2),\n },\n ],\n };\n },\n );\n}\n"],"mappings":"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","names":["path","z","z"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/lib/config.ts","../src/types/index.ts","../src/lib/errors.ts","../src/lib/api-client.ts","../src/server.ts","../src/tools/configure.ts","../src/lib/state.ts","../src/tools/list.ts","../src/tools/detail.ts","../src/tools/query.ts","../src/resources/user-profile.ts","../src/resources/route-list.ts"],"sourcesContent":["// ============================================================\n// cyberquant-mcp 入口 —— stdio 传输\n// ============================================================\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig } from './lib/config.js';\nimport { ApiClient } from './lib/api-client.js';\nimport type { ServerState } from './lib/state.js';\nimport { createServer } from './server.js';\n\nasync function main() {\n const config = loadConfig();\n\n const state: ServerState = {\n config,\n client: config ? new ApiClient(config) : null,\n };\n\n if (config) {\n process.stderr.write(\n `[cyberquant-mcp] 启动中... endpoint=${config.endpoint}, pageSize=${config.mcp.pageSize}, timeout=${config.mcp.timeout}\\n`,\n );\n } else {\n process.stderr.write(\n '[cyberquant-mcp] 未检测到配置文件,请通过 configure 工具配置 API Key\\n',\n );\n }\n\n const server = createServer(state);\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n process.stderr.write('[cyberquant-mcp] 已启动,通过 stdio 等待 MCP 连接\\n');\n}\n\nmain().catch((err) => {\n process.stderr.write(`[cyberquant-mcp] 启动失败:${err}\\n`);\n process.exit(1);\n});\n","// ============================================================\n// 配置管理 —— 共用 ~/.cyberquant/config.json\n// ============================================================\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport type { AppConfig, McpConfig } from '../types/index.js';\nimport { MCP_DEFAULTS, PAGE_SIZE_MAX } from '../types/index.js';\n\nconst CONFIG_DIR = path.join(os.homedir(), '.cyberquant');\nconst CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\n\n/** 默认 API Gateway 端点 */\nexport const DEFAULT_ENDPOINT = 'https://api.cyberspace2077.com';\n\ninterface RawConfig {\n endpoint?: string;\n apiKey?: string;\n mcp?: Partial<McpConfig>;\n}\n\n/** 读取原始配置文件 */\nfunction readRawConfig(): RawConfig | null {\n try {\n if (!fs.existsSync(CONFIG_FILE)) return null;\n const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');\n return JSON.parse(raw) as RawConfig;\n } catch {\n // 配置文件格式错误,视为无配置\n return null;\n }\n}\n\n/** 自动补全 mcp 字段(缺失时写回文件) */\nfunction ensureMcpField(raw: RawConfig): void {\n if (raw.mcp && raw.mcp.pageSize !== undefined && raw.mcp.timeout !== undefined) {\n return;\n }\n raw.mcp = { ...MCP_DEFAULTS, ...raw.mcp };\n try {\n if (!fs.existsSync(CONFIG_DIR)) {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n }\n fs.writeFileSync(CONFIG_FILE, JSON.stringify(raw, null, 2), 'utf-8');\n } catch {\n // 写入失败不影响运行,使用默认值即可\n }\n}\n\n/** 从原始配置构建 AppConfig(不抛异常) */\nfunction buildConfig(raw: RawConfig): AppConfig | null {\n if (!raw.endpoint || !raw.apiKey) return null;\n\n ensureMcpField(raw);\n\n const mcp: McpConfig = {\n pageSize: raw.mcp?.pageSize ?? MCP_DEFAULTS.pageSize,\n timeout: raw.mcp?.timeout ?? MCP_DEFAULTS.timeout,\n };\n\n if (mcp.pageSize > PAGE_SIZE_MAX) {\n process.stderr.write(\n `[cyberquant-mcp] 警告:mcp.pageSize=${mcp.pageSize} 超过上限 ${PAGE_SIZE_MAX},查询时将返回警告提示\\n`,\n );\n }\n\n return {\n endpoint: raw.endpoint.replace(/\\/+$/, ''),\n apiKey: raw.apiKey,\n mcp,\n };\n}\n\n/** 加载配置,无配置或缺少必填字段时返回 null */\nexport function loadConfig(): AppConfig | null {\n const raw = readRawConfig();\n if (!raw) return null;\n return buildConfig(raw);\n}\n\n/** 保存配置并返回 AppConfig(用于 configure tool) */\nexport function saveConfig(apiKey: string, endpoint?: string): AppConfig {\n const config: RawConfig = {\n endpoint: (endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, ''),\n apiKey,\n mcp: { ...MCP_DEFAULTS },\n };\n\n if (!fs.existsSync(CONFIG_DIR)) {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n }\n fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');\n\n return {\n endpoint: config.endpoint!,\n apiKey: config.apiKey,\n mcp: { ...MCP_DEFAULTS },\n };\n}\n","// ============================================================\n// cyberquant-mcp 类型定义\n// ============================================================\n\n/** MCP 专有配置 */\nexport interface McpConfig {\n pageSize: number;\n timeout: number;\n}\n\n/** 应用配置(共用 ~/.cyberquant/config.json) */\nexport interface AppConfig {\n endpoint: string;\n apiKey: string;\n mcp: McpConfig;\n}\n\n/** MCP 配置默认值 */\nexport const MCP_DEFAULTS: McpConfig = {\n pageSize: 200,\n timeout: 30000,\n};\n\n/** pageSize 上限 */\nexport const PAGE_SIZE_MAX = 1000;\n\n// ---- API Gateway 响应类型 ----\n\nexport interface Pagination {\n nextCursor: string | null;\n hasMore: boolean;\n pageSize: number;\n}\n\nexport interface ApiMeta {\n route?: string;\n count?: number;\n total?: number;\n pagination?: Pagination;\n userTier?: number;\n userMarkets?: string[];\n}\n\nexport interface ApiResponse<T = unknown> {\n success: boolean;\n data?: T;\n error?: string;\n code?: string;\n meta?: ApiMeta;\n}\n\n// ---- 用户信息 ----\n\nexport interface UserProfile {\n email: string;\n tier: { level: number; name: string };\n markets: { code: string; name: string }[];\n expiresAt: string | null;\n isActive: boolean;\n rateLimit: { windowMs: number; maxRequests: number };\n}\n\n// ---- 路由列表 ----\n\nexport interface QueryParam {\n name: string;\n type: 'string' | 'number' | 'date' | 'boolean';\n required: boolean;\n desc: string;\n}\n\nexport interface ResponseParam {\n name: string;\n type: 'string' | 'number' | 'date' | 'boolean';\n desc: string;\n}\n\nexport interface RouteInfo {\n routeSlug: string;\n displayName: string;\n description: string;\n category: string;\n marketType: string;\n requiredTier: number;\n queryParams: QueryParam[];\n responseParams: ResponseParam[];\n}\n\nexport interface RouteListMeta {\n total: number;\n userTier: number;\n userMarkets: string[];\n}\n","// ============================================================\n// 错误类型定义\n// ============================================================\n\nexport class McpError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'McpError';\n }\n}\n\nexport class ConfigError extends McpError {\n constructor(message: string) {\n super(message);\n this.name = 'ConfigError';\n }\n}\n\nexport class ApiError extends McpError {\n constructor(\n public readonly statusCode: number,\n message: string,\n ) {\n super(message);\n this.name = 'ApiError';\n }\n}\n\n/** HTTP 状态码 → 中文错误消息 */\nconst ERROR_MESSAGES: Record<number, string> = {\n 400: '参数校验失败',\n 401: 'API Key 无效或已过期',\n 403: '权限不足',\n 404: '未找到 API 路由',\n 429: '请求频率超限',\n 500: '服务器内部错误',\n 503: '服务暂不可用',\n};\n\n/** 将 HTTP 状态码映射为 ApiError */\nexport function mapApiError(status: number, detail?: string): ApiError {\n const base = ERROR_MESSAGES[status] ?? `请求失败 (${status})`;\n return new ApiError(status, detail ? `${base}: ${detail}` : base);\n}\n","// ============================================================\n// API Gateway HTTP 客户端(含重试 + 超时)\n// ============================================================\n\nimport type {\n AppConfig,\n ApiResponse,\n UserProfile,\n RouteInfo,\n RouteListMeta,\n} from '../types/index.js';\nimport { mapApiError } from './errors.js';\n\n/** 路由列表缓存 TTL(进程内、实例级)\n *\n * 覆盖典型链路:list_routes → 大模型读元信息并挑选路由 → get_route_detail。\n * 2 分钟足以容纳网络/模型慢的真实节奏,且路由元数据陈旧风险可忽略。\n */\nconst ROUTES_CACHE_TTL_MS = 120_000;\n\ntype RoutesResponse = ApiResponse<RouteInfo[]> & { meta?: RouteListMeta };\n\nexport class ApiClient {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly timeout: number;\n\n /** 路由列表内存缓存(仅成功响应才回填,失败不污染) */\n private routesCache: { data: RoutesResponse; expiresAt: number } | null = null;\n\n constructor(config: AppConfig) {\n this.endpoint = config.endpoint;\n this.apiKey = config.apiKey;\n this.timeout = config.mcp.timeout;\n }\n\n /** 通用请求(含 429/503 重试) */\n private async request<T>(path: string): Promise<T> {\n const url = `${this.endpoint}${path}`;\n const maxRetries = 3;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const res = await fetch(url, {\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n 'X-Client-Type': 'mcp',\n },\n signal: AbortSignal.timeout(this.timeout),\n });\n\n if (res.ok) {\n return (await res.json()) as T;\n }\n\n // 429 / 503 自动重试\n if ((res.status === 429 || res.status === 503) && attempt < maxRetries) {\n // 消耗响应体以避免 Socket 泄漏\n await res.body?.cancel().catch(() => {});\n const retryAfter = res.headers.get('Retry-After');\n const delay = retryAfter\n ? Number(retryAfter) * 1000\n : Math.pow(2, attempt) * 1000;\n await new Promise((r) => setTimeout(r, Math.min(delay, 10000)));\n continue;\n }\n\n // 其他错误:提取错误详情\n let detail = '';\n try {\n const body = (await res.json()) as ApiResponse;\n detail = body.error ?? '';\n } catch {\n // JSON 解析失败,使用空详情\n }\n throw mapApiError(res.status, detail);\n }\n\n // 重试耗尽\n throw mapApiError(503, '重试次数已用完,请稍后再试');\n }\n\n /** 获取用户信息 */\n async getProfile(): Promise<ApiResponse<UserProfile>> {\n return this.request('/api/v1/me');\n }\n\n /** 获取可用路由列表(命中 2 分钟内存缓存时零网络) */\n async listRoutes(): Promise<RoutesResponse> {\n const now = Date.now();\n if (this.routesCache && now < this.routesCache.expiresAt) {\n return this.routesCache.data;\n }\n\n const res = await this.request<RoutesResponse>('/api/v1/api-list');\n\n // 仅成功响应才写缓存,错误响应原样透传给上层处理\n if (res.success) {\n this.routesCache = { data: res, expiresAt: now + ROUTES_CACHE_TTL_MS };\n }\n return res;\n }\n\n /** 查询数据 */\n async queryData(\n routeSlug: string,\n params: Record<string, string | number | boolean | undefined | (string | number)[]>,\n pageSize: number,\n ): Promise<ApiResponse<Record<string, unknown>[]>> {\n const qs = new URLSearchParams();\n qs.set('pageSize', String(pageSize));\n\n for (const [k, v] of Object.entries(params)) {\n if (v === undefined || v === '') continue;\n if (Array.isArray(v)) {\n for (const item of v) {\n if (item !== undefined && item !== '') qs.append(k, String(item));\n }\n } else {\n qs.set(k, String(v));\n }\n }\n\n const query = qs.toString();\n return this.request(`/api/v1/data/${routeSlug}${query ? '?' + query : ''}`);\n }\n}\n","// ============================================================\n// MCP Server —— 注册 Tools + Resources\n// ============================================================\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerState } from './lib/state.js';\nimport { registerConfigureTool } from './tools/configure.js';\nimport { registerListRoutesTool } from './tools/list.js';\nimport { registerGetRouteDetailTool } from './tools/detail.js';\nimport { registerQueryDataTool } from './tools/query.js';\nimport { createUserProfileResource } from './resources/user-profile.js';\nimport { createRouteListResource } from './resources/route-list.js';\n\nexport function createServer(state: ServerState): McpServer {\n const server = new McpServer({\n name: 'cyberquant-mcp',\n version: '0.1.0',\n });\n\n // 注册 Tools\n registerConfigureTool(server, state);\n registerListRoutesTool(server, state);\n registerGetRouteDetailTool(server, state);\n registerQueryDataTool(server, state);\n\n // 注册 Resources\n createUserProfileResource(server, state);\n createRouteListResource(server, state);\n\n return server;\n}\n","// ============================================================\n// Tool: configure — 配置 API Key(首次使用时调用)\n// ============================================================\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerState } from '../lib/state.js';\nimport { saveConfig, DEFAULT_ENDPOINT } from '../lib/config.js';\nimport { ApiClient } from '../lib/api-client.js';\n\nexport function registerConfigureTool(server: McpServer, state: ServerState): void {\n server.tool(\n 'configure',\n '配置 API Key 以访问数据服务。首次使用时必须调用此工具完成配置。endpoint 默认为 https://api.cyberspace2077.com。',\n {\n apiKey: z.string().describe('API Key,格式为 sk_live_xxx 或 sk_test_xxx'),\n endpoint: z\n .string()\n .optional()\n .describe(`API Gateway 地址,默认 ${DEFAULT_ENDPOINT}`),\n },\n async ({ apiKey, endpoint }) => {\n try {\n const config = saveConfig(apiKey, endpoint);\n\n // 更新共享状态,后续调用立即可用\n state.config = config;\n state.client = new ApiClient(config);\n\n process.stderr.write(\n `[cyberquant-mcp] 配置已更新:endpoint=${config.endpoint}, pageSize=${config.mcp.pageSize}\\n`,\n );\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `配置成功!endpoint: ${config.endpoint},现在可以使用 list_routes 查看可用数据路由,或使用 query_data 查询数据。`,\n },\n ],\n };\n } catch (err) {\n const msg = err instanceof Error ? err.message : '未知错误';\n return {\n content: [\n {\n type: 'text' as const,\n text: `配置保存失败:${msg}。请检查是否有写入 ~/.cyberquant/ 目录的权限。`,\n },\n ],\n };\n }\n },\n );\n}\n","// ============================================================\n// 共享可变状态 —— Tools/Resources 通过此对象访问 ApiClient\n// ============================================================\n\nimport type { AppConfig } from '../types/index.js';\nimport type { ApiClient } from './api-client.js';\n\nexport interface ServerState {\n config: AppConfig | null;\n client: ApiClient | null;\n}\n\n/** 无配置时的统一提示文案 */\nexport const NO_CONFIG_HINT =\n '未检测到 API Key 配置。请先调用 configure 工具,传入 apiKey 参数完成配置。endpoint 默认为 https://api.cyberspace2077.com,也可自定义。';\n","// ============================================================\n// Tool: list_routes — 列出可用路由(仅目录级元信息,不含入参/返回字段)\n// ============================================================\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerState } from '../lib/state.js';\nimport { NO_CONFIG_HINT } from '../lib/state.js';\nimport type { RouteInfo } from '../types/index.js';\n\n/** 格式化单个路由为目录级条目(不渲染入参/返回字段) */\nfunction formatRoute(route: RouteInfo): string {\n return [\n `【${route.displayName}】routeSlug: ${route.routeSlug}`,\n `说明:${route.description}`,\n `分类:${route.category}`,\n ].join('\\n');\n}\n\n/** 列表后的引导:指向 get_route_detail */\nconst NEXT_STEP_HINT = [\n '以上为路由目录,**不含入参/返回字段详情**。',\n '请按以下流程操作:',\n '1. 调用 get_route_detail(routeSlug=\"...\") 获取目标路由的查询参数与返回字段说明',\n '2. 根据 get_route_detail 返回的参数说明与传值格式,自行组织 params 后调用 query_data 查询数据',\n].join('\\n');\n\nexport function registerListRoutesTool(server: McpServer, state: ServerState): void {\n server.tool(\n 'list_routes',\n '列出当前用户可用的数据路由目录(仅含 routeSlug、名称、说明、分类,不含入参/返回字段)。如需入参/返回字段详情,请使用 get_route_detail 工具。',\n {},\n async () => {\n if (!state.client) {\n return { content: [{ type: 'text' as const, text: NO_CONFIG_HINT }] };\n }\n\n try {\n const res = await state.client.listRoutes();\n\n if (!res.success || !res.data) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `获取路由列表失败:${res.error ?? '未知错误'}。请检查 API Key 配置是否正确。`,\n },\n ],\n };\n }\n\n const routes = res.data;\n const total = res.meta?.total ?? routes.length;\n\n const header = `可用数据路由(共 ${total} 个):\\n`;\n const body = routes.map(formatRoute).join('\\n\\n');\n const footer = total > 0 ? `\\n\\n${NEXT_STEP_HINT}` : '';\n\n return {\n content: [{ type: 'text' as const, text: header + body + footer }],\n };\n } catch (err) {\n const msg = err instanceof Error ? err.message : '未知错误';\n return {\n content: [\n { type: 'text' as const, text: `获取路由列表失败:${msg}` },\n ],\n };\n }\n },\n );\n}\n","// ============================================================\n// Tool: get_route_detail — 查询单个路由的入参/返回字段详情\n// ============================================================\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerState } from '../lib/state.js';\nimport { NO_CONFIG_HINT } from '../lib/state.js';\nimport type { QueryParam, ResponseParam } from '../types/index.js';\n\n/** 通用参数传值格式说明(按参数 type,所有路由共用) */\nconst PARAM_FORMAT_GUIDE = [\n '传值格式(按参数 type):',\n '- string:单值传字符串 \"v\";多值传字符串数组 [\"v1\",\"v2\"],或逗号分隔字符串 \"v1,v2,v3\"(≤100 项)',\n '- number:单值传数字 1;多值传数字数组 [1,5,30],或逗号分隔字符串 \"1,5,30\"(≤100 项)',\n '- date:单值传字符串 \"2026-05-01\";范围传两元素数组 [\"2026-05-01\",\"2026-05-07\"](语义 >= AND <=)',\n ' 支持格式:yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss',\n].join('\\n');\n\n/** 调用 query_data 的引导:让大模型自行分析需要传哪些参数 */\nconst CALL_HINT = [\n '请根据上述查询参数说明与通用传值格式,结合用户实际意图(例如指定的代码、时间范围等):',\n '1. 自行判断需要传哪些参数(必填的务必带上,可选的按需)',\n '2. 按各参数对应的 type 选择正确的传值形式(单值 / 多值 / 范围)',\n '3. 调用 query_data(routeSlug, params) 获取数据',\n '注:pageSize 由 mcp.pageSize 配置控制,不要写入 params。',\n].join('\\n');\n\n/** 渲染查询参数表 */\nfunction formatQueryParams(params: QueryParam[]): string {\n if (params.length === 0) return '查询参数:(无)';\n const lines = ['查询参数:'];\n for (const p of params) {\n const required = p.required ? '必填' : '可选';\n lines.push(` - ${p.name} (${p.type}, ${required}): ${p.desc}`);\n }\n return lines.join('\\n');\n}\n\n/** 渲染返回字段表 */\nfunction formatResponseParams(params: ResponseParam[]): string {\n if (params.length === 0) return '返回字段:(无)';\n const lines = ['返回字段:'];\n for (const f of params) {\n lines.push(` - ${f.name} (${f.type}): ${f.desc}`);\n }\n return lines.join('\\n');\n}\n\nexport function registerGetRouteDetailTool(server: McpServer, state: ServerState): void {\n server.tool(\n 'get_route_detail',\n '查询指定路由的入参与返回字段详情。先用 list_routes 找到 routeSlug,再用本工具拿参数说明,最后由大模型根据说明自行组织 params 调用 query_data。',\n {\n routeSlug: z.string().describe('路由标识,如 \"daily-stock\"。通过 list_routes 获取可用 routeSlug。'),\n },\n async ({ routeSlug }) => {\n if (!state.client) {\n return { content: [{ type: 'text' as const, text: NO_CONFIG_HINT }] };\n }\n\n try {\n const res = await state.client.listRoutes();\n\n if (!res.success || !res.data) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `获取路由详情失败:${res.error ?? '未知错误'}。请检查 API Key 配置是否正确。`,\n },\n ],\n };\n }\n\n const route = res.data.find((r) => r.routeSlug === routeSlug);\n if (!route) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `未找到 routeSlug=\"${routeSlug}\" 对应的路由。请调用 list_routes 工具确认 routeSlug 是否正确(注意大小写与连字符)。`,\n },\n ],\n };\n }\n\n const sections = [\n `【${route.displayName}】routeSlug: ${route.routeSlug}`,\n `说明:${route.description}`,\n `分类:${route.category}`,\n '',\n formatQueryParams(route.queryParams),\n '',\n formatResponseParams(route.responseParams),\n '',\n PARAM_FORMAT_GUIDE,\n '',\n CALL_HINT,\n ];\n\n return {\n content: [{ type: 'text' as const, text: sections.join('\\n') }],\n };\n } catch (err) {\n const msg = err instanceof Error ? err.message : '未知错误';\n return {\n content: [\n { type: 'text' as const, text: `获取路由详情失败:${msg}` },\n ],\n };\n }\n },\n );\n}\n","// ============================================================\n// Tool: query_data — 数据查询(CSV 格式输出)\n// ============================================================\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerState } from '../lib/state.js';\nimport { NO_CONFIG_HINT } from '../lib/state.js';\nimport { PAGE_SIZE_MAX } from '../types/index.js';\n\n/** 将 JSON 数组转为 CSV 字符串(表头取首条数据字段名) */\nfunction toCsv(rows: Record<string, unknown>[]): string {\n if (rows.length === 0) return '';\n const headers = Object.keys(rows[0]);\n const headerLine = headers.join(',');\n const dataLines = rows.map((row) =>\n headers\n .map((h) => {\n const val = row[h];\n if (val === null || val === undefined) return '';\n const str = String(val);\n // CSV 转义:含逗号、引号、换行时用双引号包裹\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n })\n .join(','),\n );\n return [headerLine, ...dataLines].join('\\n');\n}\n\n/** pageSize 超限警告 */\nconst PAGE_SIZE_WARNING = (n: number) =>\n `⚠️ 当前配置的单次查询数量为 ${n} 条,超过上限 ${PAGE_SIZE_MAX} 条。大模型不擅长直接在庞大的数值矩阵中做复杂的数学运算(如精确计算长期均线、RSI、布林带等),建议:\n1. 将 mcp.pageSize 调整为 ${PAGE_SIZE_MAX} 以内(推荐 200)\n2. 使用编程脚本(Python/Node.js)配合 cyberquant-cli 处理大数据量任务\n3. 缩小查询参数范围,获取更精准的数据子集`;\n\n/** hasMore 提示 */\nconst HAS_MORE_HINT =\n '⚠️ 当前查询范围下还有更多数据未返回。建议缩小查询参数范围(如缩小日期区间、指定具体代码等)以获取精确的数据子集,大模型更适合分析精准的小数据集。';\n\n/** 无数据提示 */\nconst NO_DATA_HINT =\n '未查询到符合条件的数据。请检查查询参数是否正确,可通过 get_route_detail 工具查看该路由支持的参数说明。';\n\nexport function registerQueryDataTool(server: McpServer, state: ServerState): void {\n server.tool(\n 'query_data',\n '查询指定路由的数据,返回 CSV 格式。先用 list_routes 找路由,再用 get_route_detail 获取入参/返回字段说明,最后调用本工具查询。',\n {\n routeSlug: z.string().describe('路由标识,如 \"daily-stock\"。通过 list_routes 获取可用路由。'),\n params: z\n .record(z.union([z.string(), z.number(), z.boolean(), z.array(z.union([z.string(), z.number()]))]))\n .optional()\n .describe('查询参数,键值对透传给 API。具体参数与传值格式请先调用 get_route_detail(routeSlug) 获取,再据此组织本对象。'),\n },\n async ({ routeSlug, params }) => {\n if (!state.client || !state.config) {\n return { content: [{ type: 'text' as const, text: NO_CONFIG_HINT }] };\n }\n\n const pageSize = state.config.mcp.pageSize;\n\n // 前置校验:pageSize 上限拦截\n if (pageSize > PAGE_SIZE_MAX) {\n return {\n content: [{ type: 'text' as const, text: PAGE_SIZE_WARNING(pageSize) }],\n };\n }\n\n try {\n const res = await state.client.queryData(routeSlug, params ?? {}, pageSize);\n\n if (!res.success) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `查询失败:${res.error ?? '未知错误'}。请检查路由标识和参数是否正确。`,\n },\n ],\n };\n }\n\n const rows = res.data;\n const count = res.meta?.count ?? (Array.isArray(rows) ? rows.length : 0);\n const hasMore = res.meta?.pagination?.hasMore ?? false;\n\n // 无数据\n if (!Array.isArray(rows) || rows.length === 0) {\n return {\n content: [{ type: 'text' as const, text: NO_DATA_HINT }],\n };\n }\n\n // 拼接结果:CSV + 统计 + 引导\n const parts: string[] = [toCsv(rows), '', `本次查询返回 ${count} 条数据。`];\n if (hasMore) {\n parts.push(HAS_MORE_HINT);\n }\n\n return {\n content: [{ type: 'text' as const, text: parts.join('\\n') }],\n };\n } catch (err) {\n const msg = err instanceof Error ? err.message : '未知错误';\n return {\n content: [{ type: 'text' as const, text: `查询失败:${msg}` }],\n };\n }\n },\n );\n}\n","// ============================================================\n// Resource: cyberquant://user/profile\n// ============================================================\n\nimport type { URL } from 'node:url';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerState } from '../lib/state.js';\n\nexport function createUserProfileResource(server: McpServer, state: ServerState): void {\n server.resource(\n 'user-profile',\n 'cyberquant://user/profile',\n {\n description: '当前用户信息(等级、市场权限、到期时间、速率限制)',\n mimeType: 'application/json',\n },\n async (uri: URL) => {\n if (!state.client) {\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify({\n error: '未检测到 API Key 配置,请先调用 configure 工具完成配置。',\n }),\n },\n ],\n };\n }\n\n const res = await state.client.getProfile();\n if (!res.success || !res.data) {\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify({ error: res.error ?? '获取用户信息失败' }),\n },\n ],\n };\n }\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify(res.data, null, 2),\n },\n ],\n };\n },\n );\n}\n","// ============================================================\n// Resource: cyberquant://routes\n// ============================================================\n\nimport type { URL } from 'node:url';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerState } from '../lib/state.js';\n\nexport function createRouteListResource(server: McpServer, state: ServerState): void {\n server.resource(\n 'routes',\n 'cyberquant://routes',\n {\n description: '当前用户可用的数据路由列表',\n mimeType: 'application/json',\n },\n async (uri: URL) => {\n if (!state.client) {\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify({\n error: '未检测到 API Key 配置,请先调用 configure 工具完成配置。',\n }),\n },\n ],\n };\n }\n\n const res = await state.client.listRoutes();\n if (!res.success || !res.data) {\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify({ error: res.error ?? '获取路由列表失败' }),\n },\n ],\n };\n }\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify(res.data, null, 2),\n },\n ],\n };\n },\n );\n}\n"],"mappings":"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","names":["path","z","z","z","z"]}
|