cyberquant-mcp 0.1.2 → 0.1.4

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 CHANGED
@@ -10,14 +10,16 @@ CyberQuant 数据共享平台的 MCP(Model Context Protocol)服务器,让
10
10
 
11
11
  ## 功能特性
12
12
 
13
- ### MCP Tools(4 个)
13
+ ### MCP Tools(6 个)
14
14
 
15
15
  | 工具 | 说明 |
16
16
  |------|------|
17
17
  | `configure` | 配置 API Key,首次使用时调用 |
18
18
  | `list_routes` | 列出当前用户可用的数据路由**目录**(仅元信息,不含入参/返回字段) |
19
19
  | `get_route_detail` | 查询单个路由的入参/返回字段详情,由大模型据此自行组织 `query_data` 参数 |
20
- | `query_data` | 查询指定路由数据,返回 **CSV 格式**(节省 token) |
20
+ | `query_data` | 查询指定数据路由的数据,返回 **CSV 格式**(节省 token) |
21
+ | `get_routes_metadata` | 查询当前用户可访问的数据路由元数据列表,返回压缩 JSON |
22
+ | `get_user_profile` | 查询当前 API Key 对应用户的账户与权限信息,返回压缩 JSON |
21
23
 
22
24
  ### MCP Resources(2 个)
23
25
 
@@ -29,7 +31,8 @@ CyberQuant 数据共享平台的 MCP(Model Context Protocol)服务器,让
29
31
  ### 设计亮点
30
32
 
31
33
  - **CSV 输出**:相比 JSON 节省 40–60% token,表格结构天然适合 AI 分析
32
- - **路由目录 + 按需详情**:`list_routes` 仅输出目录级元信息,`get_route_detail` 按需取详情,避免上百路由全量入参一次性占满上下文(路由列表内存缓存 2 分钟,list→detail 连贯调用零重复网络)
34
+ - **路由目录 + 按需详情**:`list_routes` 仅输出目录级元信息,`get_route_detail` 按需取详情,避免上百路由全量入参一次性占满上下文(路由元数据按账号按日文件缓存,减少重复拉取)
35
+ - **Resource 兼容工具**:`get_routes_metadata` 与 `get_user_profile` 让无法稳定读取 MCP Resources 的 AI 助手也能主动获取完整上下文
33
36
  - **自然语言引导**:数据返回附带提示,引导 AI 缩小查询范围而非暴力翻页
34
37
  - **pageSize 上限保护**:超过 1000 条自动拦截,避免 AI 处理超大数据集
35
38
  - **运行时配置**:通过 `configure` 工具动态更新 API Key,无需重启
@@ -94,7 +97,7 @@ AI 会依次调用 `list_routes` → `get_route_detail` → `query_data`,并
94
97
  | 文档 | 说明 |
95
98
  |------|------|
96
99
  | [配置说明](./docs/configuration.md) | 配置文件格式与字段说明 |
97
- | [工具说明](./docs/tools.md) | 三个 MCP Tool 的详细文档 |
100
+ | [工具说明](./docs/tools.md) | MCP Tools 的详细文档 |
98
101
  | [Resources 说明](./docs/resources.md) | MCP Resources 介绍 |
99
102
  | [故障排查](./docs/troubleshooting.md) | 常见问题与解决方案 |
100
103
  | [使用示例](./examples/usage-examples.md) | 典型对话场景 |
package/dist/index.js CHANGED
@@ -82,6 +82,62 @@ function saveConfig(apiKey, endpoint) {
82
82
  };
83
83
  }
84
84
 
85
+ // src/lib/api-client.ts
86
+ import { createHash } from "crypto";
87
+
88
+ // src/lib/daily-cache.ts
89
+ import fs2 from "fs";
90
+ import path2 from "path";
91
+ import os2 from "os";
92
+ var CACHE_DIR = path2.join(os2.homedir(), ".cyberquant", "cache");
93
+ function startOfTodayMs() {
94
+ const today = /* @__PURE__ */ new Date();
95
+ today.setHours(0, 0, 0, 0);
96
+ return today.getTime();
97
+ }
98
+ function pruneStaleCache() {
99
+ try {
100
+ const cutoff = startOfTodayMs();
101
+ for (const name of fs2.readdirSync(CACHE_DIR)) {
102
+ if (!name.endsWith(".json")) continue;
103
+ const filePath = path2.join(CACHE_DIR, name);
104
+ try {
105
+ if (fs2.statSync(filePath).mtimeMs < cutoff) {
106
+ fs2.unlinkSync(filePath);
107
+ }
108
+ } catch {
109
+ }
110
+ }
111
+ } catch {
112
+ }
113
+ }
114
+ function readDailyJsonCache(fileName, validate) {
115
+ const filePath = path2.join(CACHE_DIR, fileName);
116
+ try {
117
+ const stat = fs2.statSync(filePath);
118
+ if (stat.mtimeMs < startOfTodayMs()) return null;
119
+ const raw = fs2.readFileSync(filePath, "utf-8");
120
+ const value = JSON.parse(raw);
121
+ return validate(value) ? value : null;
122
+ } catch {
123
+ return null;
124
+ }
125
+ }
126
+ function writeDailyJsonCache(fileName, value) {
127
+ try {
128
+ fs2.mkdirSync(CACHE_DIR, { recursive: true });
129
+ const filePath = path2.join(CACHE_DIR, fileName);
130
+ const tmpPath = `${filePath}.${process.pid}.tmp`;
131
+ fs2.writeFileSync(tmpPath, JSON.stringify(value), "utf-8");
132
+ pruneStaleCache();
133
+ fs2.renameSync(tmpPath, filePath);
134
+ } catch (err) {
135
+ const msg = err instanceof Error ? err.message : String(err);
136
+ process.stderr.write(`[cyberquant-mcp] \u5199\u5165\u7F13\u5B58\u5931\u8D25\uFF1A${msg}
137
+ `);
138
+ }
139
+ }
140
+
85
141
  // src/lib/errors.ts
86
142
  var McpError = class extends Error {
87
143
  constructor(message) {
@@ -112,21 +168,28 @@ function mapApiError(status, detail) {
112
168
  }
113
169
 
114
170
  // src/lib/api-client.ts
115
- var ROUTES_CACHE_TTL_MS = 12e4;
171
+ function isRoutesResponse(value) {
172
+ if (!value || typeof value !== "object") return false;
173
+ const res = value;
174
+ return res.success === true && Array.isArray(res.data);
175
+ }
176
+ function routesCacheFile(endpoint, apiKey) {
177
+ const key = createHash("sha256").update(`${endpoint}
178
+ ${apiKey}`).digest("hex").slice(0, 12);
179
+ return `routes-${key}.json`;
180
+ }
116
181
  var ApiClient = class {
117
182
  endpoint;
118
183
  apiKey;
119
184
  timeout;
120
- /** 路由列表内存缓存(仅成功响应才回填,失败不污染) */
121
- routesCache = null;
122
185
  constructor(config) {
123
186
  this.endpoint = config.endpoint;
124
187
  this.apiKey = config.apiKey;
125
188
  this.timeout = config.mcp.timeout;
126
189
  }
127
190
  /** 通用请求(含 429/503 重试) */
128
- async request(path2) {
129
- const url = `${this.endpoint}${path2}`;
191
+ async request(path3) {
192
+ const url = `${this.endpoint}${path3}`;
130
193
  const maxRetries = 3;
131
194
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
132
195
  const res = await fetch(url, {
@@ -162,15 +225,14 @@ var ApiClient = class {
162
225
  async getProfile() {
163
226
  return this.request("/api/v1/me");
164
227
  }
165
- /** 获取可用路由列表(命中 2 分钟内存缓存时零网络) */
228
+ /** 获取可用路由列表(命中当日文件缓存时零网络) */
166
229
  async listRoutes() {
167
- const now = Date.now();
168
- if (this.routesCache && now < this.routesCache.expiresAt) {
169
- return this.routesCache.data;
170
- }
230
+ const cacheFile = routesCacheFile(this.endpoint, this.apiKey);
231
+ const cached = readDailyJsonCache(cacheFile, isRoutesResponse);
232
+ if (cached) return cached;
171
233
  const res = await this.request("/api/v1/api-list");
172
234
  if (res.success) {
173
- this.routesCache = { data: res, expiresAt: now + ROUTES_CACHE_TTL_MS };
235
+ writeDailyJsonCache(cacheFile, res);
174
236
  }
175
237
  return res;
176
238
  }
@@ -201,9 +263,9 @@ import { z } from "zod";
201
263
  function registerConfigureTool(server, state) {
202
264
  server.tool(
203
265
  "configure",
204
- "\u914D\u7F6E API Key \u4EE5\u8BBF\u95EE\u6570\u636E\u670D\u52A1\u3002\u9996\u6B21\u4F7F\u7528\u65F6\u5FC5\u987B\u8C03\u7528\u6B64\u5DE5\u5177\u5B8C\u6210\u914D\u7F6E\u3002endpoint \u9ED8\u8BA4\u4E3A https://api.cyberspace2077.com\u3002",
266
+ "\u914D\u7F6E API Key \u4EE5\u8BBF\u95EE\u6570\u636E\u670D\u52A1\u3002\u8C03\u7528\u5176\u4ED6\u4EFB\u4F55\u5DE5\u5177\u524D\u5FC5\u987B\u5148\u5B8C\u6210\u914D\u7F6E\uFF0C\u5426\u5219\u4F1A\u6536\u5230\u672A\u914D\u7F6E\u63D0\u793A\u3002endpoint \u9ED8\u8BA4 https://api.cyberspace2077.com\uFF0C\u901A\u5E38\u65E0\u9700\u4F20\u5165\u3002",
205
267
  {
206
- apiKey: z.string().describe("API Key\uFF0C\u683C\u5F0F\u4E3A sk_live_xxx \u6216 sk_test_xxx"),
268
+ apiKey: z.string().describe("API Key\uFF0C\u683C\u5F0F\u4E3A sk_live_xxx"),
207
269
  endpoint: z.string().optional().describe(`API Gateway \u5730\u5740\uFF0C\u9ED8\u8BA4 ${DEFAULT_ENDPOINT}`)
208
270
  },
209
271
  async ({ apiKey, endpoint }) => {
@@ -258,7 +320,7 @@ var NEXT_STEP_HINT = [
258
320
  function registerListRoutesTool(server, state) {
259
321
  server.tool(
260
322
  "list_routes",
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",
323
+ "\u5217\u51FA\u5F53\u524D\u7528\u6237\u53EF\u7528\u7684\u6570\u636E\u8DEF\u7531\u76EE\u5F55\uFF08\u8F7B\u91CF\uFF0C\u4EC5 routeSlug\u3001\u540D\u79F0\u3001\u8BF4\u660E\u3001\u5206\u7C7B\uFF0C\u4E0D\u542B\u5165\u53C2/\u8FD4\u56DE\u5B57\u6BB5\uFF09\u3002",
262
324
  {},
263
325
  async () => {
264
326
  if (!state.client) {
@@ -335,7 +397,7 @@ function formatResponseParams(params) {
335
397
  function registerGetRouteDetailTool(server, state) {
336
398
  server.tool(
337
399
  "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",
400
+ "\u67E5\u8BE2\u5355\u4E2A\u8DEF\u7531\u7684\u5165\u53C2\u4E0E\u8FD4\u56DE\u5B57\u6BB5\u8BE6\u60C5\uFF0C\u5E76\u9644\u5E26\u901A\u7528\u4F20\u503C\u683C\u5F0F\u8BF4\u660E\u3002",
339
401
  {
340
402
  routeSlug: z2.string().describe('\u8DEF\u7531\u6807\u8BC6\uFF0C\u5982 "daily-stock"\u3002\u901A\u8FC7 list_routes \u83B7\u53D6\u53EF\u7528 routeSlug\u3002')
341
403
  },
@@ -422,7 +484,7 @@ var NO_DATA_HINT = "\u672A\u67E5\u8BE2\u5230\u7B26\u5408\u6761\u4EF6\u7684\u6570
422
484
  function registerQueryDataTool(server, state) {
423
485
  server.tool(
424
486
  "query_data",
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",
487
+ "\u6309\u53C2\u6570\u67E5\u8BE2\u6307\u5B9A\u8DEF\u7531\u7684\u6570\u636E\uFF0C\u8FD4\u56DE CSV \u683C\u5F0F\u3002\u9700\u5148\u7528 `get_route_detail` \u83B7\u53D6\u5165\u53C2/\u8FD4\u56DE\u5B57\u6BB5\u8BF4\u660E\uFF0C\u518D\u636E\u6B64\u7EC4\u7EC7 params \u8C03\u7528\u672C\u5DE5\u5177\u3002",
426
488
  {
427
489
  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
490
  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")
@@ -474,6 +536,76 @@ function registerQueryDataTool(server, state) {
474
536
  );
475
537
  }
476
538
 
539
+ // src/tools/config-info.ts
540
+ function registerConfigInfoTools(server, state) {
541
+ server.tool(
542
+ "get_routes_metadata",
543
+ "\u4E00\u6B21\u6027\u8FD4\u56DE\u6240\u6709\u8DEF\u7531\u7684\u5B8C\u6574\u5143\u6570\u636E\uFF08\u538B\u7F29 JSON\uFF0C\u542B routeSlug\u3001\u540D\u79F0\u3001\u8BF4\u660E\u3001\u5206\u7C7B\u3001\u5E02\u573A\u7C7B\u578B\u3001\u6743\u9650\u7B49\u7EA7\u3001queryParams\u3001responseParams\uFF09\u3002token \u5F00\u9500\u8F83\u5927\uFF0C\u4EC5\u5F53\u9700\u8981\u4E00\u6B21\u6027\u62FF\u5230\u5168\u90E8\u63A5\u53E3\u7684\u5B8C\u6574\u5B57\u6BB5\u5B9A\u4E49\u3001\u6784\u5EFA\u5B8C\u6574\u7D22\u5F15\u65F6\u4F7F\u7528\u3002",
544
+ {},
545
+ async () => {
546
+ if (!state.client) {
547
+ return { content: [{ type: "text", text: NO_CONFIG_HINT }] };
548
+ }
549
+ try {
550
+ const res = await state.client.listRoutes();
551
+ if (!res.success || !res.data) {
552
+ return {
553
+ content: [
554
+ {
555
+ type: "text",
556
+ text: `\u83B7\u53D6\u5143\u6570\u636E\u914D\u7F6E\u5931\u8D25\uFF1A${res.error ?? "\u672A\u77E5\u9519\u8BEF"}\u3002\u8BF7\u68C0\u67E5 API Key \u914D\u7F6E\u662F\u5426\u6B63\u786E\u3002`
557
+ }
558
+ ]
559
+ };
560
+ }
561
+ return {
562
+ content: [{ type: "text", text: JSON.stringify(res.data) }]
563
+ };
564
+ } catch (err) {
565
+ const msg = err instanceof Error ? err.message : "\u672A\u77E5\u9519\u8BEF";
566
+ return {
567
+ content: [
568
+ { type: "text", text: `\u83B7\u53D6\u5143\u6570\u636E\u914D\u7F6E\u5931\u8D25\uFF1A${msg}` }
569
+ ]
570
+ };
571
+ }
572
+ }
573
+ );
574
+ server.tool(
575
+ "get_user_profile",
576
+ "\u67E5\u8BE2\u5F53\u524D API Key \u7684\u8D26\u6237\u4E0E\u6743\u9650\u4FE1\u606F\uFF08\u538B\u7F29 JSON\uFF09\uFF1A\u90AE\u7BB1\u3001\u8BA2\u9605\u7B49\u7EA7\u3001\u53EF\u7528\u5E02\u573A\u3001\u5230\u671F\u65F6\u95F4\u3001\u8D26\u6237\u662F\u5426\u6709\u6548\u3001\u901F\u7387\u9650\u5236\u914D\u7F6E\u3002\u4EC5\u5728\u9700\u8981\u6838\u5BF9\u8D26\u6237\u72B6\u6001\u3001\u53EF\u7528\u5E02\u573A\u8303\u56F4\u6216\u67E5\u770B\u9650\u6D41\u914D\u989D\u65F6\u8C03\u7528\u3002",
577
+ {},
578
+ async () => {
579
+ if (!state.client) {
580
+ return { content: [{ type: "text", text: NO_CONFIG_HINT }] };
581
+ }
582
+ try {
583
+ const res = await state.client.getProfile();
584
+ if (!res.success || !res.data) {
585
+ return {
586
+ content: [
587
+ {
588
+ type: "text",
589
+ text: `\u83B7\u53D6\u7528\u6237\u914D\u7F6E\u5931\u8D25\uFF1A${res.error ?? "\u672A\u77E5\u9519\u8BEF"}\u3002\u8BF7\u68C0\u67E5 API Key \u914D\u7F6E\u662F\u5426\u6B63\u786E\u3002`
590
+ }
591
+ ]
592
+ };
593
+ }
594
+ return {
595
+ content: [{ type: "text", text: JSON.stringify(res.data) }]
596
+ };
597
+ } catch (err) {
598
+ const msg = err instanceof Error ? err.message : "\u672A\u77E5\u9519\u8BEF";
599
+ return {
600
+ content: [
601
+ { type: "text", text: `\u83B7\u53D6\u7528\u6237\u914D\u7F6E\u5931\u8D25\uFF1A${msg}` }
602
+ ]
603
+ };
604
+ }
605
+ }
606
+ );
607
+ }
608
+
477
609
  // src/resources/user-profile.ts
478
610
  function createUserProfileResource(server, state) {
479
611
  server.resource(
@@ -574,6 +706,7 @@ function createServer(state) {
574
706
  registerListRoutesTool(server, state);
575
707
  registerGetRouteDetailTool(server, state);
576
708
  registerQueryDataTool(server, state);
709
+ registerConfigInfoTools(server, state);
577
710
  createUserProfileResource(server, state);
578
711
  createRouteListResource(server, state);
579
712
  return server;
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/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"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/lib/config.ts","../src/types/index.ts","../src/lib/api-client.ts","../src/lib/daily-cache.ts","../src/lib/errors.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/tools/config-info.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// API Gateway HTTP 客户端(含重试 + 超时)\n// ============================================================\n\nimport { createHash } from 'node:crypto';\nimport type {\n AppConfig,\n ApiResponse,\n UserProfile,\n RouteInfo,\n RouteListMeta,\n} from '../types/index.js';\nimport { readDailyJsonCache, writeDailyJsonCache } from './daily-cache.js';\nimport { mapApiError } from './errors.js';\n\ntype RoutesResponse = ApiResponse<RouteInfo[]> & { meta?: RouteListMeta };\n\nfunction isRoutesResponse(value: unknown): value is RoutesResponse {\n if (!value || typeof value !== 'object') return false;\n const res = value as Partial<RoutesResponse>;\n return res.success === true && Array.isArray(res.data);\n}\n\nfunction routesCacheFile(endpoint: string, apiKey: string): string {\n const key = createHash('sha256').update(`${endpoint}\\n${apiKey}`).digest('hex').slice(0, 12);\n return `routes-${key}.json`;\n}\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<RoutesResponse> {\n const cacheFile = routesCacheFile(this.endpoint, this.apiKey);\n const cached = readDailyJsonCache(cacheFile, isRoutesResponse);\n if (cached) return cached;\n\n const res = await this.request<RoutesResponse>('/api/v1/api-list');\n\n // 仅成功响应才写缓存,错误响应原样透传给上层处理\n if (res.success) {\n writeDailyJsonCache(cacheFile, res);\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// 按日 JSON 文件缓存 —— ~/.cyberquant/cache/\n// ============================================================\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst CACHE_DIR = path.join(os.homedir(), '.cyberquant', 'cache');\n\n/** 今日 00:00(本地时间)时间戳 */\nfunction startOfTodayMs(): number {\n const today = new Date();\n today.setHours(0, 0, 0, 0);\n return today.getTime();\n}\n\n/** 清理当日已过期的 JSON 缓存文件(mtime 早于今日 00:00)\n *\n * 目录内仅存放按日 JSON 缓存,故按 .json 后缀 + mtime 判断;\n * 写入用的 .tmp 临时文件后缀不同,不会被误删。清理失败不影响主流程。\n */\nfunction pruneStaleCache(): void {\n try {\n const cutoff = startOfTodayMs();\n for (const name of fs.readdirSync(CACHE_DIR)) {\n if (!name.endsWith('.json')) continue;\n const filePath = path.join(CACHE_DIR, name);\n try {\n if (fs.statSync(filePath).mtimeMs < cutoff) {\n fs.unlinkSync(filePath);\n }\n } catch {\n // 单个文件清理失败(如并发删除),跳过该文件\n }\n }\n } catch {\n // 目录不存在或读失败,忽略\n }\n}\n\n/** 读取当日有效 JSON 缓存,文件过期/损坏/结构不符时返回 null */\nexport function readDailyJsonCache<T>(\n fileName: string,\n validate: (value: unknown) => value is T,\n): T | null {\n const filePath = path.join(CACHE_DIR, fileName);\n\n try {\n const stat = fs.statSync(filePath);\n if (stat.mtimeMs < startOfTodayMs()) return null;\n\n const raw = fs.readFileSync(filePath, 'utf-8');\n const value: unknown = JSON.parse(raw);\n return validate(value) ? value : null;\n } catch {\n return null;\n }\n}\n\n/** 写入 JSON 缓存,并顺手清理当日已过期的旧缓存;缓存失败不影响主流程 */\nexport function writeDailyJsonCache(fileName: string, value: unknown): void {\n try {\n fs.mkdirSync(CACHE_DIR, { recursive: true });\n\n const filePath = path.join(CACHE_DIR, fileName);\n const tmpPath = `${filePath}.${process.pid}.tmp`;\n fs.writeFileSync(tmpPath, JSON.stringify(value), 'utf-8');\n\n // 先清理当日已过期的旧缓存,再落地新缓存,确保最终目录状态干净\n pruneStaleCache();\n\n fs.renameSync(tmpPath, filePath);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[cyberquant-mcp] 写入缓存失败:${msg}\\n`);\n }\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// 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 { registerConfigInfoTools } from './tools/config-info.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 registerConfigInfoTools(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'),\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、名称、说明、分类,不含入参/返回字段)。',\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 '查询单个路由的入参与返回字段详情,并附带通用传值格式说明。',\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 格式。需先用 `get_route_detail` 获取入参/返回字段说明,再据此组织 params 调用本工具。',\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// Tools: 配置查询 —— 元数据配置 + 用户配置\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';\n\nexport function registerConfigInfoTools(server: McpServer, state: ServerState): void {\n server.tool(\n 'get_routes_metadata',\n '一次性返回所有路由的完整元数据(压缩 JSON,含 routeSlug、名称、说明、分类、市场类型、权限等级、queryParams、responseParams)。token 开销较大,仅当需要一次性拿到全部接口的完整字段定义、构建完整索引时使用。',\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 return {\n content: [{ type: 'text' as const, text: JSON.stringify(res.data) }],\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 server.tool(\n 'get_user_profile',\n '查询当前 API Key 的账户与权限信息(压缩 JSON):邮箱、订阅等级、可用市场、到期时间、账户是否有效、速率限制配置。仅在需要核对账户状态、可用市场范围或查看限流配额时调用。',\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.getProfile();\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 return {\n content: [{ type: 'text' as const, text: JSON.stringify(res.data) }],\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// 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":["fs","path","os","path","z","z","z","z"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyberquant-mcp",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "MCP Server for CyberQuant Data Sharing Platform",
5
5
  "type": "module",
6
6
  "bin": {