@reconcrap/boss-recommend-mcp 2.1.11 → 2.1.13

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.
@@ -38,11 +38,11 @@ import {
38
38
  imageEvidenceFilePath,
39
39
  measureTiming
40
40
  } from "../../core/run/timing.js";
41
- import {
42
- callScreeningLlm,
43
- normalizeText,
44
- screenCandidate
45
- } from "../../core/screening/index.js";
41
+ import {
42
+ callScreeningLlm,
43
+ normalizeText,
44
+ screenCandidate
45
+ } from "../../core/screening/index.js";
46
46
  import {
47
47
  CHAT_BOTTOM_MARKER_SELECTORS,
48
48
  CHAT_CARD_SELECTORS,
@@ -104,11 +104,11 @@ function compactScreening(screening) {
104
104
 
105
105
  function compactLlmResult(llmResult) {
106
106
  if (!llmResult) return null;
107
- return {
108
- ok: Boolean(llmResult.ok),
109
- provider: llmResult.provider || null,
110
- passed: llmResult.passed,
111
- cot: llmResult.cot || llmResult.decision_cot || "",
107
+ return {
108
+ ok: Boolean(llmResult.ok),
109
+ provider: llmResult.provider || null,
110
+ passed: llmResult.passed,
111
+ cot: llmResult.cot || llmResult.decision_cot || "",
112
112
  reasoning_content: llmResult.reasoning_content || "",
113
113
  raw_model_output: llmResult.raw_model_output || "",
114
114
  evidence_count: llmResult.evidence?.length || 0,
@@ -743,12 +743,12 @@ export async function runChatWorkflow({
743
743
  safeClickPointEnabled: effectiveHumanBehavior.clickMovement,
744
744
  actionCooldownEnabled: effectiveHumanBehavior.actionCooldown
745
745
  });
746
- const humanRestController = createHumanRestController({
747
- enabled: effectiveHumanRestEnabled,
748
- shortRestEnabled: effectiveHumanBehavior.shortRest,
749
- batchRestEnabled: effectiveHumanBehavior.batchRest,
750
- restLevel: effectiveHumanBehavior.restLevel
751
- });
746
+ const humanRestController = createHumanRestController({
747
+ enabled: effectiveHumanRestEnabled,
748
+ shortRestEnabled: effectiveHumanBehavior.shortRest,
749
+ batchRestEnabled: effectiveHumanBehavior.batchRest,
750
+ restLevel: effectiveHumanBehavior.restLevel
751
+ });
752
752
  const normalizedDetailSource = normalizeDetailSource(detailSource);
753
753
  const normalizedScreeningMode = normalizeScreeningMode(screeningMode);
754
754
  const useLlmScreening = normalizedScreeningMode !== "deterministic";
@@ -792,12 +792,12 @@ export async function runChatWorkflow({
792
792
  let requestedCount = 0;
793
793
  let requestSatisfiedCount = 0;
794
794
  let requestSkippedCount = 0;
795
- let contextSetup = {};
796
- let contextRecoveryAttempts = 0;
797
- const candidateRecoveryCounts = new Map();
798
- let lastHumanEvent = null;
799
-
800
- function recordHumanEvent(event = null) {
795
+ let contextSetup = {};
796
+ let contextRecoveryAttempts = 0;
797
+ const candidateRecoveryCounts = new Map();
798
+ let lastHumanEvent = null;
799
+
800
+ function recordHumanEvent(event = null) {
801
801
  if (!event) return lastHumanEvent;
802
802
  lastHumanEvent = {
803
803
  at: new Date().toISOString(),
@@ -861,13 +861,13 @@ export async function runChatWorkflow({
861
861
  ...setup.contextSetup,
862
862
  initial_top_level_state: initialTopLevelState
863
863
  };
864
- runControl.checkpoint({
865
- chat_context: contextSetup
866
- });
867
-
868
- async function recoverAndReapplyChatContext(reason, error = null, {
869
- forceRefresh = false
870
- } = {}) {
864
+ runControl.checkpoint({
865
+ chat_context: contextSetup
866
+ });
867
+
868
+ async function recoverAndReapplyChatContext(reason, error = null, {
869
+ forceRefresh = false
870
+ } = {}) {
871
871
  runControl.setPhase("chat:recover_shell");
872
872
  contextRecoveryAttempts += 1;
873
873
  const shellRecovery = await recoverChatShell(client, {
@@ -967,12 +967,12 @@ export async function runChatWorkflow({
967
967
  context_recoveries: contextRecoveryAttempts,
968
968
  list_end_reason: listEndReason,
969
969
  viewport_checks: viewportGuard.getStats().checks,
970
- viewport_recoveries: viewportGuard.getStats().recoveries,
971
- human_behavior_enabled: effectiveHumanBehavior.enabled,
972
- human_behavior_profile: effectiveHumanBehavior.profile,
973
- human_rest_level: effectiveHumanBehavior.restLevel,
974
- human_rest_enabled: effectiveHumanRestEnabled,
975
- human_rest_count: humanRestController.getState().rest_count,
970
+ viewport_recoveries: viewportGuard.getStats().recoveries,
971
+ human_behavior_enabled: effectiveHumanBehavior.enabled,
972
+ human_behavior_profile: effectiveHumanBehavior.profile,
973
+ human_rest_level: effectiveHumanBehavior.restLevel,
974
+ human_rest_enabled: effectiveHumanRestEnabled,
975
+ human_rest_count: humanRestController.getState().rest_count,
976
976
  human_rest_ms: humanRestController.getState().total_rest_ms,
977
977
  last_human_event: lastHumanEvent
978
978
  });
@@ -997,10 +997,10 @@ export async function runChatWorkflow({
997
997
  last_human_event: lastHumanEvent,
998
998
  list_end_reason: listEndReason,
999
999
  target_pass_count: passTarget,
1000
- process_until_list_end: Boolean(processUntilListEnd),
1001
- processed_limit: processedLimit,
1002
- detail_source: normalizedDetailSource,
1003
- processed: 0,
1000
+ process_until_list_end: Boolean(processUntilListEnd),
1001
+ processed_limit: processedLimit,
1002
+ detail_source: normalizedDetailSource,
1003
+ processed: 0,
1004
1004
  screened: 0,
1005
1005
  detail_opened: 0,
1006
1006
  llm_screened: 0,
@@ -1501,11 +1501,11 @@ export async function runChatWorkflow({
1501
1501
  llmResult = createMissingLlmConfigResult();
1502
1502
  } else {
1503
1503
  try {
1504
- llmResult = await measureTiming(timings, "vision_model_ms", () => callScreeningLlm({
1505
- candidate: detailResult.candidate,
1506
- criteria,
1507
- config: llmConfig,
1508
- timeoutMs: llmTimeoutMs,
1504
+ llmResult = await measureTiming(timings, "vision_model_ms", () => callScreeningLlm({
1505
+ candidate: detailResult.candidate,
1506
+ criteria,
1507
+ config: llmConfig,
1508
+ timeoutMs: llmTimeoutMs,
1509
1509
  imageEvidence,
1510
1510
  maxImages: llmImageLimit,
1511
1511
  imageDetail: llmImageDetail
@@ -1574,10 +1574,10 @@ export async function runChatWorkflow({
1574
1574
  const llmTimingKey = imageEvidence?.file_paths?.length
1575
1575
  ? "vision_model_ms"
1576
1576
  : "text_model_ms";
1577
- llmResult = await measureTiming(timings, llmTimingKey, () => callScreeningLlm({
1578
- candidate: detailResult.candidate,
1579
- criteria,
1580
- config: llmConfig,
1577
+ llmResult = await measureTiming(timings, llmTimingKey, () => callScreeningLlm({
1578
+ candidate: detailResult.candidate,
1579
+ criteria,
1580
+ config: llmConfig,
1581
1581
  timeoutMs: llmTimeoutMs,
1582
1582
  imageEvidence,
1583
1583
  maxImages: llmImageLimit,
@@ -1773,12 +1773,12 @@ export async function runChatWorkflow({
1773
1773
  context_recoveries: contextRecoveryAttempts,
1774
1774
  list_end_reason: listEndReason || null,
1775
1775
  viewport_checks: viewportGuard.getStats().checks,
1776
- viewport_recoveries: viewportGuard.getStats().recoveries,
1777
- human_behavior_enabled: effectiveHumanBehavior.enabled,
1778
- human_behavior_profile: effectiveHumanBehavior.profile,
1779
- human_rest_level: effectiveHumanBehavior.restLevel,
1780
- human_rest_enabled: effectiveHumanRestEnabled,
1781
- human_rest_count: humanRestController.getState().rest_count,
1776
+ viewport_recoveries: viewportGuard.getStats().recoveries,
1777
+ human_behavior_enabled: effectiveHumanBehavior.enabled,
1778
+ human_behavior_profile: effectiveHumanBehavior.profile,
1779
+ human_rest_level: effectiveHumanBehavior.restLevel,
1780
+ human_rest_enabled: effectiveHumanRestEnabled,
1781
+ human_rest_count: humanRestController.getState().rest_count,
1782
1782
  human_rest_ms: humanRestController.getState().total_rest_ms,
1783
1783
  last_human_event: lastHumanEvent,
1784
1784
  last_candidate_id: screeningCandidate.id || null,
@@ -1818,11 +1818,11 @@ export async function runChatWorkflow({
1818
1818
  compactResult.human_rest = restResult;
1819
1819
  addTiming(compactResult.timings, "human_rest_ms", restElapsed);
1820
1820
  compactResult.timings.total_ms = Date.now() - candidateStarted;
1821
- runControl.updateProgress({
1822
- human_rest_enabled: effectiveHumanRestEnabled,
1823
- human_rest_level: effectiveHumanBehavior.restLevel,
1824
- human_rest_count: humanRestController.getState().rest_count,
1825
- human_rest_ms: humanRestController.getState().total_rest_ms,
1821
+ runControl.updateProgress({
1822
+ human_rest_enabled: effectiveHumanRestEnabled,
1823
+ human_rest_level: effectiveHumanBehavior.restLevel,
1824
+ human_rest_count: humanRestController.getState().rest_count,
1825
+ human_rest_ms: humanRestController.getState().total_rest_ms,
1826
1826
  human_rest_last: restResult,
1827
1827
  context_recoveries: contextRecoveryAttempts,
1828
1828
  last_human_event: lastHumanEvent
@@ -1855,10 +1855,10 @@ export async function runChatWorkflow({
1855
1855
  last_human_event: lastHumanEvent,
1856
1856
  list_end_reason: listEndReason || null,
1857
1857
  target_pass_count: passTarget,
1858
- process_until_list_end: Boolean(processUntilListEnd),
1859
- processed_limit: processedLimit,
1860
- detail_source: normalizedDetailSource,
1861
- processed: finalCounters.processed,
1858
+ process_until_list_end: Boolean(processUntilListEnd),
1859
+ processed_limit: processedLimit,
1860
+ detail_source: normalizedDetailSource,
1861
+ processed: finalCounters.processed,
1862
1862
  screened: finalCounters.screened,
1863
1863
  detail_opened: finalCounters.detail_opened,
1864
1864
  llm_screened: finalCounters.llm_screened,
@@ -1880,10 +1880,10 @@ export function createChatRunService({
1880
1880
  } = {}) {
1881
1881
  const manager = lifecycle || createRunLifecycleManager({ idPrefix, onSnapshot });
1882
1882
 
1883
- function startChatRun({
1884
- runId = "",
1885
- client,
1886
- targetUrl = CHAT_TARGET_URL,
1883
+ function startChatRun({
1884
+ runId = "",
1885
+ client,
1886
+ targetUrl = CHAT_TARGET_URL,
1887
1887
  job = "",
1888
1888
  startFrom = "all",
1889
1889
  criteria = "",
@@ -1929,10 +1929,10 @@ export function createChatRunService({
1929
1929
  legacyEnabled: humanRestEnabled === true || llmConfig?.humanRestEnabled === true
1930
1930
  });
1931
1931
  const effectiveHumanRestEnabled = effectiveHumanBehavior.restEnabled;
1932
- return manager.startRun({
1933
- runId,
1934
- name,
1935
- context: {
1932
+ return manager.startRun({
1933
+ runId,
1934
+ name,
1935
+ context: {
1936
1936
  domain: "chat",
1937
1937
  target_url: targetUrl,
1938
1938
  criteria_present: Boolean(criteria),
@@ -1962,13 +1962,13 @@ export function createChatRunService({
1962
1962
  list_settle_ms: listSettleMs,
1963
1963
  list_fallback_point: listFallbackPoint,
1964
1964
  online_resume_button_timeout_ms: onlineResumeButtonTimeoutMs,
1965
- image_output_dir: imageOutputDir || "",
1966
- human_behavior_enabled: effectiveHumanBehavior.enabled,
1967
- human_behavior_profile: effectiveHumanBehavior.profile,
1968
- human_behavior: effectiveHumanBehavior,
1969
- human_rest_level: effectiveHumanBehavior.restLevel,
1970
- human_rest_enabled: effectiveHumanRestEnabled
1971
- },
1965
+ image_output_dir: imageOutputDir || "",
1966
+ human_behavior_enabled: effectiveHumanBehavior.enabled,
1967
+ human_behavior_profile: effectiveHumanBehavior.profile,
1968
+ human_behavior: effectiveHumanBehavior,
1969
+ human_rest_level: effectiveHumanBehavior.restLevel,
1970
+ human_rest_enabled: effectiveHumanRestEnabled
1971
+ },
1972
1972
  progress: {
1973
1973
  card_count: 0,
1974
1974
  target_count: targetPassCount || (processUntilListEnd ? "all" : processedLimit),
@@ -1983,11 +1983,11 @@ export function createChatRunService({
1983
1983
  requested: 0,
1984
1984
  request_satisfied: 0,
1985
1985
  request_skipped: 0,
1986
- context_recoveries: 0,
1987
- human_behavior_enabled: effectiveHumanBehavior.enabled,
1988
- human_behavior_profile: effectiveHumanBehavior.profile,
1989
- human_rest_level: effectiveHumanBehavior.restLevel,
1990
- human_rest_enabled: effectiveHumanRestEnabled,
1986
+ context_recoveries: 0,
1987
+ human_behavior_enabled: effectiveHumanBehavior.enabled,
1988
+ human_behavior_profile: effectiveHumanBehavior.profile,
1989
+ human_rest_level: effectiveHumanBehavior.restLevel,
1990
+ human_rest_enabled: effectiveHumanRestEnabled,
1991
1991
  human_rest_count: 0,
1992
1992
  human_rest_ms: 0,
1993
1993
  last_human_event: null
@@ -38,13 +38,13 @@ import {
38
38
  resolveInfiniteListFallbackPoint
39
39
  } from "../../core/infinite-list/index.js";
40
40
  import { createViewportRunGuard } from "../../core/self-heal/index.js";
41
- import {
42
- callScreeningLlm,
43
- compactScreeningLlmResult,
44
- createFailedLlmScreeningResult,
45
- llmResultToScreening,
46
- screenCandidate
47
- } from "../../core/screening/index.js";
41
+ import {
42
+ callScreeningLlm,
43
+ compactScreeningLlmResult,
44
+ createFailedLlmScreeningResult,
45
+ llmResultToScreening,
46
+ screenCandidate
47
+ } from "../../core/screening/index.js";
48
48
  import {
49
49
  closeRecommendBlockingPanels,
50
50
  closeRecommendAvatarPreview,
@@ -906,8 +906,8 @@ export async function runRecommendWorkflow({
906
906
  recovery_reason: reason
907
907
  });
908
908
  return refreshResult;
909
- }
910
-
909
+ }
910
+
911
911
  runControl.setPhase("recommend:cleanup");
912
912
  await closeRecommendDetail(client, { attemptsLimit: 2 });
913
913
  await closeRecommendAvatarPreview(client, { attemptsLimit: 2 });
@@ -1334,11 +1334,11 @@ export async function runRecommendWorkflow({
1334
1334
  const llmTimingKey = detailResult?.image_evidence?.file_paths?.length
1335
1335
  ? "vision_model_ms"
1336
1336
  : "text_model_ms";
1337
- llmResult = await measureTiming(timings, llmTimingKey, () => callScreeningLlm({
1338
- candidate: screeningCandidate,
1339
- criteria,
1340
- config: llmConfig,
1341
- timeoutMs: llmTimeoutMs,
1337
+ llmResult = await measureTiming(timings, llmTimingKey, () => callScreeningLlm({
1338
+ candidate: screeningCandidate,
1339
+ criteria,
1340
+ config: llmConfig,
1341
+ timeoutMs: llmTimeoutMs,
1342
1342
  imageEvidence: detailResult?.image_evidence || null,
1343
1343
  maxImages: llmImageLimit,
1344
1344
  imageDetail: llmImageDetail
@@ -1532,7 +1532,7 @@ export async function runRecommendWorkflow({
1532
1532
  human_rest: humanRestController.getState(),
1533
1533
  last_human_event: lastHumanEvent,
1534
1534
  list_end_reason: listEndReason || null,
1535
- refresh_rounds: refreshRounds,
1535
+ refresh_rounds: refreshRounds,
1536
1536
  refresh_attempts: refreshAttempts,
1537
1537
  context_recoveries: contextRecoveryAttempts,
1538
1538
  ...countRecommendResultStatuses(results, { greetCount }),
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
- resume: {
498
- checkpoint_path: normalizeText(run.resume?.checkpoint_path || ""),
499
- output_csv: normalizeText(run.resume?.output_csv || ""),
500
- worker_stdout_path: normalizeText(run.resume?.worker_stdout_path || ""),
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 createToolsSchema() {
1298
- return [
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: createRunInputSchema()
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: createRunInputSchema()
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: createRunInputSchema()
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: createScheduleRunInputSchema()
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);