@zltquant/zlt-mcp 0.4.2 → 0.5.0-beta.1
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 +6 -0
- package/config.json +4 -4
- package/package.json +1 -1
- package/zlt-mcp-proxy.bundle.cjs +119 -11
package/README.md
CHANGED
|
@@ -14,6 +14,12 @@ npx -y @zltquant/zlt-mcp list --json
|
|
|
14
14
|
npx -y @zltquant/zlt-mcp call zlt-runtime-doctor --args-json '{}'
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
业务调用支持 `call --output-json <新文件路径>`:完整 JSON 写入文件,stdout 返回含
|
|
18
|
+
`ok`、`tool`、`resultFile`、`cliProvenance`、精简 `data` 的回执。
|
|
19
|
+
输出目录须已存在,已有文件会在执行工具前报错;每次使用新文件名。完整参数与大段数据从结果文件读取。
|
|
20
|
+
stderr 保留诊断,失败返回非零退出码。工具执行后若写文件失败,完整结果会输出到 stdout;先核对结果,不要自动重复执行。
|
|
21
|
+
不传该参数时保持原有完整 stdout。
|
|
22
|
+
|
|
17
23
|
其他支持原生 MCP 的客户端(Trae / Claude Code / Cursor 等)可以使用下面的配置:
|
|
18
24
|
|
|
19
25
|
```json
|
package/config.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"name": "mcp-strategy-get-template-list",
|
|
24
|
-
"description": "
|
|
24
|
+
"description": "获取平台策略参数模板列表。创建策略前应先调用本工具,只能从实时返回的条目取 val 作为 templateItemVal,不要编造或硬编码模板标识。用户明确指定的模板优先;用户只说「推荐策略并回测」等委托选择请求时,agent 可根据返回的名称、说明和可用标签选择合适模板,无需再要求用户选择。没有其他偏好时,优先选择实时返回的带平台推荐标签且适合的日线模板;没有返回推荐标记时,若列表中存在且适合用户需求,可选择「均线择时」作为后备;否则从当前列表选择其他合适模板并简要说明理由。随后调用 mcp-create-strategy-with-template 创建。",
|
|
25
25
|
"inputSchema": {
|
|
26
26
|
"type": "object",
|
|
27
27
|
"properties": {
|
|
@@ -47,13 +47,13 @@
|
|
|
47
47
|
},
|
|
48
48
|
{
|
|
49
49
|
"name": "mcp-create-strategy-with-template",
|
|
50
|
-
"description": "
|
|
50
|
+
"description": "基于平台策略模板创建本地策略。先用 mcp-strategy-get-template-list 取实时列表,把所选条目的 val 填入 templateItemVal;templateVal 固定为 strategy。用户明确指定的模板优先;用户委托推荐时允许 agent 代选,无需再要求用户选择。名称默认用「模板名称+日期时间(YYMMDD-HHMM)」,允许用户修改;用户未指定名称时直接生成唯一名称——ZLTAF 对重名策略会静默不创建并返回空结果,本工具已将其转为明确的 ErrNo 10051,遇到就改用唯一名称重试,**切勿改为复用既有同名策略**。\n\n返回含 strategyId 与 localPath,请直接使用,不要再自行查询策略列表按名称猜测。",
|
|
51
51
|
"inputSchema": {
|
|
52
52
|
"type": "object",
|
|
53
53
|
"properties": {
|
|
54
54
|
"name": {
|
|
55
55
|
"type": "string",
|
|
56
|
-
"description": "
|
|
56
|
+
"description": "策略名称。默认使用“模板名称+日期时间(YYMMDD-HHMM)”生成唯一名称,允许用户修改;用户未指定名称时由 agent 直接生成,调用时传入该名称。"
|
|
57
57
|
},
|
|
58
58
|
"templateVal": {
|
|
59
59
|
"type": "string",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
},
|
|
66
66
|
"templateItemVal": {
|
|
67
67
|
"type": "string",
|
|
68
|
-
"description": "
|
|
68
|
+
"description": "用户选择或委托 agent 代选的模板 val,必须来自 mcp-strategy-get-template-list 实时返回的模板条目;不要编造或硬编码。用户明确指定的模板优先。"
|
|
69
69
|
},
|
|
70
70
|
"userId": {
|
|
71
71
|
"type": "string",
|
package/package.json
CHANGED
package/zlt-mcp-proxy.bundle.cjs
CHANGED
|
@@ -23302,14 +23302,14 @@ function shapeToolResult(tool, args, result, session) {
|
|
|
23302
23302
|
session.activeBacktestRunIds.delete(args.runId);
|
|
23303
23303
|
}
|
|
23304
23304
|
const echoed = echoResolvedBacktestConfig(tool, args, result, session.backtestRunContexts);
|
|
23305
|
-
const guarded = applyBacktestResultGuards(
|
|
23305
|
+
const guarded = annotateBacktestOutcome(tool, applyBacktestResultGuards(
|
|
23306
23306
|
tool,
|
|
23307
23307
|
args,
|
|
23308
23308
|
echoed,
|
|
23309
23309
|
session.backtestPollStates,
|
|
23310
23310
|
session.backtestRunContexts,
|
|
23311
23311
|
session.activeBacktestRunIds
|
|
23312
|
-
);
|
|
23312
|
+
));
|
|
23313
23313
|
const activeChange = classifyActiveBacktestChange(tool, args, guarded);
|
|
23314
23314
|
if (activeChange) {
|
|
23315
23315
|
if (activeChange.active) session.activeBacktestRunIds.add(activeChange.runId);
|
|
@@ -23323,6 +23323,38 @@ function shapeToolResult(tool, args, result, session) {
|
|
|
23323
23323
|
const distilled = distillFactorDetectReport(tool, args, diagnosed);
|
|
23324
23324
|
return injectDebugRunId(tool, injectStrategyDocsHint(tool, distilled, session.docsPath));
|
|
23325
23325
|
}
|
|
23326
|
+
function backtestStatusNumber(status) {
|
|
23327
|
+
return typeof status === "number" || typeof status === "string" && status.trim() !== "" ? Number(status) : NaN;
|
|
23328
|
+
}
|
|
23329
|
+
function annotateBacktestOutcome(tool, result) {
|
|
23330
|
+
if (tool.name !== "mcp-get-backtest-state") return result;
|
|
23331
|
+
try {
|
|
23332
|
+
const parsed = JSON.parse(result.content[0]?.text ?? "");
|
|
23333
|
+
if (!isRecord2(parsed) || !isRecord2(parsed.Data)) return result;
|
|
23334
|
+
const data = parsed.Data;
|
|
23335
|
+
const status = backtestStatusNumber(data.status);
|
|
23336
|
+
const reconciled = parsed.ErrNo === 10043 && data.reconciledFrom === "result.json";
|
|
23337
|
+
const backtestOutcome = reconciled ? "failed" : status === 0 || status === 1 ? "running" : status === 2 ? "failed" : status === 3 ? "cancelled" : status === 4 ? "terminated" : status === 5 ? "completed" : "unknown";
|
|
23338
|
+
const isTerminal2 = backtestOutcome !== "running" && backtestOutcome !== "unknown";
|
|
23339
|
+
const message = typeof data.error_msg === "string" ? data.error_msg.trim() : "";
|
|
23340
|
+
return {
|
|
23341
|
+
...result,
|
|
23342
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
23343
|
+
...parsed,
|
|
23344
|
+
Data: {
|
|
23345
|
+
...data,
|
|
23346
|
+
backtestOutcome,
|
|
23347
|
+
isTerminal: isTerminal2,
|
|
23348
|
+
evidenceSource: reconciled ? "current-attempt-result.json" : "ipc-state",
|
|
23349
|
+
nextAction: isTerminal2 ? "finalize-backtest" : backtestOutcome === "unknown" || data.waitStopped || data.pollingStopped ? "inspect-backtest-state" : "keep-waiting",
|
|
23350
|
+
...backtestOutcome === "failed" && !reconciled && (!message || /异常(?:退出|终止)|unexpected.*exit|exited unexpectedly/i.test(message)) ? { failureCause: "unknown" } : {}
|
|
23351
|
+
}
|
|
23352
|
+
}) }]
|
|
23353
|
+
};
|
|
23354
|
+
} catch {
|
|
23355
|
+
return result;
|
|
23356
|
+
}
|
|
23357
|
+
}
|
|
23326
23358
|
var ZLTAF_CONFIG_SEGMENTS = ["zltaf", "application-local.properties"];
|
|
23327
23359
|
var FALLBACK_FACTOR_PROJECT_PATH = ["Users", "factors"];
|
|
23328
23360
|
var factorProjectRootCache = /* @__PURE__ */ new Map();
|
|
@@ -24404,11 +24436,11 @@ function unreadableDownloadProgressError(uuid2) {
|
|
|
24404
24436
|
);
|
|
24405
24437
|
}
|
|
24406
24438
|
function unreadableBacktestStateError(runId) {
|
|
24407
|
-
return waitError(
|
|
24439
|
+
return annotateBacktestOutcome(BACKTEST_WAIT_POLL_IDENTITY, waitError(
|
|
24408
24440
|
10054,
|
|
24409
24441
|
"\u56DE\u6D4B\u72B6\u6001\u5E94\u7B54\u65E0\u6CD5\u89E3\u6790\uFF0C\u5DF2\u505C\u6B62\u7B49\u5F85\uFF1B\u8BF7\u7528 mcp-get-backtest-state \u590D\u67E5\u3002",
|
|
24410
24442
|
{ runId }
|
|
24411
|
-
);
|
|
24443
|
+
));
|
|
24412
24444
|
}
|
|
24413
24445
|
function factorWaitLane(executionId, session) {
|
|
24414
24446
|
let staleCount = 0;
|
|
@@ -24557,7 +24589,7 @@ function backtestWaitLane(runId, session, codePath, resultBaseline, attemptStart
|
|
|
24557
24589
|
return { verdict: "return", result: unreadableBacktestStateError(runId) };
|
|
24558
24590
|
}
|
|
24559
24591
|
const data = parsed.Data;
|
|
24560
|
-
const status =
|
|
24592
|
+
const status = backtestStatusNumber(data.status);
|
|
24561
24593
|
if (!Number.isFinite(status)) {
|
|
24562
24594
|
return { verdict: "return", result: unreadableBacktestStateError(runId) };
|
|
24563
24595
|
}
|
|
@@ -24598,7 +24630,10 @@ function backtestWaitLane(runId, session, codePath, resultBaseline, attemptStart
|
|
|
24598
24630
|
frozenCount = signature === lastSignature ? frozenCount + 1 : 0;
|
|
24599
24631
|
lastSignature = signature;
|
|
24600
24632
|
if (frozenCount >= WAIT_FROZEN_THRESHOLD) {
|
|
24601
|
-
return { verdict: "return", result:
|
|
24633
|
+
return { verdict: "return", result: annotateBacktestOutcome(
|
|
24634
|
+
BACKTEST_WAIT_POLL_IDENTITY,
|
|
24635
|
+
frozenBacktestResult(parsed, data)
|
|
24636
|
+
) };
|
|
24602
24637
|
}
|
|
24603
24638
|
const progress = typeof data.progress === "number" && Number.isFinite(data.progress) ? data.progress : 0;
|
|
24604
24639
|
return { verdict: "wait", progress };
|
|
@@ -24936,6 +24971,12 @@ function echoResolvedBacktestConfig(tool, args, result, contexts) {
|
|
|
24936
24971
|
const echoedRunId = typeof parsed.Data.runId === "string" ? parsed.Data.runId.trim() : "";
|
|
24937
24972
|
const runId = echoedRunId || (tool.name === "mcp-re-backtest" && typeof args.runId === "string" ? args.runId.trim() : "");
|
|
24938
24973
|
const context = runId ? contexts.get(runId) : void 0;
|
|
24974
|
+
if (runId) echo.runId = runId;
|
|
24975
|
+
if (context?.codePath) {
|
|
24976
|
+
echo.codePath = context.codePath;
|
|
24977
|
+
echo.runDir = context.codePath;
|
|
24978
|
+
}
|
|
24979
|
+
if (context?.runParams) echo.runParams = { ...context.runParams };
|
|
24939
24980
|
if (context?.attemptStartedAtMs) {
|
|
24940
24981
|
echo.attemptFence = {
|
|
24941
24982
|
attemptStartedAtMs: context.attemptStartedAtMs,
|
|
@@ -27501,7 +27542,7 @@ function runtimeRecoveryStopData(message, extra = {}, handler = "runtimeStart")
|
|
|
27501
27542
|
// src/index.ts
|
|
27502
27543
|
var CLI_NAME = "zlt-mcp";
|
|
27503
27544
|
var CLI_FALLBACK_PACKAGE_NAME = "@zltquant/zlt-mcp";
|
|
27504
|
-
var CLI_FALLBACK_VERSION = "0.
|
|
27545
|
+
var CLI_FALLBACK_VERSION = "0.5.0-beta.0";
|
|
27505
27546
|
if (process.argv.length <= 2) {
|
|
27506
27547
|
startMcpServer();
|
|
27507
27548
|
} else {
|
|
@@ -27511,21 +27552,44 @@ if (process.argv.length <= 2) {
|
|
|
27511
27552
|
if (kind !== "backtest") throw new Error(`Unsupported keepalive kind: ${kind}`);
|
|
27512
27553
|
await keepMcpBacktestAlive(id, codePath);
|
|
27513
27554
|
});
|
|
27514
|
-
program2.command("call <tool>").description("Call any MCP tool through the bundled MCP server").option("--args-json <json>", "JSON object containing tool arguments").option("--args-file <path>", "Read the JSON argument object from a file").option("--timeout-ms <ms>", "MCP request inactivity timeout in milliseconds").option("--max-total-timeout-ms <ms>", "Optional hard upper bound for the request").option("--no-progress", "Suppress progress diagnostics on stderr").action(async (tool, options) => {
|
|
27555
|
+
program2.command("call <tool>").description("Call any MCP tool through the bundled MCP server").option("--args-json <json>", "JSON object containing tool arguments").option("--args-file <path>", "Read the JSON argument object from a file").option("--output-json <path>", "Save the complete result to a new JSON file and print a compact receipt").option("--timeout-ms <ms>", "MCP request inactivity timeout in milliseconds").option("--max-total-timeout-ms <ms>", "Optional hard upper bound for the request").option("--no-progress", "Suppress progress diagnostics on stderr").action(async (tool, options) => {
|
|
27556
|
+
let outputFd;
|
|
27515
27557
|
try {
|
|
27516
27558
|
const args = parseMcpArgs(options.argsJson, options.argsFile);
|
|
27559
|
+
const timeoutMs = parseTimeoutOption(options.timeoutMs, "--timeout-ms");
|
|
27560
|
+
const maxTotalTimeoutMs = parseTimeoutOption(options.maxTotalTimeoutMs, "--max-total-timeout-ms");
|
|
27561
|
+
const resultFile = options.outputJson === void 0 ? void 0 : path14.resolve(options.outputJson);
|
|
27562
|
+
if (resultFile !== void 0) outputFd = fs13.openSync(resultFile, "wx");
|
|
27517
27563
|
const result = await callMcpTool(tool, args, {
|
|
27518
|
-
timeoutMs
|
|
27519
|
-
maxTotalTimeoutMs
|
|
27564
|
+
timeoutMs,
|
|
27565
|
+
maxTotalTimeoutMs,
|
|
27520
27566
|
showProgress: options.progress !== false
|
|
27521
27567
|
});
|
|
27522
|
-
|
|
27568
|
+
const complete = withCliProvenance(result);
|
|
27569
|
+
const serialized = JSON.stringify(complete) + "\n";
|
|
27570
|
+
if (outputFd !== void 0 && resultFile !== void 0) {
|
|
27571
|
+
try {
|
|
27572
|
+
fs13.writeFileSync(outputFd, serialized, "utf-8");
|
|
27573
|
+
fs13.closeSync(outputFd);
|
|
27574
|
+
outputFd = void 0;
|
|
27575
|
+
} catch (error2) {
|
|
27576
|
+
process.stdout.write(serialized);
|
|
27577
|
+
throw new Error(`Tool returned a result, but saving ${resultFile} failed; complete result is on stdout. Do not repeat the tool automatically. ${String(error2)}`);
|
|
27578
|
+
}
|
|
27579
|
+
const receipt = compactCliReceipt(tool, complete, resultFile);
|
|
27580
|
+
process.stdout.write(JSON.stringify(receipt) + "\n");
|
|
27581
|
+
if (!receipt.ok) process.exitCode = 1;
|
|
27582
|
+
} else {
|
|
27583
|
+
process.stdout.write(serialized);
|
|
27584
|
+
}
|
|
27523
27585
|
if (result.isError === true) process.exitCode = 1;
|
|
27524
27586
|
} catch (error2) {
|
|
27525
27587
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
27526
27588
|
process.stderr.write(`Error: ${message}
|
|
27527
27589
|
`);
|
|
27528
27590
|
process.exitCode = 1;
|
|
27591
|
+
} finally {
|
|
27592
|
+
if (outputFd !== void 0) fs13.closeSync(outputFd);
|
|
27529
27593
|
}
|
|
27530
27594
|
});
|
|
27531
27595
|
program2.command("list").description("List MCP tools exposed by the bundled server").option("--json", "Emit the complete tools/list response as JSON").action(async (options) => {
|
|
@@ -27619,6 +27683,50 @@ function resolveCliPackageProvenance() {
|
|
|
27619
27683
|
function withCliProvenance(result) {
|
|
27620
27684
|
return { ...result, cliProvenance: resolveCliPackageProvenance() };
|
|
27621
27685
|
}
|
|
27686
|
+
function compactCliReceipt(tool, result, resultFile) {
|
|
27687
|
+
const payload = parseMcpResult(result);
|
|
27688
|
+
const code = payload?.ErrNo;
|
|
27689
|
+
const ok = result.isError !== true && (code === void 0 || code === null || Number(code) === 0);
|
|
27690
|
+
const source = payload?.Data;
|
|
27691
|
+
const data = {};
|
|
27692
|
+
if (source && typeof source === "object" && !Array.isArray(source)) {
|
|
27693
|
+
const fields = source;
|
|
27694
|
+
for (const key of [
|
|
27695
|
+
"runId",
|
|
27696
|
+
"backtestId",
|
|
27697
|
+
"strategyId",
|
|
27698
|
+
"localPath",
|
|
27699
|
+
"codePath",
|
|
27700
|
+
"runDir",
|
|
27701
|
+
"attemptFence",
|
|
27702
|
+
"status",
|
|
27703
|
+
"progress",
|
|
27704
|
+
"available",
|
|
27705
|
+
"runtimeReady",
|
|
27706
|
+
"backtestReady",
|
|
27707
|
+
"backtestOutcome",
|
|
27708
|
+
"isTerminal",
|
|
27709
|
+
"evidenceSource",
|
|
27710
|
+
"failureCause",
|
|
27711
|
+
"nextAction",
|
|
27712
|
+
"recommendedAction",
|
|
27713
|
+
"error_code",
|
|
27714
|
+
"error_msg"
|
|
27715
|
+
]) {
|
|
27716
|
+
if (fields[key] !== void 0) {
|
|
27717
|
+
data[key] = key === "error_msg" && typeof fields[key] === "string" ? fields[key].slice(0, 512) : fields[key];
|
|
27718
|
+
}
|
|
27719
|
+
}
|
|
27720
|
+
}
|
|
27721
|
+
return {
|
|
27722
|
+
ok,
|
|
27723
|
+
tool,
|
|
27724
|
+
resultFile,
|
|
27725
|
+
cliProvenance: result.cliProvenance,
|
|
27726
|
+
data,
|
|
27727
|
+
...!ok ? { error: { code: code ?? null, message: String(payload?.ErrMsg ?? "Tool reported an error; see resultFile").slice(0, 512) } } : {}
|
|
27728
|
+
};
|
|
27729
|
+
}
|
|
27622
27730
|
function parseTimeoutOption(value, flag) {
|
|
27623
27731
|
if (value === void 0) return void 0;
|
|
27624
27732
|
const parsed = Number(value);
|