@reconcrap/boss-recommend-mcp 2.1.11 → 2.1.12
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 +4 -2
- package/package.json +1 -1
- package/skills/boss-chat/SKILL.md +2 -0
- package/skills/boss-recommend-pipeline/SKILL.md +2 -0
- package/skills/boss-recruit-pipeline/SKILL.md +2 -0
- package/src/cli.js +129 -85
- package/src/index.js +238 -18
package/README.md
CHANGED
|
@@ -225,8 +225,9 @@ node src/cli.js start
|
|
|
225
225
|
如果检测到 legacy Boss server entries,installer 会:
|
|
226
226
|
|
|
227
227
|
- 保留非 Boss MCP server。
|
|
228
|
-
-
|
|
229
|
-
-
|
|
228
|
+
- Trae/Trae-CN 默认写入三个小 toolset server:`boss-recommend`(`BOSS_RECOMMEND_MCP_TOOLSET=recommend`)、`boss-chat`(`chat`)、`boss-recruit`(`recruit`)。这样 recommend/chat/search 的 tool list 不会互相挤占 agent 可见工具预算。
|
|
229
|
+
- 其它宿主默认仍写入兼容统一 server:`boss-recommend -> npx -y @reconcrap/boss-recommend-mcp@<installed-version> start`。
|
|
230
|
+
- 如果传入 `--mcp-launch global-wrapper`,Trae/Trae-CN 同样会写入三个 toolset server,但 command 指向升级稳定 wrapper。该 wrapper 会加载 `~/.nvm/nvm.sh` 并执行当前全局 `boss-recommend-mcp start`,适合 macOS 上通过 `npm -g i @reconcrap/boss-recommend-mcp@latest` 持续升级。
|
|
230
231
|
- 从同一个 `mcp.json` 删除旧 `boss-recruit-mcp`、standalone `boss-chat`、旧本地 Boss repo 路径,避免 agent 继续调用 legacy 包。
|
|
231
232
|
- 在原文件旁生成 `mcp.json.boss-mcp-migration-*.bak`。
|
|
232
233
|
- 同步外部 skills 目录里的 `boss-recommend-pipeline`、`boss-recruit-pipeline`、`boss-chat`。
|
|
@@ -263,6 +264,7 @@ BOSS_RECOMMEND_EXTERNAL_SKILL_DIRS="/path/to/skills" boss-recommend-mcp install
|
|
|
263
264
|
BOSS_RECOMMEND_HOME # 统一状态目录,默认 ~/.boss-recommend-mcp
|
|
264
265
|
BOSS_CHAT_HOME # 覆盖 boss-chat 运行态目录;默认 ~/.boss-recommend-mcp/boss-chat
|
|
265
266
|
BOSS_RECOMMEND_SCREEN_CONFIG # 显式指定 screening-config.json 路径(最高优先级)
|
|
267
|
+
BOSS_RECOMMEND_MCP_TOOLSET # 可选收窄 MCP 工具:all|recommend|chat|recruit;Trae/Trae-CN installer 会自动设置
|
|
266
268
|
BOSS_RECOMMEND_MCP_CONFIG_TARGETS # JSON 数组或系统 path 分隔路径列表,指定额外 mcp.json 目标文件
|
|
267
269
|
BOSS_RECOMMEND_EXTERNAL_SKILL_DIRS # JSON 数组或系统 path 分隔路径列表,指定额外 skills 根目录
|
|
268
270
|
```
|
package/package.json
CHANGED
|
@@ -13,6 +13,8 @@ description: "Use when users want Boss chat-page screening/outreach via the bund
|
|
|
13
13
|
|
|
14
14
|
## Tool Routing
|
|
15
15
|
|
|
16
|
+
Trae/Trae-CN split-server config exposes these under the `boss-chat` MCP server. Chat-only tasks should call `boss-chat/<tool>` when the host shows server-qualified tool names.
|
|
17
|
+
|
|
16
18
|
- 健康检查:`boss_chat_health_check`
|
|
17
19
|
- 只读获取聊天页岗位列表:`list_boss_chat_jobs`
|
|
18
20
|
- 预备并获取岗位列表:`prepare_boss_chat_run`
|
|
@@ -97,6 +97,8 @@ description: "Use when users want Boss recommend-page filtering/screening via bo
|
|
|
97
97
|
|
|
98
98
|
## Tool Usage
|
|
99
99
|
|
|
100
|
+
Trae/Trae-CN split-server config exposes these under the `boss-recommend` MCP server. 推荐页任务应调用 `boss-recommend/<tool>`;不要切到 `boss-chat` 或 `boss-recruit`。
|
|
101
|
+
|
|
100
102
|
- 岗位发现工具:`list_recommend_jobs`
|
|
101
103
|
- 用途:当用户需要为 cron / 一次性自动任务提前填写完整参数时,先用它读取推荐页岗位下拉框的全部可用岗位名;默认会复用/自动打开本机 9222 Chrome 并导航到推荐页。
|
|
102
104
|
- 输出:优先把 `job_names` 里的值作为后续 `overrides.job` / `confirmation.job_value`。
|
|
@@ -11,6 +11,8 @@ description: "Use when users want Boss search/recruit-page screening via the uni
|
|
|
11
11
|
|
|
12
12
|
## Tool Routing
|
|
13
13
|
|
|
14
|
+
Trae/Trae-CN split-server config exposes these under the `boss-recruit` MCP server. Search/recruit tasks should call `boss-recruit/<tool>` when the host shows server-qualified tool names.
|
|
15
|
+
|
|
14
16
|
- 同步启动:`run_recruit_pipeline`
|
|
15
17
|
- 异步启动:`start_recruit_pipeline_run`
|
|
16
18
|
- 查询进度:`get_recruit_pipeline_run`
|
package/src/cli.js
CHANGED
|
@@ -57,12 +57,15 @@ const bossLoginUrl = "https://www.zhipin.com/web/user/?ka=bticket";
|
|
|
57
57
|
const chromeOnboardingUrlPattern = /^chrome:\/\/(welcome|intro|newtab|signin|history-sync|settings\/syncSetup)/i;
|
|
58
58
|
const bossLoginUrlPattern = /(?:zhipin\.com\/web\/user(?:\/|\?|$)|passport\.zhipin\.com)/i;
|
|
59
59
|
const bossLoginTitlePattern = /登录|signin|扫码登录|BOSS直聘登录/i;
|
|
60
|
-
const supportedMcpClients = ["generic", "cursor", "trae", "claudecode", "openclaw", "qclaw"];
|
|
60
|
+
const supportedMcpClients = ["generic", "cursor", "trae", "claudecode", "openclaw", "qclaw"];
|
|
61
61
|
const defaultMcpServerName = "boss-recommend";
|
|
62
|
+
const bossChatMcpServerName = "boss-chat";
|
|
63
|
+
const bossRecruitMcpServerName = "boss-recruit";
|
|
62
64
|
const defaultMcpCommand = "npx";
|
|
63
65
|
const recommendMcpPackageName = "@reconcrap/boss-recommend-mcp";
|
|
64
66
|
const recommendMcpBinaryName = "boss-recommend-mcp";
|
|
65
67
|
const globalMcpWrapperFileName = "boss-recommend-mcp-mcp-server";
|
|
68
|
+
const mcpToolsetEnv = "BOSS_RECOMMEND_MCP_TOOLSET";
|
|
66
69
|
const supportedMcpLaunchModes = ["npx", "global-wrapper"];
|
|
67
70
|
const autoSyncSkipCommands = new Set(["install", "install-skill", "where", "help", "--help", "-h", "list-jobs", "jobs", "recommend-jobs"]);
|
|
68
71
|
const externalMcpTargetsEnv = "BOSS_RECOMMEND_MCP_CONFIG_TARGETS";
|
|
@@ -807,12 +810,35 @@ function shouldDefaultRecommendDetachedMcpEnv(options = {}) {
|
|
|
807
810
|
return client === "openclaw"
|
|
808
811
|
|| client === "qclaw"
|
|
809
812
|
|| agent === "openclaw"
|
|
810
|
-
|| agent === "qclaw";
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
function
|
|
814
|
-
|
|
815
|
-
|
|
813
|
+
|| agent === "qclaw";
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function normalizeMcpToolsetOption(value) {
|
|
817
|
+
const raw = String(value || "").trim().toLowerCase().replace(/[\s_]+/g, "-");
|
|
818
|
+
if (!raw || raw === "all") return "";
|
|
819
|
+
if (raw === "boss-recommend" || raw === "recommend-page") return "recommend";
|
|
820
|
+
if (raw === "boss-chat" || raw === "chat-only" || raw === "chat-page") return "chat";
|
|
821
|
+
if (raw === "boss-recruit" || raw === "search" || raw === "search-page" || raw === "recruit-page") return "recruit";
|
|
822
|
+
if (["recommend", "chat", "recruit"].includes(raw)) return raw;
|
|
823
|
+
throw new Error(`Unsupported --toolset value: ${raw}. Supported: recommend, chat, recruit, all`);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function getMcpToolsetEnv(options = {}) {
|
|
827
|
+
const toolset = normalizeMcpToolsetOption(options.toolset || options["toolset"]);
|
|
828
|
+
return toolset ? { [mcpToolsetEnv]: toolset } : {};
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function shouldUseSplitBossMcpServers(options = {}) {
|
|
832
|
+
if (options["server-name"] || options.serverName || options.toolset || options["toolset"]) return false;
|
|
833
|
+
if (options.split === true || options["split-tools"] === true || options.splitTools === true) return true;
|
|
834
|
+
const client = normalizeMcpClientName(options.client);
|
|
835
|
+
const agent = normalizeAgentName(options.agent);
|
|
836
|
+
return client === "trae" || agent === "trae" || agent === "trae-cn";
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
function getDefaultMcpEnv(options = {}) {
|
|
840
|
+
return shouldDefaultRecommendDetachedMcpEnv(options)
|
|
841
|
+
? { ...detachedRecommendMcpEnv }
|
|
816
842
|
: {};
|
|
817
843
|
}
|
|
818
844
|
|
|
@@ -834,6 +860,7 @@ function buildMcpLaunchConfig(options = {}) {
|
|
|
834
860
|
};
|
|
835
861
|
const mergedEnv = {
|
|
836
862
|
...getDefaultMcpEnv(options),
|
|
863
|
+
...getMcpToolsetEnv(options),
|
|
837
864
|
...(isPlainObject(env) ? env : {})
|
|
838
865
|
};
|
|
839
866
|
if (Object.keys(mergedEnv).length > 0) {
|
|
@@ -853,19 +880,36 @@ function buildMcpLaunchConfig(options = {}) {
|
|
|
853
880
|
? ["start"]
|
|
854
881
|
: buildDefaultMcpArgs(options);
|
|
855
882
|
const launchConfig = { command, args: launchArgs };
|
|
856
|
-
const mergedEnv = {
|
|
857
|
-
...getDefaultMcpEnv(options),
|
|
858
|
-
...(
|
|
859
|
-
|
|
883
|
+
const mergedEnv = {
|
|
884
|
+
...getDefaultMcpEnv(options),
|
|
885
|
+
...getMcpToolsetEnv(options),
|
|
886
|
+
...(isPlainObject(env) ? env : {})
|
|
887
|
+
};
|
|
860
888
|
if (Object.keys(mergedEnv).length > 0) {
|
|
861
889
|
launchConfig.env = mergedEnv;
|
|
862
890
|
}
|
|
863
891
|
return launchConfig;
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
function
|
|
867
|
-
if (
|
|
868
|
-
return
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
function buildBossMcpServerEntries(options = {}) {
|
|
895
|
+
if (shouldUseSplitBossMcpServers(options)) {
|
|
896
|
+
return {
|
|
897
|
+
[defaultMcpServerName]: buildMcpLaunchConfig({ ...options, toolset: "recommend" }),
|
|
898
|
+
[bossChatMcpServerName]: buildMcpLaunchConfig({ ...options, toolset: "chat" }),
|
|
899
|
+
[bossRecruitMcpServerName]: buildMcpLaunchConfig({ ...options, toolset: "recruit" })
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
const serverName = typeof options["server-name"] === "string" && options["server-name"].trim()
|
|
903
|
+
? options["server-name"].trim()
|
|
904
|
+
: defaultMcpServerName;
|
|
905
|
+
return {
|
|
906
|
+
[serverName]: buildMcpLaunchConfig(options)
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
function mergeExistingMcpEntryEnv(existingEntry, launchConfig) {
|
|
911
|
+
if (!isPlainObject(existingEntry?.env) || !isPlainObject(launchConfig)) {
|
|
912
|
+
return launchConfig;
|
|
869
913
|
}
|
|
870
914
|
return {
|
|
871
915
|
...launchConfig,
|
|
@@ -874,28 +918,21 @@ function mergeExistingMcpEntryEnv(existingEntry, launchConfig) {
|
|
|
874
918
|
...(isPlainObject(launchConfig.env) ? launchConfig.env : {})
|
|
875
919
|
}
|
|
876
920
|
};
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
function buildMcpConfigFileContent(options = {}) {
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
}
|
|
893
|
-
return {
|
|
894
|
-
mcpServers: {
|
|
895
|
-
[serverName]: launchConfig
|
|
896
|
-
}
|
|
897
|
-
};
|
|
898
|
-
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function buildMcpConfigFileContent(options = {}) {
|
|
924
|
+
const servers = buildBossMcpServerEntries(options);
|
|
925
|
+
if (normalizeMcpClientName(options.client) === "qclaw") {
|
|
926
|
+
return {
|
|
927
|
+
mcp: {
|
|
928
|
+
servers
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
return {
|
|
933
|
+
mcpServers: servers
|
|
934
|
+
};
|
|
935
|
+
}
|
|
899
936
|
|
|
900
937
|
function writeMcpConfigFiles(options = {}) {
|
|
901
938
|
const clients = parseMcpClientTargets(options.client);
|
|
@@ -1039,25 +1076,28 @@ function getMcpServersFromConfig(config = {}, useQClawShape = false) {
|
|
|
1039
1076
|
return {};
|
|
1040
1077
|
}
|
|
1041
1078
|
|
|
1042
|
-
function mergeMcpServerConfigFile(filePath, options = {}) {
|
|
1043
|
-
const current = readJsonObjectFileSafe(filePath);
|
|
1044
|
-
const useQClawShape = isQClawMcpConfigTarget(filePath, options, current);
|
|
1045
|
-
const nextConfig = buildMcpConfigFileContent({ ...options, client: useQClawShape ? "qclaw" : options.client });
|
|
1046
|
-
const nextServers = useQClawShape ? nextConfig.mcp?.servers : nextConfig.mcpServers;
|
|
1047
|
-
const
|
|
1048
|
-
const
|
|
1049
|
-
const
|
|
1050
|
-
const
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1079
|
+
function mergeMcpServerConfigFile(filePath, options = {}) {
|
|
1080
|
+
const current = readJsonObjectFileSafe(filePath);
|
|
1081
|
+
const useQClawShape = isQClawMcpConfigTarget(filePath, options, current);
|
|
1082
|
+
const nextConfig = buildMcpConfigFileContent({ ...options, client: useQClawShape ? "qclaw" : options.client });
|
|
1083
|
+
const nextServers = useQClawShape ? nextConfig.mcp?.servers : nextConfig.mcpServers;
|
|
1084
|
+
const serverNames = Object.keys(nextServers || {});
|
|
1085
|
+
const nextBossServerNames = new Set(serverNames);
|
|
1086
|
+
const existingServers = getMcpServersFromConfig(current, useQClawShape);
|
|
1087
|
+
const mergedBossServers = {};
|
|
1088
|
+
for (const serverName of serverNames) {
|
|
1089
|
+
mergedBossServers[serverName] = mergeExistingMcpEntryEnv(
|
|
1090
|
+
existingServers[serverName],
|
|
1091
|
+
nextServers?.[serverName] || buildMcpLaunchConfig(options)
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
const retainedServers = {};
|
|
1095
|
+
const migratedLegacyServers = [];
|
|
1096
|
+
for (const [name, config] of Object.entries(existingServers)) {
|
|
1097
|
+
if (nextBossServerNames.has(name)) continue;
|
|
1098
|
+
if (isBossMcpServerEntry(name, config)) {
|
|
1099
|
+
migratedLegacyServers.push(name);
|
|
1100
|
+
continue;
|
|
1061
1101
|
}
|
|
1062
1102
|
retainedServers[name] = config;
|
|
1063
1103
|
}
|
|
@@ -1065,20 +1105,20 @@ function mergeMcpServerConfigFile(filePath, options = {}) {
|
|
|
1065
1105
|
? {
|
|
1066
1106
|
...current,
|
|
1067
1107
|
mcp: {
|
|
1068
|
-
...(current?.mcp && typeof current.mcp === "object" && !Array.isArray(current.mcp) ? current.mcp : {}),
|
|
1069
|
-
servers: {
|
|
1070
|
-
...retainedServers,
|
|
1071
|
-
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
: {
|
|
1076
|
-
...current,
|
|
1077
|
-
mcpServers: {
|
|
1078
|
-
...retainedServers,
|
|
1079
|
-
|
|
1080
|
-
}
|
|
1081
|
-
};
|
|
1108
|
+
...(current?.mcp && typeof current.mcp === "object" && !Array.isArray(current.mcp) ? current.mcp : {}),
|
|
1109
|
+
servers: {
|
|
1110
|
+
...retainedServers,
|
|
1111
|
+
...mergedBossServers
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
: {
|
|
1116
|
+
...current,
|
|
1117
|
+
mcpServers: {
|
|
1118
|
+
...retainedServers,
|
|
1119
|
+
...mergedBossServers
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1082
1122
|
|
|
1083
1123
|
ensureDir(path.dirname(filePath));
|
|
1084
1124
|
const before = pathExists(filePath) ? fs.readFileSync(filePath, "utf8") : "";
|
|
@@ -1088,14 +1128,15 @@ function mergeMcpServerConfigFile(filePath, options = {}) {
|
|
|
1088
1128
|
backupFile = `${filePath}.boss-mcp-migration-${new Date().toISOString().replace(/[:.]/g, "-")}.bak`;
|
|
1089
1129
|
fs.writeFileSync(backupFile, before, "utf8");
|
|
1090
1130
|
}
|
|
1091
|
-
fs.writeFileSync(filePath, JSON.stringify(merged, null, 2), "utf8");
|
|
1092
|
-
const updated = before.trim() !== next.trim()
|
|
1093
|
-
return {
|
|
1094
|
-
file: filePath,
|
|
1095
|
-
server:
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1131
|
+
fs.writeFileSync(filePath, JSON.stringify(merged, null, 2), "utf8");
|
|
1132
|
+
const updated = before.trim() !== next.trim();
|
|
1133
|
+
return {
|
|
1134
|
+
file: filePath,
|
|
1135
|
+
server: serverNames[0] || defaultMcpServerName,
|
|
1136
|
+
servers: serverNames,
|
|
1137
|
+
config_shape: useQClawShape ? "qclaw" : "mcpServers",
|
|
1138
|
+
updated,
|
|
1139
|
+
migrated_legacy_servers: migratedLegacyServers,
|
|
1099
1140
|
backup_file: backupFile
|
|
1100
1141
|
};
|
|
1101
1142
|
}
|
|
@@ -1108,12 +1149,13 @@ function installExternalMcpConfigs(options = {}) {
|
|
|
1108
1149
|
try {
|
|
1109
1150
|
const existed = pathExists(target);
|
|
1110
1151
|
const merged = mergeMcpServerConfigFile(target, options);
|
|
1111
|
-
applied.push({
|
|
1112
|
-
file: target,
|
|
1113
|
-
server: merged.server,
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1152
|
+
applied.push({
|
|
1153
|
+
file: target,
|
|
1154
|
+
server: merged.server,
|
|
1155
|
+
servers: merged.servers,
|
|
1156
|
+
created: !existed,
|
|
1157
|
+
updated: merged.updated,
|
|
1158
|
+
migrated_legacy_servers: merged.migrated_legacy_servers,
|
|
1117
1159
|
backup_file: merged.backup_file
|
|
1118
1160
|
});
|
|
1119
1161
|
} catch (error) {
|
|
@@ -2718,6 +2760,7 @@ function printHelp() {
|
|
|
2718
2760
|
console.log(" boss-recommend-mcp launch-chrome Launch or reuse Chrome debug instance and open Boss recommend page");
|
|
2719
2761
|
console.log(" boss-recommend-mcp where Print installed package, skill, and config paths");
|
|
2720
2762
|
console.log(" boss-recommend-mcp install --mcp-launch global-wrapper Use ~/.boss-recommend-mcp/bin wrapper so npm global upgrades affect MCP hosts");
|
|
2763
|
+
console.log(" boss-recommend-mcp start --toolset recommend Start a narrowed MCP server (all|recommend|chat|recruit)");
|
|
2721
2764
|
console.log("");
|
|
2722
2765
|
console.log("Run command:");
|
|
2723
2766
|
console.log(" boss-recommend-mcp prepare-run --instruction \"...\" --overrides-file overrides.json --confirmation-file confirmation.json --rest-level medium");
|
|
@@ -3397,6 +3440,7 @@ export const __testables = {
|
|
|
3397
3440
|
buildBossChatCliInput,
|
|
3398
3441
|
buildDefaultMcpArgs,
|
|
3399
3442
|
buildMcpLaunchConfig,
|
|
3443
|
+
buildMcpConfigFileContent,
|
|
3400
3444
|
ensureGlobalMcpWrapper,
|
|
3401
3445
|
getGlobalMcpWrapperPath,
|
|
3402
3446
|
collectRuntimeDirectories,
|
package/src/index.js
CHANGED
|
@@ -123,8 +123,50 @@ const TOOL_GET_RECRUIT_PIPELINE_RUN = "get_recruit_pipeline_run";
|
|
|
123
123
|
const TOOL_CANCEL_RECRUIT_PIPELINE_RUN = "cancel_recruit_pipeline_run";
|
|
124
124
|
const TOOL_PAUSE_RECRUIT_PIPELINE_RUN = "pause_recruit_pipeline_run";
|
|
125
125
|
const TOOL_RESUME_RECRUIT_PIPELINE_RUN = "resume_recruit_pipeline_run";
|
|
126
|
-
|
|
127
|
-
const SERVER_NAME = "boss-recommend-mcp";
|
|
126
|
+
|
|
127
|
+
const SERVER_NAME = "boss-recommend-mcp";
|
|
128
|
+
const MCP_TOOLSET_ENV = "BOSS_RECOMMEND_MCP_TOOLSET";
|
|
129
|
+
const MCP_TOOLSET_ALL = "all";
|
|
130
|
+
const MCP_TOOLSET_RECOMMEND = "recommend";
|
|
131
|
+
const MCP_TOOLSET_CHAT = "chat";
|
|
132
|
+
const MCP_TOOLSET_RECRUIT = "recruit";
|
|
133
|
+
const VALID_MCP_TOOLSETS = new Set([
|
|
134
|
+
MCP_TOOLSET_ALL,
|
|
135
|
+
MCP_TOOLSET_RECOMMEND,
|
|
136
|
+
MCP_TOOLSET_CHAT,
|
|
137
|
+
MCP_TOOLSET_RECRUIT
|
|
138
|
+
]);
|
|
139
|
+
const RECOMMEND_TOOL_NAMES = new Set([
|
|
140
|
+
TOOL_LIST_RECOMMEND_JOBS,
|
|
141
|
+
TOOL_RUN_RECOMMEND,
|
|
142
|
+
TOOL_START_RUN,
|
|
143
|
+
TOOL_PREPARE_RUN,
|
|
144
|
+
TOOL_SCHEDULE_RUN,
|
|
145
|
+
TOOL_GET_SCHEDULED_RUN,
|
|
146
|
+
TOOL_GET_RUN,
|
|
147
|
+
TOOL_LIST_RUNS,
|
|
148
|
+
TOOL_CANCEL_RUN,
|
|
149
|
+
TOOL_PAUSE_RUN,
|
|
150
|
+
TOOL_RESUME_RUN
|
|
151
|
+
]);
|
|
152
|
+
const BOSS_CHAT_TOOL_NAMES = new Set([
|
|
153
|
+
TOOL_BOSS_CHAT_HEALTH_CHECK,
|
|
154
|
+
TOOL_BOSS_CHAT_LIST_JOBS,
|
|
155
|
+
TOOL_BOSS_CHAT_PREPARE_RUN,
|
|
156
|
+
TOOL_BOSS_CHAT_START_RUN,
|
|
157
|
+
TOOL_BOSS_CHAT_GET_RUN,
|
|
158
|
+
TOOL_BOSS_CHAT_PAUSE_RUN,
|
|
159
|
+
TOOL_BOSS_CHAT_RESUME_RUN,
|
|
160
|
+
TOOL_BOSS_CHAT_CANCEL_RUN
|
|
161
|
+
]);
|
|
162
|
+
const RECRUIT_TOOL_NAMES = new Set([
|
|
163
|
+
TOOL_RUN_RECRUIT_PIPELINE,
|
|
164
|
+
TOOL_START_RECRUIT_PIPELINE_RUN,
|
|
165
|
+
TOOL_GET_RECRUIT_PIPELINE_RUN,
|
|
166
|
+
TOOL_CANCEL_RECRUIT_PIPELINE_RUN,
|
|
167
|
+
TOOL_PAUSE_RECRUIT_PIPELINE_RUN,
|
|
168
|
+
TOOL_RESUME_RECRUIT_PIPELINE_RUN
|
|
169
|
+
]);
|
|
128
170
|
const FRAMING_UNKNOWN = "unknown";
|
|
129
171
|
const FRAMING_HEADER = "header";
|
|
130
172
|
const FRAMING_LINE = "line";
|
|
@@ -454,6 +496,26 @@ function readRawRunState(runId) {
|
|
|
454
496
|
}
|
|
455
497
|
}
|
|
456
498
|
|
|
499
|
+
function compactProgressForList(progress = {}) {
|
|
500
|
+
const compact = {};
|
|
501
|
+
for (const key of [
|
|
502
|
+
"processed",
|
|
503
|
+
"screened",
|
|
504
|
+
"passed",
|
|
505
|
+
"skipped",
|
|
506
|
+
"target_count",
|
|
507
|
+
"card_count",
|
|
508
|
+
"detail_opened",
|
|
509
|
+
"greet_count",
|
|
510
|
+
"post_action_clicked"
|
|
511
|
+
]) {
|
|
512
|
+
if (Number.isFinite(progress?.[key])) {
|
|
513
|
+
compact[key] = progress[key];
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return compact;
|
|
517
|
+
}
|
|
518
|
+
|
|
457
519
|
function getRunSortTime(run = {}, fallbackMs = 0) {
|
|
458
520
|
for (const key of ["updated_at", "heartbeat_at", "completed_at", "started_at", "updatedAt", "completedAt", "startedAt"]) {
|
|
459
521
|
const ms = Date.parse(run?.[key] || "");
|
|
@@ -477,7 +539,7 @@ function compactRunForList(run = {}) {
|
|
|
477
539
|
heartbeat_at: run.heartbeat_at || null,
|
|
478
540
|
completed_at: run.completed_at || run.completedAt || null,
|
|
479
541
|
pid: Number.isInteger(run.pid) && run.pid > 0 ? run.pid : null,
|
|
480
|
-
progress: run.progress
|
|
542
|
+
progress: compactProgressForList(run.progress),
|
|
481
543
|
last_message: normalizeText(run.last_message || error?.message || ""),
|
|
482
544
|
control: {
|
|
483
545
|
pause_requested: run.control?.pause_requested === true,
|
|
@@ -494,11 +556,10 @@ function compactRunForList(run = {}) {
|
|
|
494
556
|
report_json: normalizeText(result.report_json || result.result?.report_json || ""),
|
|
495
557
|
checkpoint_path: normalizeText(result.checkpoint_path || result.result?.checkpoint_path || "")
|
|
496
558
|
} : null,
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
worker_stderr_path: normalizeText(run.resume?.worker_stderr_path || "")
|
|
559
|
+
artifacts: {
|
|
560
|
+
output_csv: normalizeText(result?.output_csv || result?.result?.output_csv || ""),
|
|
561
|
+
report_json: normalizeText(result?.report_json || result?.result?.report_json || ""),
|
|
562
|
+
checkpoint_path: normalizeText(result?.checkpoint_path || result?.result?.checkpoint_path || "")
|
|
502
563
|
}
|
|
503
564
|
};
|
|
504
565
|
}
|
|
@@ -1294,8 +1355,154 @@ function createScheduleRunInputSchema() {
|
|
|
1294
1355
|
};
|
|
1295
1356
|
}
|
|
1296
1357
|
|
|
1297
|
-
function
|
|
1298
|
-
|
|
1358
|
+
function createCompactRunInputSchema() {
|
|
1359
|
+
const targetCountSchema = {
|
|
1360
|
+
anyOf: [
|
|
1361
|
+
{ type: "integer", minimum: 0 },
|
|
1362
|
+
{ type: "string" }
|
|
1363
|
+
],
|
|
1364
|
+
description: "目标通过人数;扫到底/不限可传 all"
|
|
1365
|
+
};
|
|
1366
|
+
return {
|
|
1367
|
+
type: "object",
|
|
1368
|
+
properties: {
|
|
1369
|
+
instruction: {
|
|
1370
|
+
type: "string",
|
|
1371
|
+
description: "用户原始筛选标准/任务说明;正式启动时逐字复用"
|
|
1372
|
+
},
|
|
1373
|
+
confirmation: {
|
|
1374
|
+
type: "object",
|
|
1375
|
+
properties: {
|
|
1376
|
+
final_confirmed: {
|
|
1377
|
+
type: "boolean",
|
|
1378
|
+
description: "用户完成总确认后传 true"
|
|
1379
|
+
}
|
|
1380
|
+
},
|
|
1381
|
+
additionalProperties: true
|
|
1382
|
+
},
|
|
1383
|
+
overrides: {
|
|
1384
|
+
type: "object",
|
|
1385
|
+
properties: {
|
|
1386
|
+
page_scope: { type: "string", enum: ["recommend", "latest", "featured"] },
|
|
1387
|
+
school_tag: {
|
|
1388
|
+
anyOf: [
|
|
1389
|
+
{ type: "string" },
|
|
1390
|
+
{ type: "array", items: { type: "string" } }
|
|
1391
|
+
]
|
|
1392
|
+
},
|
|
1393
|
+
degree: {
|
|
1394
|
+
anyOf: [
|
|
1395
|
+
{ type: "string" },
|
|
1396
|
+
{ type: "array", items: { type: "string" } }
|
|
1397
|
+
]
|
|
1398
|
+
},
|
|
1399
|
+
gender: { type: "string" },
|
|
1400
|
+
recent_not_view: { type: "string" },
|
|
1401
|
+
criteria: { type: "string" },
|
|
1402
|
+
target_count: targetCountSchema,
|
|
1403
|
+
post_action: { type: "string", enum: ["greet", "none"] },
|
|
1404
|
+
max_greet_count: targetCountSchema,
|
|
1405
|
+
job: { type: "string" }
|
|
1406
|
+
},
|
|
1407
|
+
additionalProperties: true
|
|
1408
|
+
},
|
|
1409
|
+
human_behavior: {
|
|
1410
|
+
type: "object",
|
|
1411
|
+
properties: {
|
|
1412
|
+
restLevel: { type: "string", enum: ["low", "medium", "high"] },
|
|
1413
|
+
rest_level: { type: "string", enum: ["low", "medium", "high"] }
|
|
1414
|
+
},
|
|
1415
|
+
additionalProperties: true
|
|
1416
|
+
},
|
|
1417
|
+
host: { type: "string" },
|
|
1418
|
+
port: { type: "integer", minimum: 1 },
|
|
1419
|
+
slow_live: { type: "boolean" },
|
|
1420
|
+
delay_ms: { type: "integer", minimum: 0 },
|
|
1421
|
+
detail_limit: { type: "integer", minimum: 0 },
|
|
1422
|
+
execute_post_action: { type: "boolean" },
|
|
1423
|
+
no_filter: { type: "boolean" },
|
|
1424
|
+
dry_run: { type: "boolean" }
|
|
1425
|
+
},
|
|
1426
|
+
required: ["instruction"],
|
|
1427
|
+
additionalProperties: true
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
function createCompactScheduleRunInputSchema() {
|
|
1432
|
+
const base = createCompactRunInputSchema();
|
|
1433
|
+
return {
|
|
1434
|
+
...base,
|
|
1435
|
+
properties: {
|
|
1436
|
+
...base.properties,
|
|
1437
|
+
schedule_id: {
|
|
1438
|
+
type: "string",
|
|
1439
|
+
description: "可选,自定义定时任务 id;默认自动生成"
|
|
1440
|
+
},
|
|
1441
|
+
schedule_run_at: {
|
|
1442
|
+
type: "string",
|
|
1443
|
+
description: "ISO 时间字符串"
|
|
1444
|
+
},
|
|
1445
|
+
schedule_delay_minutes: {
|
|
1446
|
+
type: "number",
|
|
1447
|
+
minimum: 0
|
|
1448
|
+
},
|
|
1449
|
+
schedule_delay_seconds: {
|
|
1450
|
+
type: "number",
|
|
1451
|
+
minimum: 0
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
function normalizeMcpToolset(value) {
|
|
1458
|
+
const raw = String(value || "").trim().toLowerCase().replace(/[\s_]+/g, "-");
|
|
1459
|
+
if (!raw) return MCP_TOOLSET_ALL;
|
|
1460
|
+
if (raw === "boss-recommend" || raw === "recommend-page" || raw === "recommended") return MCP_TOOLSET_RECOMMEND;
|
|
1461
|
+
if (raw === "boss-chat" || raw === "chat-only" || raw === "chat-page") return MCP_TOOLSET_CHAT;
|
|
1462
|
+
if (raw === "boss-recruit" || raw === "search" || raw === "search-page" || raw === "recruit-page") return MCP_TOOLSET_RECRUIT;
|
|
1463
|
+
return VALID_MCP_TOOLSETS.has(raw) ? raw : MCP_TOOLSET_ALL;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
function getConfiguredMcpToolset(argv = process.argv, env = process.env) {
|
|
1467
|
+
const args = Array.isArray(argv) ? argv.slice(2) : [];
|
|
1468
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1469
|
+
const arg = String(args[index] || "");
|
|
1470
|
+
if (arg === "--toolset" || arg === "--tools") {
|
|
1471
|
+
return normalizeMcpToolset(args[index + 1]);
|
|
1472
|
+
}
|
|
1473
|
+
if (arg.startsWith("--toolset=")) {
|
|
1474
|
+
return normalizeMcpToolset(arg.slice("--toolset=".length));
|
|
1475
|
+
}
|
|
1476
|
+
if (arg.startsWith("--tools=")) {
|
|
1477
|
+
return normalizeMcpToolset(arg.slice("--tools=".length));
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
return normalizeMcpToolset(env?.[MCP_TOOLSET_ENV]);
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
function toolNamesForMcpToolset(toolset) {
|
|
1484
|
+
const normalized = normalizeMcpToolset(toolset);
|
|
1485
|
+
if (normalized === MCP_TOOLSET_RECOMMEND) return RECOMMEND_TOOL_NAMES;
|
|
1486
|
+
if (normalized === MCP_TOOLSET_CHAT) return BOSS_CHAT_TOOL_NAMES;
|
|
1487
|
+
if (normalized === MCP_TOOLSET_RECRUIT) return RECRUIT_TOOL_NAMES;
|
|
1488
|
+
return null;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
function filterToolsForMcpToolset(tools, toolset = getConfiguredMcpToolset()) {
|
|
1492
|
+
const names = toolNamesForMcpToolset(toolset);
|
|
1493
|
+
if (!names) return tools;
|
|
1494
|
+
return tools.filter((tool) => names.has(tool.name));
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
function createToolsSchema(toolset = getConfiguredMcpToolset()) {
|
|
1498
|
+
const normalizedToolset = normalizeMcpToolset(toolset);
|
|
1499
|
+
const runInputSchema = normalizedToolset === MCP_TOOLSET_RECOMMEND
|
|
1500
|
+
? createCompactRunInputSchema()
|
|
1501
|
+
: createRunInputSchema();
|
|
1502
|
+
const scheduleRunInputSchema = normalizedToolset === MCP_TOOLSET_RECOMMEND
|
|
1503
|
+
? createCompactScheduleRunInputSchema()
|
|
1504
|
+
: createScheduleRunInputSchema();
|
|
1505
|
+
const tools = [
|
|
1299
1506
|
{
|
|
1300
1507
|
name: TOOL_BOSS_CHAT_HEALTH_CHECK,
|
|
1301
1508
|
description: "Boss 聊天页/chat-only 健康检查。chat-only、未读、全部聊天、求简历等任务必须先走 boss-chat 工具,不要调用 list_recommend_jobs 或 start_recommend_pipeline_run。",
|
|
@@ -1402,22 +1609,22 @@ function createToolsSchema() {
|
|
|
1402
1609
|
{
|
|
1403
1610
|
name: TOOL_RUN_RECOMMEND,
|
|
1404
1611
|
description: "立即通过原生 MCP 启动 Boss 推荐页筛选。它是 start_recommend_pipeline_run 的短别名;用户已经确认并要现在启动时,优先调用本工具,不需要先调用 prepare_recommend_pipeline_run。必须作为 MCP tool call 调用,禁止通过 terminal/shell/run_command/PowerShell/CLI/manual JSON-RPC 代替,也不要用 schedule_recommend_pipeline_run 冒充立即启动。",
|
|
1405
|
-
inputSchema:
|
|
1612
|
+
inputSchema: runInputSchema
|
|
1406
1613
|
},
|
|
1407
1614
|
{
|
|
1408
1615
|
name: TOOL_START_RUN,
|
|
1409
1616
|
description: "立即通过原生 MCP 异步启动 Boss 推荐页流水线(含同步门禁预检)。用户已经确认并要现在启动时,优先调用本工具或 run_recommend,不需要先调用 prepare_recommend_pipeline_run。必须作为 MCP tool call 调用,禁止通过 terminal/shell/run_command/PowerShell/CLI/manual JSON-RPC 代替,也不要用 schedule_recommend_pipeline_run 冒充立即启动。",
|
|
1410
|
-
inputSchema:
|
|
1617
|
+
inputSchema: runInputSchema
|
|
1411
1618
|
},
|
|
1412
1619
|
{
|
|
1413
1620
|
name: TOOL_PREPARE_RUN,
|
|
1414
1621
|
description: "只校验 Boss 推荐页流水线参数是否完整;不会启动筛选任务。主要用于显式预检或稍后/cron/定时启动前校验。若用户要现在运行,READY/cron_ready=true 后必须继续调用本 MCP server 的 run_recommend 或 start_recommend_pipeline_run;prepare 能返回结果就证明原生 MCP 可用,禁止改用 terminal/shell/run_command/PowerShell/CLI/manual JSON-RPC,也禁止再次调用 prepare 试图启动。",
|
|
1415
|
-
inputSchema:
|
|
1622
|
+
inputSchema: runInputSchema
|
|
1416
1623
|
},
|
|
1417
1624
|
{
|
|
1418
1625
|
name: TOOL_SCHEDULE_RUN,
|
|
1419
1626
|
description: "只用于用户明确要求稍后/cron/定时启动的 package-owned Boss 推荐页定时任务。若用户要现在运行,必须调用 run_recommend 或 start_recommend_pipeline_run,不要用短延迟 schedule 冒充立即启动。schedule 会先校验 READY/cron_ready,再保存完整 payload,并由 detached scheduler 到点直接启动,不依赖 AI harness 自己拼 shell cron。",
|
|
1420
|
-
inputSchema:
|
|
1627
|
+
inputSchema: scheduleRunInputSchema
|
|
1421
1628
|
},
|
|
1422
1629
|
{
|
|
1423
1630
|
name: TOOL_GET_SCHEDULED_RUN,
|
|
@@ -1558,6 +1765,7 @@ function createToolsSchema() {
|
|
|
1558
1765
|
inputSchema: createRecruitRunIdInputSchema()
|
|
1559
1766
|
}
|
|
1560
1767
|
];
|
|
1768
|
+
return filterToolsForMcpToolset(tools, toolset);
|
|
1561
1769
|
}
|
|
1562
1770
|
|
|
1563
1771
|
function createToolResultResponse(id, payload, isError = false) {
|
|
@@ -2859,10 +3067,19 @@ async function handleRequest(message, workspaceRoot) {
|
|
|
2859
3067
|
};
|
|
2860
3068
|
}
|
|
2861
3069
|
|
|
2862
|
-
if (method === "tools/call") {
|
|
2863
|
-
const toolName = params?.name;
|
|
2864
|
-
const args = params?.arguments || {};
|
|
2865
|
-
|
|
3070
|
+
if (method === "tools/call") {
|
|
3071
|
+
const toolName = params?.name;
|
|
3072
|
+
const args = params?.arguments || {};
|
|
3073
|
+
const toolset = getConfiguredMcpToolset();
|
|
3074
|
+
const visibleToolNames = new Set(createToolsSchema(toolset).map((tool) => tool.name));
|
|
3075
|
+
if (!visibleToolNames.has(toolName)) {
|
|
3076
|
+
return createJsonRpcError(
|
|
3077
|
+
id,
|
|
3078
|
+
-32602,
|
|
3079
|
+
`Tool ${toolName || ""} is not available in the ${toolset} boss-recommend-mcp toolset. Use the MCP server/toolset that exposes this domain instead of terminal or CLI fallback.`
|
|
3080
|
+
);
|
|
3081
|
+
}
|
|
3082
|
+
|
|
2866
3083
|
if ([TOOL_RUN_RECOMMEND, TOOL_START_RUN].includes(toolName)) {
|
|
2867
3084
|
const inputError = validateRunArgs(args);
|
|
2868
3085
|
if (inputError) {
|
|
@@ -3105,6 +3322,9 @@ export function startServer() {
|
|
|
3105
3322
|
}
|
|
3106
3323
|
|
|
3107
3324
|
export const __testables = {
|
|
3325
|
+
createToolsSchema,
|
|
3326
|
+
getConfiguredMcpToolset,
|
|
3327
|
+
normalizeMcpToolset,
|
|
3108
3328
|
handleRequest,
|
|
3109
3329
|
runDetachedWorkerForTests(options = {}) {
|
|
3110
3330
|
return runDetachedWorker(options);
|