@zltquant/zlt-mcp 0.7.4 → 0.7.6

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/config.json CHANGED
@@ -1077,7 +1077,7 @@
1077
1077
  },
1078
1078
  {
1079
1079
  "name": "mcp-get-strategy-list",
1080
- "description": "查询本地策略列表(只展示 templateVal=strategy 的数据),返回策略ID、名称、类型、本地路径等。treeId 是策略分类(folder)ID:默认 1(用户策略默认所在文件夹);传其他值只查该分类;传 0 则跨所有分类聚合并按 strategyId 去重(ZLTAF 原生 treeId=0 查不到数据,聚合由 MCP 实现)。status 固定为 0,必须显式传入。",
1080
+ "description": "查询本地策略列表(只展示 templateVal=strategy 的数据),默认只返回定位字段,其他字段用 fields 点名(见 disclosure.availableFields)。treeId 是策略分类(folder)ID:默认 1(用户策略默认所在文件夹);传其他值只查该分类;传 0 则跨所有分类聚合并按 strategyId 去重(ZLTAF 原生 treeId=0 查不到数据,聚合由 MCP 实现)。status 固定为 0,必须显式传入。",
1081
1081
  "inputSchema": {
1082
1082
  "type": "object",
1083
1083
  "properties": {
@@ -1118,7 +1118,7 @@
1118
1118
  "type": "string",
1119
1119
  "maxLength": 128
1120
1120
  },
1121
- "description": "代理层按需保留的顶层字段名,不发送给上游;省略时保留原字段。"
1121
+ "description": "代理层按需保留的顶层字段名,不发送给上游;省略时只返回默认五个字段,传入则完全按所列字段返回。"
1122
1122
  }
1123
1123
  },
1124
1124
  "required": [
@@ -1132,7 +1132,7 @@
1132
1132
  },
1133
1133
  {
1134
1134
  "name": "mcp-get-backtest-list",
1135
- "description": "查询指定策略ID数组对应的回测列表,返回回测ID、名称、回测周期、回测状态与回测时间——**不要展示收益相关的信息**。pageSize 与 runType 是固定值,必须显式传入,不要依赖 default 自动补值。",
1135
+ "description": "查询指定策略ID数组对应的回测列表——**不要展示收益相关的信息**。默认只返回定位字段(code 即 codePath),其他字段用 fields 点名(见 disclosure.availableFields)。pageSize 与 runType 必须显式传入,不要依赖 default 自动补值。",
1136
1136
  "inputSchema": {
1137
1137
  "type": "object",
1138
1138
  "properties": {
@@ -1152,11 +1152,10 @@
1152
1152
  },
1153
1153
  "pageSize": {
1154
1154
  "type": "integer",
1155
- "enum": [
1156
- 100
1157
- ],
1158
- "default": 100,
1159
- "description": "每页数量,固定为 100,必须显式传入。不要依赖 default 自动补值。"
1155
+ "minimum": 1,
1156
+ "maximum": 50,
1157
+ "default": 20,
1158
+ "description": "每页数量,传 20;最大 50,超出按 50 处理。必须显式传入,不要依赖 default 自动补值。"
1160
1159
  },
1161
1160
  "runType": {
1162
1161
  "type": "integer",
@@ -1165,6 +1164,15 @@
1165
1164
  ],
1166
1165
  "default": 2,
1167
1166
  "description": "回测运行类型,固定为 2,必须显式传入。不要依赖 default 自动补值。"
1167
+ },
1168
+ "fields": {
1169
+ "type": "array",
1170
+ "maxItems": 50,
1171
+ "items": {
1172
+ "type": "string",
1173
+ "maxLength": 128
1174
+ },
1175
+ "description": "代理层按需保留的顶层字段名,不发送给上游;省略时只返回默认八个字段,传入则完全按所列字段返回。"
1168
1176
  }
1169
1177
  },
1170
1178
  "required": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zltquant/zlt-mcp",
3
- "version": "0.7.4",
3
+ "version": "0.7.6",
4
4
  "description": "智量通 ZLT Quant MCP server(Windows x64;量化策略创建与本地回测)",
5
5
  "bin": {
6
6
  "zlt-mcp": "./zlt-mcp-proxy.bundle.cjs"
@@ -10687,6 +10687,7 @@ var CliEvidence = class {
10687
10687
  teeWrite;
10688
10688
  constructor(directory, provenance, command) {
10689
10689
  const root2 = path.resolve(directory);
10690
+ fs.mkdirSync(path.dirname(root2), { recursive: true });
10690
10691
  fs.mkdirSync(root2);
10691
10692
  this.resultFile = path.join(root2, "result.json");
10692
10693
  this.readableFile = path.join(root2, "result.readable.txt");
@@ -24642,6 +24643,49 @@ var MAX_FIELDS = 50;
24642
24643
  var MAX_FIELD_NAME_BYTES = 128;
24643
24644
  var MAX_UNKNOWN_FIELDS = 50;
24644
24645
  var MAX_OMITTED_FIELDS = 100;
24646
+ var DEFAULT_FIELDS = {
24647
+ "mcp-get-strategy-list": ["strategyId", "strategyName", "localPath", "tags", "updateTime"],
24648
+ "mcp-get-backtest-list": ["runId", "runName", "strategyId", "status", "startTime", "endTime", "errorMsg", "code"]
24649
+ };
24650
+ var FIELD_PROJECTION_TOOLS = /* @__PURE__ */ new Set(["mcp-get-backtest-list"]);
24651
+ function selectFields(toolName, args, source) {
24652
+ const requestedFields = Array.isArray(args.fields) ? [...new Set(args.fields.filter((field) => typeof field === "string"))].slice(0, MAX_FIELDS).map((field) => field.slice(0, MAX_FIELD_NAME_BYTES)) : [];
24653
+ const allAvailableFields = [...new Set(source.flatMap((row) => isRecord4(row) ? Object.keys(row) : []))];
24654
+ const defaultFields = requestedFields.length ? [] : (DEFAULT_FIELDS[toolName] ?? []).filter((field) => allAvailableFields.includes(field));
24655
+ const fields = requestedFields.length ? requestedFields : defaultFields;
24656
+ const unknownFields = requestedFields.filter((field) => !allAvailableFields.includes(field));
24657
+ return { requestedFields, allAvailableFields, defaultFields, fields, unknownFields };
24658
+ }
24659
+ function project(row, fields) {
24660
+ return isRecord4(row) && fields.length ? Object.fromEntries(Object.entries(row).filter(([key]) => fields.includes(key))) : row;
24661
+ }
24662
+ function shapeFieldProjection(tool, args, result) {
24663
+ if (!FIELD_PROJECTION_TOOLS.has(tool.name) || result.isError) return result;
24664
+ try {
24665
+ const envelope = JSON.parse(result.content[0]?.text ?? "");
24666
+ if (!isRecord4(envelope) || envelope.disclosure || Number(envelope.ErrNo ?? 0) !== 0) return result;
24667
+ if (!Array.isArray(envelope.Data)) return result;
24668
+ const { requestedFields, allAvailableFields, defaultFields, fields, unknownFields } = selectFields(tool.name, args, envelope.Data);
24669
+ const pageNum = integer2(args.pageNum, 1, 1, Number.MAX_SAFE_INTEGER);
24670
+ const pageSize = integer2(args.pageSize, 0, 0, Number.MAX_SAFE_INTEGER);
24671
+ const pagingIgnored = pageSize > 0 && envelope.Data.length > pageSize;
24672
+ const fullPage = pageSize > 0 && envelope.Data.length === pageSize;
24673
+ const disclosure = {
24674
+ pageNum,
24675
+ pageSize,
24676
+ returned: envelope.Data.length,
24677
+ possiblyMore: fullPage,
24678
+ nextPageNum: fullPage ? pageNum + 1 : null,
24679
+ ...pagingIgnored ? { pagingIgnored: true } : {},
24680
+ availableFields: allAvailableFields.slice(0, MAX_FIELDS),
24681
+ ...defaultFields.length ? { defaultFields } : {},
24682
+ ...requestedFields.length && unknownFields.length ? { unknownFields: unknownFields.slice(0, MAX_UNKNOWN_FIELDS) } : {}
24683
+ };
24684
+ return replace(envelope, envelope.Data.map((row) => project(row, fields)), disclosure, result);
24685
+ } catch {
24686
+ return result;
24687
+ }
24688
+ }
24645
24689
  function shapeListResultDisclosure(tool, args, result) {
24646
24690
  if (!LIST_DISCLOSURE_TOOLS.has(tool.name) || result.isError) return result;
24647
24691
  try {
@@ -24651,8 +24695,7 @@ function shapeListResultDisclosure(tool, args, result) {
24651
24695
  const source = envelope.Data;
24652
24696
  const offset = integer2(args.offset, 0, 0, Number.MAX_SAFE_INTEGER);
24653
24697
  const limit = integer2(args.limit, 20, 1, 50);
24654
- const fields = Array.isArray(args.fields) ? [...new Set(args.fields.filter((field) => typeof field === "string"))].slice(0, MAX_FIELDS).map((field) => field.slice(0, MAX_FIELD_NAME_BYTES)) : [];
24655
- const allAvailableFields = [...new Set(source.flatMap((row) => isRecord4(row) ? Object.keys(row) : []))];
24698
+ const { requestedFields, allAvailableFields, defaultFields, fields, unknownFields } = selectFields(tool.name, args, source);
24656
24699
  const availableFields = allAvailableFields.slice(0, MAX_FIELDS).map((field) => field.slice(0, MAX_FIELD_NAME_BYTES));
24657
24700
  const rows = [];
24658
24701
  let nextCursor = offset;
@@ -24665,13 +24708,11 @@ function shapeListResultDisclosure(tool, args, result) {
24665
24708
  hasMore: nextCursor < source.length,
24666
24709
  nextOffset: nextCursor < source.length ? nextCursor : null,
24667
24710
  availableFields,
24668
- ...fields.length ? (() => {
24669
- const unknownFields = fields.filter((field) => !allAvailableFields.includes(field));
24670
- return {
24671
- unknownFields: unknownFields.slice(0, MAX_UNKNOWN_FIELDS).map((field) => field.slice(0, MAX_FIELD_NAME_BYTES)),
24672
- ...unknownFields.length > MAX_UNKNOWN_FIELDS ? { unknownFieldsTruncated: true } : {}
24673
- };
24674
- })() : {},
24711
+ ...defaultFields.length ? { defaultFields } : {},
24712
+ ...requestedFields.length ? {
24713
+ unknownFields: unknownFields.slice(0, MAX_UNKNOWN_FIELDS).map((field) => field.slice(0, MAX_FIELD_NAME_BYTES)),
24714
+ ...unknownFields.length > MAX_UNKNOWN_FIELDS ? { unknownFieldsTruncated: true } : {}
24715
+ } : {},
24675
24716
  ...allAvailableFields.length > availableFields.length ? { availableFieldsTruncated: true } : {},
24676
24717
  ...omittedFields.length ? {
24677
24718
  omittedFields: omittedFields.slice(0, MAX_OMITTED_FIELDS).map((entry) => ({ ...entry, field: entry.field.slice(0, MAX_FIELD_NAME_BYTES) })),
@@ -25366,6 +25407,9 @@ function stripControlParams(args, toolName) {
25366
25407
  delete filtered.limit;
25367
25408
  delete filtered.fields;
25368
25409
  }
25410
+ if (toolName && FIELD_PROJECTION_TOOLS.has(toolName)) {
25411
+ delete filtered.fields;
25412
+ }
25369
25413
  if (toolName && DATA_DISCLOSURE_TOOLS.has(toolName)) {
25370
25414
  delete filtered.offset;
25371
25415
  delete filtered.limit;
@@ -27387,7 +27431,7 @@ function prepareToolArgs(tool, args, contexts) {
27387
27431
  const oversizedWindow = refuseOversizedBacktestPreloadWindow(tool, dated);
27388
27432
  if (oversizedWindow) return oversizedWindow;
27389
27433
  const named = normalizeBacktestRunName(tool, dated);
27390
- const prepared = stripEmptyLegacyUserId(tool, named);
27434
+ const prepared = clampBacktestListPageSize(tool, stripEmptyLegacyUserId(tool, named));
27391
27435
  const refused = refuseMaskedFuturesBacktest(tool, prepared);
27392
27436
  if (refused) return refused;
27393
27437
  capturePendingBacktestAttempt(tool, prepared, contexts);
@@ -27477,7 +27521,11 @@ function shapeToolResult(tool, args, result, session) {
27477
27521
  const diagnosed = tool.name === "mcp-factor-calculate-detail" ? diagnoseFactorFailure(withWorkspace) : withWorkspace;
27478
27522
  const distilled = distillFactorDetectReport(tool, args, diagnosed);
27479
27523
  const enriched = injectDebugRunId(tool, injectStrategyDocsHint(tool, distilled, session.docsPath));
27480
- return shapeDataResultDisclosure(tool, args, shapeListResultDisclosure(tool, args, enriched));
27524
+ return shapeFieldProjection(
27525
+ tool,
27526
+ args,
27527
+ shapeDataResultDisclosure(tool, args, shapeListResultDisclosure(tool, args, enriched))
27528
+ );
27481
27529
  }
27482
27530
  function backtestStatusNumber(status) {
27483
27531
  return typeof status === "number" || typeof status === "string" && status.trim() !== "" ? Number(status) : NaN;
@@ -27717,6 +27765,13 @@ function subtractMonthsClamped(value, months) {
27717
27765
  target.setDate(Math.min(day, lastDay));
27718
27766
  return target;
27719
27767
  }
27768
+ var MAX_BACKTEST_LIST_PAGE_SIZE = 50;
27769
+ function clampBacktestListPageSize(tool, args) {
27770
+ if (tool.name !== "mcp-get-backtest-list") return args;
27771
+ const pageSize = args.pageSize;
27772
+ if (typeof pageSize !== "number" || !Number.isFinite(pageSize) || pageSize <= MAX_BACKTEST_LIST_PAGE_SIZE) return args;
27773
+ return { ...args, pageSize: MAX_BACKTEST_LIST_PAGE_SIZE };
27774
+ }
27720
27775
  function stripEmptyLegacyUserId(tool, args) {
27721
27776
  if (!toolHasUserIdField(tool)) return args;
27722
27777
  if (!Object.prototype.hasOwnProperty.call(args, "userId")) return args;