@reconcrap/boss-recommend-mcp 1.3.7 → 1.3.8

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
@@ -201,10 +201,12 @@ node src/cli.js chat run --job "算法工程师" --start-from unread --criteria
201
201
 
202
202
  - CLI:
203
203
  - `boss-recommend-mcp chat health-check`
204
+ - `boss-recommend-mcp chat prepare-run`
204
205
  - `boss-recommend-mcp chat run --job "算法工程师" --start-from unread --targetCount 20 --criteria "有 AI Agent 经验"`(后台启动,不自动轮询)
205
206
  - `boss-recommend-mcp chat start-run|get-run|pause-run|resume-run|cancel-run`
206
207
  - MCP:
207
208
  - `boss_chat_health_check`
209
+ - `prepare_boss_chat_run`
208
210
  - `start_boss_chat_run`
209
211
  - `get_boss_chat_run`
210
212
  - `pause_boss_chat_run`
@@ -213,13 +215,14 @@ node src/cli.js chat run --job "算法工程师" --start-from unread --criteria
213
215
 
214
216
  chat-only 交互建议:
215
217
 
216
- - 先调用一次 `start_boss_chat_run`(可不带参数),服务会先导航到 `https://www.zhipin.com/web/chat/index` 并返回 `NEED_INPUT`,其中包含岗位 `job_options` 与待补字段。
217
- - 然后基于 `job_options` 让用户选择 `job`,并补齐 `start_from`、`target_count`、`criteria` 后再次调用 `start_boss_chat_run` 启动任务。
218
+ - 先调用一次 `prepare_boss_chat_run`(可不带参数),服务会先导航到 `https://www.zhipin.com/web/chat/index` 并返回 `NEED_INPUT`,其中包含岗位 `job_options` 与待补字段。
219
+ - 然后基于 `job_options` 让用户选择 `job`,并补齐 `start_from`、`target_count`、`criteria` 后调用 `start_boss_chat_run` 启动任务。
218
220
  - `target_count` 支持正整数;若用户给出 `全部候选人` / `所有候选人`,会自动按不限(扫到底)处理。
219
221
 
220
222
  Trae-CN / 长对话防循环建议:
221
223
 
222
- - 固定流程:`boss_chat_health_check` -> `start_boss_chat_run(空参可)` -> 一次性补齐 `job/start_from/target_count/criteria` -> 再次 `start_boss_chat_run`。
224
+ - 固定流程:`boss_chat_health_check` -> `prepare_boss_chat_run(空参可)` -> 一次性补齐 `job/start_from/target_count/criteria` -> `start_boss_chat_run`。
225
+ - `start_boss_chat_run` 的工具 schema 已把 `job/start_from/target_count/criteria` 标记为必填;不要用它获取岗位列表。
223
226
  - `start_boss_chat_run` 返回 `ACCEPTED` 后直接结束当前回合,不要自动轮询。
224
227
  - 缺参或校验失败时,一次性列出全部缺失/错误项,避免重复同一句提示触发宿主“陷入循环”保护。
225
228
  - 仅当用户明确要求“查进度”时再调用 `get_boss_chat_run`。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reconcrap/boss-recommend-mcp",
3
- "version": "1.3.7",
3
+ "version": "1.3.8",
4
4
  "description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
5
5
  "keywords": [
6
6
  "boss",
@@ -13,7 +13,7 @@ Please run a Boss chat-only task (do not switch to recommend flow).
13
13
 
14
14
  Execution order:
15
15
  1) Call boss_chat_health_check.
16
- 2) Call start_boss_chat_run once (empty params allowed) to fetch job_options and missing fields.
16
+ 2) Call prepare_boss_chat_run once (empty params allowed) to fetch job_options and missing fields.
17
17
  3) Ask for these required fields in one shot: job, start_from (unread/all), target_count, criteria.
18
18
  4) After user reply, call start_boss_chat_run exactly once to start the run.
19
19
  5) If ACCEPTED, reply only with run_id and "task started"; no auto polling.
@@ -21,6 +21,7 @@ Execution order:
21
21
  Anti-loop rules:
22
22
  - Do not repeat the same sentence across turns.
23
23
  - On validation errors, list all missing/invalid fields once.
24
+ - Do not use start_boss_chat_run for preflight. It is only for the final start call and must include job/start_from/target_count/criteria.
24
25
  - Do not call start_boss_chat_run repeatedly in one turn.
25
26
  - Do not call get_boss_chat_run unless user explicitly asks for progress.
26
27
 
@@ -30,4 +31,5 @@ target_count mapping:
30
31
  - `全部候选人` / `所有候选人` must also be treated as unlimited.
31
32
  - Always write the argument key as `target_count`.
32
33
  - For unlimited mode, send `"target_count": "all"` in the tool call.
34
+ - If start_boss_chat_run returns NEED_INPUT for `target_count`, the previous tool call omitted the argument. Retry once using `next_call_example` and include `"target_count": "all"` or a positive integer.
33
35
  ```
@@ -14,6 +14,7 @@ description: "Use when users want Boss chat-page screening/outreach via the bund
14
14
  ## Tool Routing
15
15
 
16
16
  - 健康检查:`boss_chat_health_check`
17
+ - 预备并获取岗位列表:`prepare_boss_chat_run`
17
18
  - 启动异步任务:`start_boss_chat_run`
18
19
  - 查询进度:`get_boss_chat_run`
19
20
  - 暂停:`pause_boss_chat_run`
@@ -29,7 +30,6 @@ description: "Use when users want Boss chat-page screening/outreach via the bund
29
30
 
30
31
  可选:
31
32
 
32
- - `target_count`
33
33
  - `profile`(默认 `default`)
34
34
  - `port`
35
35
  - `dry_run`
@@ -57,7 +57,9 @@ description: "Use when users want Boss chat-page screening/outreach via the bund
57
57
  - 当用户选择“扫到底/全部候选人/所有候选人”时,调用参数统一写:`"target_count": "all"`。
58
58
  - 禁止 agent 自行补全 `job/start_from/criteria` 并直接执行,必须由用户明确给出或确认。
59
59
  - chat-only 启动流程必须先进入聊天页并拉取岗位列表,再让用户从列表中选择 `job`。
60
- - 允许先用空参调用 `start_boss_chat_run` 触发 `NEED_INPUT`;若返回了 `job_options`,必须完整展示所有岗位选项给用户确认。
60
+ - 必须先用空参调用 `prepare_boss_chat_run` 获取 `job_options`;不要用 `start_boss_chat_run` 做预备调用。
61
+ - `start_boss_chat_run` 只能用于真正启动,必须一次性传齐 `job` / `start_from` / `target_count` / `criteria`。
62
+ - 若 `start_boss_chat_run` 返回 `NEED_INPUT` 且 `missing_fields` 包含 `target_count`,说明你没有把用户选择写入工具参数;下一次调用必须照 `next_call_example` 原样补上 `"target_count": "all"` 或正整数,不要重复空调用。
61
63
  - 默认不自动轮询;只有用户要求查进度时才调用 `get_boss_chat_run`。
62
64
  - `start_boss_chat_run` 返回 `ACCEPTED` 后,默认立即结束当前回合,不得主动连续调用 `get_boss_chat_run`。
63
65
  - 只有当用户明确给出“轮询频率/间隔”(例如“每30分钟查一次”)时,才允许按该频率查询进度。
@@ -73,6 +75,6 @@ description: "Use when users want Boss chat-page screening/outreach via the bund
73
75
  ## Response Style
74
76
 
75
77
  - 用结构化中文。
76
- - 首轮建议先调用一次 `start_boss_chat_run`(可空参)获取 `job_options` 与 `pending_questions`。
78
+ - 首轮建议先调用一次 `prepare_boss_chat_run`(可空参)获取 `job_options` 与 `pending_questions`。
77
79
  - 缺参时必须逐项确认:`job`(来自岗位列表)、`start_from`(`unread|all`)、`target_count`、`criteria`。
78
80
  - 若健康检查失败,明确提示共享配置文件 `screening-config.json` 不可用。
package/src/boss-chat.js CHANGED
@@ -508,7 +508,23 @@ export async function startBossChatRun({ workspaceRoot, input = {} }) {
508
508
  }
509
509
 
510
510
  export async function prepareBossChatRun({ workspaceRoot, input = {} }) {
511
- return (await spawnBossChatCli({ workspaceRoot, command: "prepare-run", input })).payload;
511
+ const payload = (await spawnBossChatCli({ workspaceRoot, command: "prepare-run", input })).payload;
512
+ if (payload?.status !== "NEED_INPUT") return payload;
513
+
514
+ const missingFields = getMissingBossChatStartFields(input);
515
+ const pendingQuestions = Array.isArray(payload?.pending_questions)
516
+ ? payload.pending_questions.filter((item) => (
517
+ missingFields.length === 0 || missingFields.includes(String(item?.field || ""))
518
+ ))
519
+ : [];
520
+ const nextCallExample = buildNextCallExample(input, missingFields);
521
+ return {
522
+ ...payload,
523
+ required_fields: CHAT_REQUIRED_FIELDS.slice(),
524
+ missing_fields: missingFields,
525
+ pending_questions: normalizePendingQuestions(pendingQuestions),
526
+ ...(nextCallExample ? { next_call_example: nextCallExample } : {})
527
+ };
512
528
  }
513
529
 
514
530
  export async function getBossChatRun({ workspaceRoot, input = {} }) {
package/src/cli.js CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  getBossChatHealthCheck,
22
22
  getBossChatRun,
23
23
  pauseBossChatRun,
24
+ prepareBossChatRun,
24
25
  resumeBossChatRun,
25
26
  startBossChatRun
26
27
  } from "./boss-chat.js";
@@ -1360,6 +1361,14 @@ async function runBossChatCliCommand(subcommand, options = {}) {
1360
1361
  return;
1361
1362
  }
1362
1363
 
1364
+ if (subcommand === "prepare-run") {
1365
+ printJson(await prepareBossChatRun({
1366
+ workspaceRoot,
1367
+ input: buildBossChatCliInput(options)
1368
+ }));
1369
+ return;
1370
+ }
1371
+
1363
1372
  if (subcommand === "run") {
1364
1373
  printJson(await startBossChatRun({
1365
1374
  workspaceRoot,
package/src/index.js CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  getBossChatHealthCheck,
14
14
  getBossChatRun,
15
15
  pauseBossChatRun,
16
+ prepareBossChatRun,
16
17
  resumeBossChatRun,
17
18
  startBossChatRun
18
19
  } from "./boss-chat.js";
@@ -51,6 +52,7 @@ const TOOL_RUN_FEATURED_CALIBRATION = "run_featured_calibration";
51
52
  const TOOL_GET_FEATURED_CALIBRATION_STATUS = "get_featured_calibration_status";
52
53
  const TOOL_RUN_RECOMMEND_SELF_HEAL = "run_recommend_self_heal";
53
54
  const TOOL_BOSS_CHAT_HEALTH_CHECK = "boss_chat_health_check";
55
+ const TOOL_BOSS_CHAT_PREPARE_RUN = "prepare_boss_chat_run";
54
56
  const TOOL_BOSS_CHAT_START_RUN = "start_boss_chat_run";
55
57
  const TOOL_BOSS_CHAT_GET_RUN = "get_boss_chat_run";
56
58
  const TOOL_BOSS_CHAT_PAUSE_RUN = "pause_boss_chat_run";
@@ -413,8 +415,8 @@ function createRunInputSchema() {
413
415
  };
414
416
  }
415
417
 
416
- function createBossChatStartInputSchema() {
417
- return {
418
+ function createBossChatStartInputSchema({ requireFullInput = false } = {}) {
419
+ const schema = {
418
420
  type: "object",
419
421
  properties: {
420
422
  profile: {
@@ -459,6 +461,10 @@ function createBossChatStartInputSchema() {
459
461
  },
460
462
  additionalProperties: false
461
463
  };
464
+ if (requireFullInput) {
465
+ schema.required = ["job", "start_from", "target_count", "criteria"];
466
+ }
467
+ return schema;
462
468
  }
463
469
 
464
470
  function createRunFeaturedCalibrationInputSchema() {
@@ -610,10 +616,15 @@ function createToolsSchema() {
610
616
  }
611
617
  },
612
618
  {
613
- name: TOOL_BOSS_CHAT_START_RUN,
614
- description: "异步启动一次 boss-chat 任务。若缺少必填参数会先返回 NEED_INPUT(含岗位列表与待确认字段)。",
619
+ name: TOOL_BOSS_CHAT_PREPARE_RUN,
620
+ description: "预备一次 boss-chat 任务:只导航聊天页并返回岗位列表与待补字段,不会启动任务。用它先获取 job_options。",
615
621
  inputSchema: createBossChatStartInputSchema()
616
622
  },
623
+ {
624
+ name: TOOL_BOSS_CHAT_START_RUN,
625
+ description: "异步启动一次 boss-chat 任务。必须一次性提供 job、start_from、target_count、criteria;扫到底请传 target_count=\"all\"。",
626
+ inputSchema: createBossChatStartInputSchema({ requireFullInput: true })
627
+ },
617
628
  {
618
629
  name: TOOL_BOSS_CHAT_GET_RUN,
619
630
  description: "查询 boss-chat run_id 的当前状态。",
@@ -1776,6 +1787,10 @@ function handleBossChatHealthCheckTool(workspaceRoot, args) {
1776
1787
  return getBossChatHealthCheck(workspaceRoot, args);
1777
1788
  }
1778
1789
 
1790
+ async function handleBossChatPrepareRunTool({ workspaceRoot, args }) {
1791
+ return prepareBossChatRun({ workspaceRoot, input: args });
1792
+ }
1793
+
1779
1794
  async function handleBossChatStartRunTool({ workspaceRoot, args }) {
1780
1795
  return startBossChatRun({ workspaceRoot, input: args });
1781
1796
  }
@@ -1859,7 +1874,7 @@ async function handleRequest(message, workspaceRoot) {
1859
1874
  }
1860
1875
  }
1861
1876
 
1862
- if (toolName === TOOL_BOSS_CHAT_START_RUN) {
1877
+ if ([TOOL_BOSS_CHAT_PREPARE_RUN, TOOL_BOSS_CHAT_START_RUN].includes(toolName)) {
1863
1878
  const inputError = validateBossChatStartArgs(args);
1864
1879
  if (inputError) {
1865
1880
  return createJsonRpcError(id, -32602, inputError);
@@ -1901,6 +1916,8 @@ async function handleRequest(message, workspaceRoot) {
1901
1916
  payload = await handleRunRecommendSelfHealTool({ workspaceRoot, args });
1902
1917
  } else if (toolName === TOOL_BOSS_CHAT_HEALTH_CHECK) {
1903
1918
  payload = handleBossChatHealthCheckTool(workspaceRoot, args);
1919
+ } else if (toolName === TOOL_BOSS_CHAT_PREPARE_RUN) {
1920
+ payload = await handleBossChatPrepareRunTool({ workspaceRoot, args });
1904
1921
  } else if (toolName === TOOL_BOSS_CHAT_START_RUN) {
1905
1922
  payload = await handleBossChatStartRunTool({ workspaceRoot, args });
1906
1923
  } else if (toolName === TOOL_BOSS_CHAT_GET_RUN) {
@@ -8,6 +8,7 @@ import {
8
8
  getBossChatHealthCheck,
9
9
  getBossChatRun,
10
10
  pauseBossChatRun,
11
+ prepareBossChatRun,
11
12
  resumeBossChatRun,
12
13
  startBossChatRun
13
14
  } from "./boss-chat.js";
@@ -17,6 +18,7 @@ import { __testables as indexTestables } from "./index.js";
17
18
  const { handleRequest } = indexTestables;
18
19
 
19
20
  const TOOL_BOSS_CHAT_HEALTH_CHECK = "boss_chat_health_check";
21
+ const TOOL_BOSS_CHAT_PREPARE_RUN = "prepare_boss_chat_run";
20
22
  const TOOL_BOSS_CHAT_START_RUN = "start_boss_chat_run";
21
23
  const TOOL_BOSS_CHAT_GET_RUN = "get_boss_chat_run";
22
24
  const TOOL_BOSS_CHAT_PAUSE_RUN = "pause_boss_chat_run";
@@ -202,6 +204,16 @@ async function testBossChatAdapterShouldResolveSharedConfigAndInvokeLocalCli() {
202
204
  assert.equal(health.shared_llm_config, true);
203
205
  assert.equal(health.debug_port, 9666);
204
206
 
207
+ const prepared = await prepareBossChatRun({
208
+ workspaceRoot,
209
+ input: {}
210
+ });
211
+ assert.equal(prepared.status, "NEED_INPUT");
212
+ assert.deepEqual(prepared.missing_fields, ["job", "start_from", "target_count", "criteria"]);
213
+ const preparedTargetQuestion = prepared.pending_questions.find((item) => item.field === "target_count");
214
+ assert.equal(preparedTargetQuestion.argument_name, "target_count");
215
+ assert.equal(prepared.next_call_example.target_count, "all");
216
+
205
217
  const preflight = await startBossChatRun({
206
218
  workspaceRoot,
207
219
  input: {}
@@ -301,6 +313,23 @@ async function testBossChatAdapterShouldResolveSharedConfigAndInvokeLocalCli() {
301
313
 
302
314
  async function testBossChatMcpToolsShouldValidateAndRoute() {
303
315
  await withBossChatWorkspace(async (workspaceRoot) => {
316
+ const toolsResponse = await handleRequest({
317
+ jsonrpc: "2.0",
318
+ id: 10,
319
+ method: "tools/list",
320
+ params: {}
321
+ }, workspaceRoot);
322
+ const tools = toolsResponse.result.tools;
323
+ const prepareToolSchema = tools.find((item) => item.name === TOOL_BOSS_CHAT_PREPARE_RUN).inputSchema;
324
+ const startToolSchema = tools.find((item) => item.name === TOOL_BOSS_CHAT_START_RUN).inputSchema;
325
+ assert.equal(prepareToolSchema.required, undefined);
326
+ assert.deepEqual(startToolSchema.required, ["job", "start_from", "target_count", "criteria"]);
327
+
328
+ const prepared = await callTool(workspaceRoot, TOOL_BOSS_CHAT_PREPARE_RUN, {}, 101);
329
+ assert.equal(prepared.status, "NEED_INPUT");
330
+ assert.deepEqual(prepared.missing_fields, ["job", "start_from", "target_count", "criteria"]);
331
+ assert.equal(prepared.pending_questions.find((item) => item.field === "target_count").argument_name, "target_count");
332
+
304
333
  const needInput = await callTool(workspaceRoot, TOOL_BOSS_CHAT_START_RUN, {}, 11);
305
334
  assert.equal(needInput.status, "NEED_INPUT");
306
335
  assert.deepEqual(needInput.required_fields, ["job", "start_from", "target_count", "criteria"]);
@@ -413,6 +442,15 @@ async function testBossChatCliShouldSupportRunAndFollowUpParsing() {
413
442
  }
414
443
 
415
444
  await withBossChatWorkspace(async (workspaceRoot) => {
445
+ const prepareLogs = await captureConsoleLogs(async () => {
446
+ await cliTestables.runBossChatCliCommand("prepare-run", {
447
+ "workspace-root": workspaceRoot
448
+ });
449
+ });
450
+ const prepared = JSON.parse(prepareLogs[0]);
451
+ assert.equal(prepared.status, "NEED_INPUT");
452
+ assert.equal(prepared.pending_questions.find((item) => item.field === "target_count").argument_name, "target_count");
453
+
416
454
  const logs = await captureConsoleLogs(async () => {
417
455
  await cliTestables.runBossChatCliCommand("run", {
418
456
  "workspace-root": workspaceRoot,
@@ -141,10 +141,15 @@ function isUnlimitedTargetCountToken(value) {
141
141
  'inf',
142
142
  'max',
143
143
  'full',
144
+ 'allcandidates',
144
145
  '全部',
145
146
  '全量',
146
147
  '不限',
147
148
  '扫到底',
149
+ '全部候选人',
150
+ '所有候选人',
151
+ '全部人选',
152
+ '所有人选',
148
153
  '直到完成所有人选',
149
154
  ].includes(token);
150
155
  }