@zltquant/zlt-mcp 0.4.1 → 0.5.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -9
- package/package.json +1 -1
- package/zlt-mcp-proxy.bundle.cjs +126 -18
package/README.md
CHANGED
|
@@ -7,7 +7,20 @@
|
|
|
7
7
|
|
|
8
8
|
## 接入
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
WorkBuddy 使用配套 Skill,并由 Skill 调用 npm CLI 完成任务,无需配置 MCP 连接器:
|
|
11
|
+
|
|
12
|
+
```powershell
|
|
13
|
+
npx -y @zltquant/zlt-mcp list --json
|
|
14
|
+
npx -y @zltquant/zlt-mcp call zlt-runtime-doctor --args-json '{}'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
业务调用支持 `call --output-json <新文件路径>`:完整 JSON 写入文件,stdout 返回含
|
|
18
|
+
`ok`、`tool`、`resultFile`、`cliProvenance`、精简 `data` 的回执。
|
|
19
|
+
输出目录须已存在,已有文件会在执行工具前报错;每次使用新文件名。完整参数与大段数据从结果文件读取。
|
|
20
|
+
stderr 保留诊断,失败返回非零退出码。工具执行后若写文件失败,完整结果会输出到 stdout;先核对结果,不要自动重复执行。
|
|
21
|
+
不传该参数时保持原有完整 stdout。
|
|
22
|
+
|
|
23
|
+
其他支持原生 MCP 的客户端(Trae / Claude Code / Cursor 等)可以使用下面的配置:
|
|
11
24
|
|
|
12
25
|
```json
|
|
13
26
|
{
|
|
@@ -20,17 +33,10 @@
|
|
|
20
33
|
}
|
|
21
34
|
```
|
|
22
35
|
|
|
23
|
-
WorkBuddy 也可以不配置 MCP,直接由 Skill 调用 CLI:
|
|
24
|
-
|
|
25
|
-
```powershell
|
|
26
|
-
npx -y @zltquant/zlt-mcp list --json
|
|
27
|
-
npx -y @zltquant/zlt-mcp call mcp-get-strategy-list --args-json '{"status":0,"treeId":0}'
|
|
28
|
-
```
|
|
29
|
-
|
|
30
36
|
首次使用前请从 https://api.zltquant.com/login 获取 Key,并由用户自己创建
|
|
31
37
|
`%USERPROFILE%\.zltquant\credentials.json`(只填 `apiKey`)。
|
|
32
38
|
|
|
33
|
-
|
|
39
|
+
其他客户端无法使用 npx 时,也可以固定安装后用 node 启动 MCP 服务:
|
|
34
40
|
|
|
35
41
|
```powershell
|
|
36
42
|
npm i @zltquant/zlt-mcp --prefix "$env:USERPROFILE\.zlt-quant" --registry https://registry.npmmirror.com
|
package/package.json
CHANGED
package/zlt-mcp-proxy.bundle.cjs
CHANGED
|
@@ -18374,8 +18374,21 @@ function parsePackageList(value) {
|
|
|
18374
18374
|
|
|
18375
18375
|
// ../zlt-runtime-core/dist/registry.js
|
|
18376
18376
|
var import_child_process = require("child_process");
|
|
18377
|
-
var
|
|
18378
|
-
var
|
|
18377
|
+
var POWERSHELL_EXE = "powershell.exe";
|
|
18378
|
+
var REGISTRY_SUBKEY_ENV = "ZLT_REGISTRY_SUBKEY_B64";
|
|
18379
|
+
var REGISTRY_VALUE_NAME_ENV = "ZLT_REGISTRY_VALUE_NAME_B64";
|
|
18380
|
+
var READ_REGISTRY_VALUE_SCRIPT = [
|
|
18381
|
+
"$subKey = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($env:ZLT_REGISTRY_SUBKEY_B64))",
|
|
18382
|
+
"$valueName = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($env:ZLT_REGISTRY_VALUE_NAME_B64))",
|
|
18383
|
+
"$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($subKey)",
|
|
18384
|
+
"if ($null -eq $key) { exit 1 }",
|
|
18385
|
+
"try {",
|
|
18386
|
+
" if ($key.GetValueKind($valueName) -ne [Microsoft.Win32.RegistryValueKind]::String) { exit 1 }",
|
|
18387
|
+
" $value = $key.GetValue($valueName, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)",
|
|
18388
|
+
" if ($null -eq $value) { exit 1 }",
|
|
18389
|
+
" [Console]::Out.Write([string]$value)",
|
|
18390
|
+
"} finally { $key.Dispose() }"
|
|
18391
|
+
].join("\n");
|
|
18379
18392
|
function buildAssistantRegistryKey(registryKeyName) {
|
|
18380
18393
|
return `HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${registryKeyName}`;
|
|
18381
18394
|
}
|
|
@@ -18393,26 +18406,45 @@ function getRegistryKeyNames(config2) {
|
|
|
18393
18406
|
}
|
|
18394
18407
|
return names;
|
|
18395
18408
|
}
|
|
18396
|
-
function
|
|
18397
|
-
const
|
|
18398
|
-
|
|
18399
|
-
|
|
18409
|
+
function buildPowerShellRegistryEnvironment(registryKey, valueName) {
|
|
18410
|
+
const subKey = registryKey.replace(/^HKLM\\/i, "");
|
|
18411
|
+
return {
|
|
18412
|
+
...process.env,
|
|
18413
|
+
[REGISTRY_SUBKEY_ENV]: Buffer.from(subKey, "utf-8").toString("base64"),
|
|
18414
|
+
[REGISTRY_VALUE_NAME_ENV]: Buffer.from(valueName, "utf-8").toString("base64")
|
|
18415
|
+
};
|
|
18400
18416
|
}
|
|
18401
|
-
|
|
18417
|
+
function buildPowerShellRegistryArgs() {
|
|
18418
|
+
return ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", READ_REGISTRY_VALUE_SCRIPT];
|
|
18419
|
+
}
|
|
18420
|
+
function readRegistryValueWithPowerShell(registryKey, valueName) {
|
|
18421
|
+
return new Promise((resolve8, reject) => {
|
|
18422
|
+
(0, import_child_process.execFile)(POWERSHELL_EXE, buildPowerShellRegistryArgs(), {
|
|
18423
|
+
encoding: "utf-8",
|
|
18424
|
+
env: buildPowerShellRegistryEnvironment(registryKey, valueName),
|
|
18425
|
+
windowsHide: true
|
|
18426
|
+
}, (error2, stdout) => {
|
|
18427
|
+
if (error2)
|
|
18428
|
+
reject(error2);
|
|
18429
|
+
else
|
|
18430
|
+
resolve8(stdout.trim());
|
|
18431
|
+
});
|
|
18432
|
+
});
|
|
18433
|
+
}
|
|
18434
|
+
async function readAssistantRegistryStringValue(config2, valueName, _suppressStderr = true) {
|
|
18402
18435
|
const registryKeyNames = getRegistryKeyNames(config2);
|
|
18403
18436
|
const registryKey = buildAssistantRegistryKey(registryKeyNames[0]);
|
|
18404
18437
|
if (process.platform !== "win32")
|
|
18405
18438
|
return { registryKey, valueName };
|
|
18406
|
-
const stderrRedirect = suppressStderr ? " 2>nul" : "";
|
|
18407
18439
|
for (const registryKeyName of registryKeyNames) {
|
|
18408
18440
|
const candidateRegistryKey = buildAssistantRegistryKey(registryKeyName);
|
|
18409
18441
|
try {
|
|
18410
|
-
const
|
|
18442
|
+
const stdout = await readRegistryValueWithPowerShell(candidateRegistryKey, valueName);
|
|
18411
18443
|
return {
|
|
18412
18444
|
registryKey: candidateRegistryKey,
|
|
18413
18445
|
valueName,
|
|
18414
18446
|
stdout,
|
|
18415
|
-
value:
|
|
18447
|
+
value: stdout
|
|
18416
18448
|
};
|
|
18417
18449
|
} catch {
|
|
18418
18450
|
}
|
|
@@ -24904,6 +24936,12 @@ function echoResolvedBacktestConfig(tool, args, result, contexts) {
|
|
|
24904
24936
|
const echoedRunId = typeof parsed.Data.runId === "string" ? parsed.Data.runId.trim() : "";
|
|
24905
24937
|
const runId = echoedRunId || (tool.name === "mcp-re-backtest" && typeof args.runId === "string" ? args.runId.trim() : "");
|
|
24906
24938
|
const context = runId ? contexts.get(runId) : void 0;
|
|
24939
|
+
if (runId) echo.runId = runId;
|
|
24940
|
+
if (context?.codePath) {
|
|
24941
|
+
echo.codePath = context.codePath;
|
|
24942
|
+
echo.runDir = context.codePath;
|
|
24943
|
+
}
|
|
24944
|
+
if (context?.runParams) echo.runParams = { ...context.runParams };
|
|
24907
24945
|
if (context?.attemptStartedAtMs) {
|
|
24908
24946
|
echo.attemptFence = {
|
|
24909
24947
|
attemptStartedAtMs: context.attemptStartedAtMs,
|
|
@@ -27468,7 +27506,8 @@ function runtimeRecoveryStopData(message, extra = {}, handler = "runtimeStart")
|
|
|
27468
27506
|
|
|
27469
27507
|
// src/index.ts
|
|
27470
27508
|
var CLI_NAME = "zlt-mcp";
|
|
27471
|
-
var
|
|
27509
|
+
var CLI_FALLBACK_PACKAGE_NAME = "@zltquant/zlt-mcp";
|
|
27510
|
+
var CLI_FALLBACK_VERSION = "0.5.0-beta.0";
|
|
27472
27511
|
if (process.argv.length <= 2) {
|
|
27473
27512
|
startMcpServer();
|
|
27474
27513
|
} else {
|
|
@@ -27478,28 +27517,51 @@ if (process.argv.length <= 2) {
|
|
|
27478
27517
|
if (kind !== "backtest") throw new Error(`Unsupported keepalive kind: ${kind}`);
|
|
27479
27518
|
await keepMcpBacktestAlive(id, codePath);
|
|
27480
27519
|
});
|
|
27481
|
-
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) => {
|
|
27520
|
+
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) => {
|
|
27521
|
+
let outputFd;
|
|
27482
27522
|
try {
|
|
27483
27523
|
const args = parseMcpArgs(options.argsJson, options.argsFile);
|
|
27524
|
+
const timeoutMs = parseTimeoutOption(options.timeoutMs, "--timeout-ms");
|
|
27525
|
+
const maxTotalTimeoutMs = parseTimeoutOption(options.maxTotalTimeoutMs, "--max-total-timeout-ms");
|
|
27526
|
+
const resultFile = options.outputJson === void 0 ? void 0 : path14.resolve(options.outputJson);
|
|
27527
|
+
if (resultFile !== void 0) outputFd = fs13.openSync(resultFile, "wx");
|
|
27484
27528
|
const result = await callMcpTool(tool, args, {
|
|
27485
|
-
timeoutMs
|
|
27486
|
-
maxTotalTimeoutMs
|
|
27529
|
+
timeoutMs,
|
|
27530
|
+
maxTotalTimeoutMs,
|
|
27487
27531
|
showProgress: options.progress !== false
|
|
27488
27532
|
});
|
|
27489
|
-
|
|
27533
|
+
const complete = withCliProvenance(result);
|
|
27534
|
+
const serialized = JSON.stringify(complete) + "\n";
|
|
27535
|
+
if (outputFd !== void 0 && resultFile !== void 0) {
|
|
27536
|
+
try {
|
|
27537
|
+
fs13.writeFileSync(outputFd, serialized, "utf-8");
|
|
27538
|
+
fs13.closeSync(outputFd);
|
|
27539
|
+
outputFd = void 0;
|
|
27540
|
+
} catch (error2) {
|
|
27541
|
+
process.stdout.write(serialized);
|
|
27542
|
+
throw new Error(`Tool returned a result, but saving ${resultFile} failed; complete result is on stdout. Do not repeat the tool automatically. ${String(error2)}`);
|
|
27543
|
+
}
|
|
27544
|
+
const receipt = compactCliReceipt(tool, complete, resultFile);
|
|
27545
|
+
process.stdout.write(JSON.stringify(receipt) + "\n");
|
|
27546
|
+
if (!receipt.ok) process.exitCode = 1;
|
|
27547
|
+
} else {
|
|
27548
|
+
process.stdout.write(serialized);
|
|
27549
|
+
}
|
|
27490
27550
|
if (result.isError === true) process.exitCode = 1;
|
|
27491
27551
|
} catch (error2) {
|
|
27492
27552
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
27493
27553
|
process.stderr.write(`Error: ${message}
|
|
27494
27554
|
`);
|
|
27495
27555
|
process.exitCode = 1;
|
|
27556
|
+
} finally {
|
|
27557
|
+
if (outputFd !== void 0) fs13.closeSync(outputFd);
|
|
27496
27558
|
}
|
|
27497
27559
|
});
|
|
27498
27560
|
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) => {
|
|
27499
27561
|
try {
|
|
27500
27562
|
const result = await listMcpTools();
|
|
27501
27563
|
if (options.json) {
|
|
27502
|
-
process.stdout.write(JSON.stringify(result) + "\n");
|
|
27564
|
+
process.stdout.write(JSON.stringify(withCliProvenance(result)) + "\n");
|
|
27503
27565
|
return;
|
|
27504
27566
|
}
|
|
27505
27567
|
const tools = Array.isArray(result.tools) ? result.tools : [];
|
|
@@ -27564,13 +27626,16 @@ if (process.argv.length <= 2) {
|
|
|
27564
27626
|
});
|
|
27565
27627
|
}
|
|
27566
27628
|
function resolveCliVersion() {
|
|
27629
|
+
return resolveCliPackageProvenance().packageVersion;
|
|
27630
|
+
}
|
|
27631
|
+
function resolveCliPackageProvenance() {
|
|
27567
27632
|
let current = path14.dirname(path14.resolve(process.argv[1] || process.cwd()));
|
|
27568
27633
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
27569
27634
|
const manifestPath = path14.join(current, "package.json");
|
|
27570
27635
|
try {
|
|
27571
27636
|
const manifest = JSON.parse(fs13.readFileSync(manifestPath, "utf-8"));
|
|
27572
27637
|
if ((manifest.name === "@zltquant/zlt-mcp" || manifest.name === "zlt-mcp-proxy") && typeof manifest.version === "string") {
|
|
27573
|
-
return manifest.version;
|
|
27638
|
+
return { packageName: manifest.name, packageVersion: manifest.version };
|
|
27574
27639
|
}
|
|
27575
27640
|
} catch {
|
|
27576
27641
|
}
|
|
@@ -27578,7 +27643,50 @@ function resolveCliVersion() {
|
|
|
27578
27643
|
if (parent === current) break;
|
|
27579
27644
|
current = parent;
|
|
27580
27645
|
}
|
|
27581
|
-
return CLI_FALLBACK_VERSION;
|
|
27646
|
+
return { packageName: CLI_FALLBACK_PACKAGE_NAME, packageVersion: CLI_FALLBACK_VERSION };
|
|
27647
|
+
}
|
|
27648
|
+
function withCliProvenance(result) {
|
|
27649
|
+
return { ...result, cliProvenance: resolveCliPackageProvenance() };
|
|
27650
|
+
}
|
|
27651
|
+
function compactCliReceipt(tool, result, resultFile) {
|
|
27652
|
+
const payload = parseMcpResult(result);
|
|
27653
|
+
const code = payload?.ErrNo;
|
|
27654
|
+
const ok = result.isError !== true && (code === void 0 || code === null || Number(code) === 0);
|
|
27655
|
+
const source = payload?.Data;
|
|
27656
|
+
const data = {};
|
|
27657
|
+
if (source && typeof source === "object" && !Array.isArray(source)) {
|
|
27658
|
+
const fields = source;
|
|
27659
|
+
for (const key of [
|
|
27660
|
+
"runId",
|
|
27661
|
+
"backtestId",
|
|
27662
|
+
"strategyId",
|
|
27663
|
+
"localPath",
|
|
27664
|
+
"codePath",
|
|
27665
|
+
"runDir",
|
|
27666
|
+
"attemptFence",
|
|
27667
|
+
"status",
|
|
27668
|
+
"progress",
|
|
27669
|
+
"available",
|
|
27670
|
+
"runtimeReady",
|
|
27671
|
+
"backtestReady",
|
|
27672
|
+
"nextAction",
|
|
27673
|
+
"recommendedAction",
|
|
27674
|
+
"error_code",
|
|
27675
|
+
"error_msg"
|
|
27676
|
+
]) {
|
|
27677
|
+
if (fields[key] !== void 0) {
|
|
27678
|
+
data[key] = key === "error_msg" && typeof fields[key] === "string" ? fields[key].slice(0, 512) : fields[key];
|
|
27679
|
+
}
|
|
27680
|
+
}
|
|
27681
|
+
}
|
|
27682
|
+
return {
|
|
27683
|
+
ok,
|
|
27684
|
+
tool,
|
|
27685
|
+
resultFile,
|
|
27686
|
+
cliProvenance: result.cliProvenance,
|
|
27687
|
+
data,
|
|
27688
|
+
...!ok ? { error: { code: code ?? null, message: String(payload?.ErrMsg ?? "Tool reported an error; see resultFile").slice(0, 512) } } : {}
|
|
27689
|
+
};
|
|
27582
27690
|
}
|
|
27583
27691
|
function parseTimeoutOption(value, flag) {
|
|
27584
27692
|
if (value === void 0) return void 0;
|