@cjsqbn/resume-screening-mcp 0.1.1 → 0.2.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/index.js +68 -165
- package/package.json +6 -2
package/index.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
3
7
|
const SERVER_NAME = "resume-screening-mcp";
|
|
4
|
-
const SERVER_VERSION = "0.
|
|
8
|
+
const SERVER_VERSION = "0.2.0";
|
|
5
9
|
|
|
6
10
|
const DEFAULT_CONFIG = {
|
|
7
11
|
trust_threshold: 60,
|
|
@@ -531,177 +535,76 @@ function generateReport({ screening_result }) {
|
|
|
531
535
|
return { markdown: lines.join("\n") };
|
|
532
536
|
}
|
|
533
537
|
|
|
534
|
-
|
|
535
|
-
{
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
properties: {
|
|
541
|
-
job_description: { type: "string", description: "岗位 JD 原文。" },
|
|
542
|
-
},
|
|
543
|
-
required: ["job_description"],
|
|
544
|
-
},
|
|
545
|
-
},
|
|
546
|
-
{
|
|
547
|
-
name: "analyze_candidate",
|
|
548
|
-
description: "分析单个候选人,输出结构化简历信息、可信度、匹配度、综合得分和面试追问。",
|
|
549
|
-
inputSchema: {
|
|
550
|
-
type: "object",
|
|
551
|
-
properties: {
|
|
552
|
-
resume: {
|
|
553
|
-
type: "object",
|
|
554
|
-
properties: {
|
|
555
|
-
file_name: { type: "string" },
|
|
556
|
-
text: { type: "string" },
|
|
557
|
-
},
|
|
558
|
-
required: ["text"],
|
|
559
|
-
},
|
|
560
|
-
job_description: { type: "string" },
|
|
561
|
-
config: { type: "object" },
|
|
562
|
-
},
|
|
563
|
-
required: ["resume"],
|
|
564
|
-
},
|
|
565
|
-
},
|
|
566
|
-
{
|
|
567
|
-
name: "screen_resumes",
|
|
568
|
-
description: "批量分析多份简历和岗位 JD,输出淘汰名单、晋级排序榜、疑点追问和 Markdown 报告。",
|
|
569
|
-
inputSchema: {
|
|
570
|
-
type: "object",
|
|
571
|
-
properties: {
|
|
572
|
-
resumes: {
|
|
573
|
-
type: "array",
|
|
574
|
-
items: {
|
|
575
|
-
type: "object",
|
|
576
|
-
properties: {
|
|
577
|
-
file_name: { type: "string" },
|
|
578
|
-
text: { type: "string" },
|
|
579
|
-
},
|
|
580
|
-
required: ["text"],
|
|
581
|
-
},
|
|
582
|
-
},
|
|
583
|
-
job_description: { type: "string" },
|
|
584
|
-
config: {
|
|
585
|
-
type: "object",
|
|
586
|
-
properties: {
|
|
587
|
-
trust_threshold: { type: "number" },
|
|
588
|
-
match_weight: { type: "number" },
|
|
589
|
-
competitiveness_weight: { type: "number" },
|
|
590
|
-
},
|
|
591
|
-
},
|
|
592
|
-
},
|
|
593
|
-
required: ["resumes", "job_description"],
|
|
594
|
-
},
|
|
595
|
-
},
|
|
596
|
-
{
|
|
597
|
-
name: "generate_report",
|
|
598
|
-
description: "把 screen_resumes 的结构化结果转换为中文 Markdown 报告。",
|
|
599
|
-
inputSchema: {
|
|
600
|
-
type: "object",
|
|
601
|
-
properties: {
|
|
602
|
-
screening_result: { type: "object" },
|
|
538
|
+
function asTextResult(value) {
|
|
539
|
+
return {
|
|
540
|
+
content: [
|
|
541
|
+
{
|
|
542
|
+
type: "text",
|
|
543
|
+
text: JSON.stringify(value, null, 2),
|
|
603
544
|
},
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
},
|
|
607
|
-
];
|
|
608
|
-
|
|
609
|
-
function callTool(name, args) {
|
|
610
|
-
if (name === "parse_jd") return parseJd(args || {});
|
|
611
|
-
if (name === "analyze_candidate") return analyzeCandidate(args || {});
|
|
612
|
-
if (name === "screen_resumes") return screenResumes(args || {});
|
|
613
|
-
if (name === "generate_report") return generateReport(args || {});
|
|
614
|
-
throw new Error(`未知工具:${name}`);
|
|
545
|
+
],
|
|
546
|
+
};
|
|
615
547
|
}
|
|
616
548
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
processMessages();
|
|
549
|
+
const resumeSchema = z.object({
|
|
550
|
+
file_name: z.string().optional(),
|
|
551
|
+
name: z.string().optional(),
|
|
552
|
+
text: z.string().min(1, "resume.text 不能为空"),
|
|
622
553
|
});
|
|
623
554
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
const length = Number(match[1]);
|
|
639
|
-
const messageStart = headerEnd + 4;
|
|
640
|
-
const messageEnd = messageStart + length;
|
|
641
|
-
if (inputBuffer.length < messageEnd) return;
|
|
642
|
-
|
|
643
|
-
const raw = inputBuffer.slice(messageStart, messageEnd).toString("utf8");
|
|
644
|
-
inputBuffer = inputBuffer.slice(messageEnd);
|
|
645
|
-
|
|
646
|
-
try {
|
|
647
|
-
handleMessage(JSON.parse(raw));
|
|
648
|
-
} catch (error) {
|
|
649
|
-
sendError(null, -32700, `解析 MCP 消息失败:${error.message}`);
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
function handleMessage(message) {
|
|
655
|
-
if (!message || typeof message !== "object") return;
|
|
656
|
-
if (message.id === undefined || message.id === null) return;
|
|
657
|
-
|
|
658
|
-
try {
|
|
659
|
-
if (message.method === "initialize") {
|
|
660
|
-
sendResult(message.id, {
|
|
661
|
-
protocolVersion: message.params?.protocolVersion || "2024-11-05",
|
|
662
|
-
capabilities: { tools: {} },
|
|
663
|
-
serverInfo: { name: SERVER_NAME, version: SERVER_VERSION },
|
|
664
|
-
});
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
if (message.method === "tools/list") {
|
|
669
|
-
sendResult(message.id, { tools: TOOLS });
|
|
670
|
-
return;
|
|
671
|
-
}
|
|
555
|
+
const configSchema = z.object({
|
|
556
|
+
trust_threshold: z.number().optional(),
|
|
557
|
+
high_trust_threshold: z.number().optional(),
|
|
558
|
+
final_score_priority_threshold: z.number().optional(),
|
|
559
|
+
final_score_interview_threshold: z.number().optional(),
|
|
560
|
+
match_weight: z.number().optional(),
|
|
561
|
+
competitiveness_weight: z.number().optional(),
|
|
562
|
+
}).passthrough().optional();
|
|
563
|
+
|
|
564
|
+
const server = new McpServer({
|
|
565
|
+
name: SERVER_NAME,
|
|
566
|
+
version: SERVER_VERSION,
|
|
567
|
+
});
|
|
672
568
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
text: JSON.stringify(result, null, 2),
|
|
682
|
-
},
|
|
683
|
-
],
|
|
684
|
-
structuredContent: result,
|
|
685
|
-
});
|
|
686
|
-
return;
|
|
687
|
-
}
|
|
569
|
+
server.tool(
|
|
570
|
+
"parse_jd",
|
|
571
|
+
"从岗位 JD 文本中提取工作年限、技能、行业、职责和加分项要求。",
|
|
572
|
+
{
|
|
573
|
+
job_description: z.string().min(1, "job_description 不能为空"),
|
|
574
|
+
},
|
|
575
|
+
async (args) => asTextResult(parseJd(args)),
|
|
576
|
+
);
|
|
688
577
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
578
|
+
server.tool(
|
|
579
|
+
"analyze_candidate",
|
|
580
|
+
"分析单个候选人,输出结构化简历信息、可信度、匹配度、综合得分和面试追问。",
|
|
581
|
+
{
|
|
582
|
+
resume: resumeSchema,
|
|
583
|
+
job_description: z.string().optional(),
|
|
584
|
+
config: configSchema,
|
|
585
|
+
},
|
|
586
|
+
async (args) => asTextResult(analyzeCandidate(args)),
|
|
587
|
+
);
|
|
694
588
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
589
|
+
server.tool(
|
|
590
|
+
"screen_resumes",
|
|
591
|
+
"批量分析多份简历和岗位 JD,输出淘汰名单、晋级排序榜、疑点追问和 Markdown 报告。",
|
|
592
|
+
{
|
|
593
|
+
resumes: z.array(resumeSchema).min(1, "resumes 必须是非空数组"),
|
|
594
|
+
job_description: z.string().min(1, "job_description 不能为空"),
|
|
595
|
+
config: configSchema,
|
|
596
|
+
},
|
|
597
|
+
async (args) => asTextResult(screenResumes(args)),
|
|
598
|
+
);
|
|
698
599
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
600
|
+
server.tool(
|
|
601
|
+
"generate_report",
|
|
602
|
+
"把 screen_resumes 的结构化结果转换为中文 Markdown 报告。",
|
|
603
|
+
{
|
|
604
|
+
screening_result: z.record(z.string(), z.any()),
|
|
605
|
+
},
|
|
606
|
+
async (args) => asTextResult(generateReport(args)),
|
|
607
|
+
);
|
|
702
608
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
const bytes = Buffer.byteLength(json, "utf8");
|
|
706
|
-
process.stdout.write(`Content-Length: ${bytes}\r\n\r\n${json}`);
|
|
707
|
-
}
|
|
609
|
+
const transport = new StdioServerTransport();
|
|
610
|
+
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cjsqbn/resume-screening-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "MCP service for resume screening, JD matching, trust checking, scoring and candidate ranking.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,8 +20,12 @@
|
|
|
20
20
|
"engines": {
|
|
21
21
|
"node": ">=18"
|
|
22
22
|
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@modelcontextprotocol/sdk": "^1.18.1",
|
|
25
|
+
"zod": "^3.25.76"
|
|
26
|
+
},
|
|
23
27
|
"publishConfig": {
|
|
24
28
|
"access": "public"
|
|
25
29
|
},
|
|
26
30
|
"license": "MIT"
|
|
27
|
-
}
|
|
31
|
+
}
|