@ser3nd1pity/rts-evidence-rank-mcp 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.
@@ -0,0 +1,228 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { McpServer } from "@modelcontextprotocol/server";
4
+ import { serveStdio } from "@modelcontextprotocol/server/stdio";
5
+ import * as z from "zod/v4";
6
+
7
+ const SKILL_GROUPS = {
8
+ recommendation: [
9
+ "推荐",
10
+ "召回",
11
+ "排序",
12
+ "双塔",
13
+ "DIN",
14
+ "DeepFM",
15
+ "MMoE",
16
+ "CTR",
17
+ "GMV",
18
+ "留存"
19
+ ],
20
+ search: [
21
+ "搜索",
22
+ "搜索排序",
23
+ "相关性",
24
+ "BERT",
25
+ "NDCG",
26
+ "查询理解"
27
+ ],
28
+ nlp: [
29
+ "NLP",
30
+ "自然语言处理",
31
+ "文本分类",
32
+ "实体识别",
33
+ "大模型",
34
+ "LLM",
35
+ "Transformer"
36
+ ],
37
+ cv: [
38
+ "CV",
39
+ "计算机视觉",
40
+ "图像",
41
+ "目标检测",
42
+ "图像分类",
43
+ "OCR"
44
+ ],
45
+ engineering: [
46
+ "Python",
47
+ "SQL",
48
+ "PyTorch",
49
+ "TensorFlow",
50
+ "Spark",
51
+ "Flink",
52
+ "Kafka",
53
+ "Docker",
54
+ "Kubernetes"
55
+ ],
56
+ backend: [
57
+ "Java",
58
+ "Spring Boot",
59
+ "Spring MVC",
60
+ "MyBatis",
61
+ "MySQL",
62
+ "Redis",
63
+ "REST API",
64
+ "微服务",
65
+ "分布式"
66
+ ]
67
+ };
68
+
69
+ function normalize(text) {
70
+ return String(text ?? "")
71
+ .replace(/\s+/g, " ")
72
+ .trim();
73
+ }
74
+
75
+ function unique(items) {
76
+ return [...new Set(items)];
77
+ }
78
+
79
+ function findTerms(text, terms) {
80
+ const source = normalize(text);
81
+ return unique(terms.filter((term) => source.toLowerCase().includes(term.toLowerCase())));
82
+ }
83
+
84
+ function extractYears(text) {
85
+ const source = normalize(text);
86
+ const matches = [
87
+ ...source.matchAll(/(\d+(?:\.\d+)?)\s*(?:年|年以上|年经验)/g)
88
+ ];
89
+
90
+ const values = matches
91
+ .map((match) => Number(match[1]))
92
+ .filter((value) => Number.isFinite(value));
93
+
94
+ return values.length ? Math.max(...values) : null;
95
+ }
96
+
97
+ function detectJobType(text, skills) {
98
+ const source = normalize(text);
99
+
100
+ if (
101
+ skills.some((item) =>
102
+ ["推荐", "召回", "排序", "CTR", "GMV", "双塔", "DIN", "DeepFM", "MMoE"].includes(item)
103
+ )
104
+ ) {
105
+ return "algorithm_recommendation";
106
+ }
107
+
108
+ if (
109
+ skills.some((item) =>
110
+ ["搜索", "搜索排序", "NDCG", "查询理解"].includes(item)
111
+ )
112
+ ) {
113
+ return "algorithm_search";
114
+ }
115
+
116
+ if (
117
+ skills.some((item) =>
118
+ ["Spring Boot", "Spring MVC", "MyBatis", "MySQL", "Redis", "微服务"].includes(item)
119
+ )
120
+ ) {
121
+ return "java_backend";
122
+ }
123
+
124
+ if (/(数据分析|统计分析|BI|报表|指标体系)/i.test(source)) {
125
+ return "data_analysis";
126
+ }
127
+
128
+ if (skills.some((item) => ["NLP", "自然语言处理", "CV", "计算机视觉"].includes(item))) {
129
+ return "algorithm_other";
130
+ }
131
+
132
+ return "other";
133
+ }
134
+
135
+ function parseJd(jdText) {
136
+ const text = normalize(jdText);
137
+
138
+ const allTerms = Object.values(SKILL_GROUPS).flat();
139
+ const skills = findTerms(text, allTerms);
140
+
141
+ const hardRequirements = [];
142
+ const bonusItems = [];
143
+
144
+ if (/本科|硕士|研究生|博士/.test(text)) {
145
+ hardRequirements.push("本科及以上或相关学历要求");
146
+ }
147
+
148
+ const years = extractYears(text);
149
+ if (years !== null) {
150
+ hardRequirements.push(`至少 ${years} 年相关经验`);
151
+ }
152
+
153
+ if (/Python/.test(text)) {
154
+ hardRequirements.push("Python");
155
+ }
156
+
157
+ if (/PyTorch|TensorFlow/.test(text)) {
158
+ hardRequirements.push("PyTorch 或 TensorFlow");
159
+ }
160
+
161
+ if (/SQL/.test(text)) {
162
+ hardRequirements.push("SQL");
163
+ }
164
+
165
+ if (/Spark|Flink|论文|竞赛|开源|0\s*到\s*1|0→1/.test(text)) {
166
+ bonusItems.push("大数据工具、论文竞赛、开源或从 0 到 1 经验");
167
+ }
168
+
169
+ const skillGroups = {};
170
+ for (const [group, terms] of Object.entries(SKILL_GROUPS)) {
171
+ const found = findTerms(text, terms);
172
+ if (found.length) {
173
+ skillGroups[group] = found;
174
+ }
175
+ }
176
+
177
+ return {
178
+ schema_version: "rts-evidence-rank-0.1",
179
+ job_type: detectJobType(text, skills),
180
+ experience_years_required: years,
181
+ hard_requirements: hardRequirements,
182
+ core_skills: skills,
183
+ skill_groups: skillGroups,
184
+ bonus_items: bonusItems,
185
+ source_excerpt: text.slice(0, 1200)
186
+ };
187
+ }
188
+
189
+ function createServer() {
190
+ const server = new McpServer(
191
+ {
192
+ name: "rts-evidence-rank-mcp",
193
+ version: "0.1.0"
194
+ },
195
+ {
196
+ instructions:
197
+ "这是自研 RTS-EvidenceRank 算法服务。先解析 JD,再基于候选人原文证据进行匹配度、证据完整度和分层计算。不得把文件名或单纯关键词当作强证据。"
198
+ }
199
+ );
200
+
201
+ server.registerTool(
202
+ "parse_jd_profile",
203
+ {
204
+ description:
205
+ "使用自研规则将岗位 JD 拆解为岗位类型、经验门槛、核心技能、技能分组、硬性要求和加分项。",
206
+ inputSchema: z.object({
207
+ jd_text: z.string().min(1)
208
+ })
209
+ },
210
+ async ({ jd_text }) => {
211
+ const result = parseJd(jd_text);
212
+
213
+ return {
214
+ content: [
215
+ {
216
+ type: "text",
217
+ text: JSON.stringify(result, null, 2)
218
+ }
219
+ ]
220
+ };
221
+ }
222
+ );
223
+
224
+ return server;
225
+ }
226
+
227
+ const server = createServer();
228
+ await serveStdio(server);