求职mcp
  
一个自托管的MCP服务器,将LibreChat代理变成一个完整的求职助手——从跨多个板搜索,到构建和存储简历档案,再到根据列表评分,再到通过管道跟踪申请。使用FastMCP构建,用于多用户LibreChat部署。
搜索 跨Adzuna、Removative、WeWork Remotely、Jobice、USAJobs等。 丰富 通过多层提取管道列出完整的职位描述。 个人资料 你的简历一次,每个评分和定制工具都会自动使用它。 得分 符合结构化的克劳德动力分解,包括ATS分析。 匹配 在语义上与你的个人资料相匹配。 轨道 Postgres中每个用户的完整管道。 手表 在后台查看新的比赛,并收到电子邮件提醒。
大多数求职MCP工具只做一件事——抓取列表或生成求职信。这个连接了整个工作流,因此代理可以端到端地驱动它。
你需要的
jobsearchmcp是模块化的——从基础开始,根据需要添加服务。
| 能力 | 所需服务 |
|---|---|
| 搜索职位+跟踪申请 | Adzuna密钥· docker compose up (通过原始HTTP回退提取JD——不使用Firecrawl时质量会有所不同) |
| 完整的职位描述提取 | Firecrawl Simple(自托管) |
| AI拟合评分+轮廓构建 | Anthropic API关键 |
| 语义作业匹配 | Ollama+bge-m3 |
| 后台电子邮件警报 | SMTP中继 |
开始的最低要求: Adzuna API密钥和 docker compose up。这为您提供了跨6个来源的求职、完整的应用程序跟踪和通过原始HTTP回退提取JD。其他一切都是添加剂。需要未配置服务的工具会返回一条明确的错误消息,解释要设置什么。
______________________________________________________________________
运作原理
flowchart TD
subgraph profile["① Profile Setup (one-time)"]
BP["build_profile\nparse resume text via Claude"] --> SP["save_profile\nstore structured profile"]
end
subgraph search["② Search & Discover"]
SJ["search_jobs\nAdzuna · RSS · USAJobs · more"] --> CA["check_active"]
CA --> GJD["get_job_detail\nFirecrawl → Crawl4AI → rawFetch"]
GJD --> SI["salary_insights"]
end
subgraph index["③ Index & Match"]
GJD --> IJ["index_job\nOllama bge-m3 → Qdrant"]
IJ --> MJ["match_jobs\nsemantic search"]
end
subgraph score["④ Score & Tailor"]
GJD --> SF["score_fit\nClaude · ATS score · apply/maybe/skip"]
MJ --> SF
SF --> CLB["cover_letter_brief"]
SF --> TR["tailor_resume\nJD-tailored profile draft"]
end
subgraph track["⑤ Track"]
SF --> MA["mark_applied"]
MA --> AN["add_note"]
AN --> US["update_status\napplied → interviewing → offered"]
US --> GMJ["get_my_jobs"]
end
SP -. "auto-used by score_fit,\ncover_letter_brief, tailor_resume" .-> score你不必使用每一步——代理可以在不接触跟踪器的情况下进行搜索和评分,也可以单独使用跟踪器在其他地方找到工作。
______________________________________________________________________
建筑
graph TB
subgraph clients["Clients"]
LC["LibreChat Agent"]
EMAIL["User Email"]
end
subgraph stack["Docker Stack · jobsearch-net"]
MCP["jobsearch-mcp\n:8383 streamable-http"]
JW["job-watcher\nbackground poller"]
PG[("Postgres 16\ntracking · profiles · notes")]
QD[("Qdrant\nvector index")]
VK[("Valkey\nenrichment cache")]
end
subgraph external["External Services"]
OLLAMA["Ollama\nbge-m3 embeddings"]
CLAUDE["Claude API\nHaiku"]
ENRICH["Firecrawl · Crawl4AI"]
SOURCES["Job Sources\nAdzuna · RSS · USAJobs · …"]
SMTP["SMTP Relay"]
end
LC -->|"streamable-http"| MCP
MCP --> PG & QD & VK
MCP --> OLLAMA & CLAUDE & ENRICH & SOURCES
JW --> PG & VK & SOURCES
JW --> SMTP --> EMAIL______________________________________________________________________
工具
简历简介
构建并存储您的个人资料一次-- score_fit, cover_letter_brief,以及 tailor_resume 当没有明确传递简历时,所有人都会自动使用它。
| 工具 | 说明 |
|---|---|
build_profile | 使用Claude将原始简历或简历文本解析为结构化的个人资料。返回结果以供查看--不自动保存。 |
save_profile | 存储结构化配置文件。从这一点开始,所有评分和裁剪工具都会自动使用它。 |
get_profile | 检索您存储的个人资料。 |
delete_profile | 删除您存储的配置文件和相关数据。 |
tailor_resume | 重写您存储的个人资料的亮点和摘要,以匹配特定的JD。返回定制版本以供查看,不会覆盖您存储的配置文件。 |
搜索与发现
| 工具 | 说明 |
|---|---|
search_jobs | 搜索Adzuna、远程、WeWorkRemotely、Jobice和USAJobs(默认)。支持 query, location, remote_only,以及 sources params。 可选来源: findwork, themuse (以技术/文化为重点), indeed, glassdoor, ziprecruiter (基于python jobspy的抓取,默认情况下不包括在内)。 |
get_job_detail | 从URL获取完整的职位描述。使用多层丰富管道:Firecrawl→ Crawl4AI→ rawFetch。结果缓存在Valkey中。 |
check_active | 检查列表是否仍处于活动状态。退货 active=True/False/None 以及触发它的信号 |
salary_insights | 职位的薪资智能——来自实时列表的最小/最大/平均/中位数、分布直方图和月度趋势。由Adzuna提供技术支持。 |
矢量搜索与匹配
| 工具 | 说明 |
|---|---|
index_job | 获取一个作业,并使用Ollama bge-m3嵌入将其存储在Qdrant中。在值得追踪的房源上打电话。 |
match_jobs | 查找语义上类似于简历或自由文本描述的索引工作。支持 top_k 和 exclude_seen params。 |
体能评分和应用准备
| 工具 | 说明 |
|---|---|
score_fit | 对简历与工作的匹配程度进行评分。获取完整的JD,然后使用Claude返回匹配的技能、缺失的技能、很高兴认识的人、资历匹配、ATS分数(0-100)和 apply/maybe/skip 建议。如果没有简历通过,则使用存储的配置文件。 |
cover_letter_brief | 结构化的求职信写作指南——开场角、根据你的经验制定的要求、需要承认的差距、推荐的语气。一封简短的信,没有写完。如果没有简历通过,则使用存储的配置文件。 |
应用程序跟踪
| 工具 | 说明 |
|---|---|
mark_seen | 将当前用户的作业标记为可见。 |
mark_applied | 将工作标记为已申请。 |
update_status | 通过管道移动作业: seen → applied → interviewing → offered → rejected → closed. |
add_note | 为跟踪的作业添加注释。笔记会累积,但不会被替换。 |
get_my_jobs | 获取当前用户的跟踪作业,按管道阶段排序。筛选依据 status 参数。 |
______________________________________________________________________
工作观察者
这 job-watcher 容器独立于MCP服务器运行。在可配置的时间间隔内(默认值:每4小时一次),它轮询Adzuna、Remative、WeWorkRemotely和USAJobs,将结果与每个用户的存储配置文件进行匹配(target_roles 和 skills 字段)并发送列出新匹配项的SMTP电子邮件。
重复数据删除是通过Valkey处理的——每个用户只会收到他们以前从未见过的列表的提醒。电子邮件会发送到用户个人资料中存储的地址。无需代理交互;它完全在后台运行。
通过配置 job-watcher.env (参见 job-watcher.env.example).要禁用它而不将其从堆栈中删除,请设置 JOB_WATCH_INTERVAL_SECONDS 值非常大。
______________________________________________________________________
先决条件
基本使用所需:
| 服务 | 它做什么 | 如何获得它 |
|---|---|---|
| 新思路 | 求职API+薪资数据 | 在 developer.adzuna.com |
特定功能所需:
| 服务 | 启用 | 如何获取 |
|---|---|---|
| Anthropic | score_fit, build_profile, tailor_resume, cover_letter_brief | console.anthropic.com |
| 奥利马(bge-m3) | index_job, match_jobs --语义搜索 | 本地运行; ollama pull bge-m3 |
| Firecrawl简单 | 完整JD提取(主层) | 自主机-- trieve ai/firecrall简单 |
可选:
| 服务 | 它增加了什么 | 如何获得它 |
|---|---|---|
| Crawl4AI | 如果Firecrawl不可用,则回退JD提取 | 自托管-- 取消编码/爬行4ai |
| SMTP中继 | 工作观察者电子邮件提醒 | Brevo免费版有效 |
| 美国职位 | 政府职位列表 | 可选键 developer.usajobs.gov;在不降低费率限制的情况下工作 |
| 发现/缪斯 | 以技术/文化为重点的房源 | findwork.dev /The Muse不需要钥匙 |
Postgres、Qdrant和Valkey都包含在Docker堆栈中,不需要外部设置。
______________________________________________________________________
堆栈
| 组件 | 目的 |
|---|---|
| FastMCP(可流式传输http) | MCP服务器传输 |
| Postgres 16 | 每个用户跟踪状态、应用程序管道、配置文件、注释 |
| Qdrant | 语义作业匹配的矢量索引 |
| Valkey | 富集缓存——避免重新获取最近看到的JD |
| Ollama(bge-m3) | 工作和简历嵌入 |
| Firecrawl Simple/Crawl4AI | 多层完整JD提取 |
克劳德(claude-haiku-4-5) | 个人资料解析、匹配度评分、简历定制 |
______________________________________________________________________
部署
Docker堆栈
五个集装箱,全部放在一个单独的集装箱上 jobsearch-net 网桥网络:
| 容器 | 图像 | 端口 |
|---|---|---|
| jobsearch mcp | 本地构建 | 8383(mcp端点) |
| 作业观察者 | 本地构建 | 仅限内部 |
| jobsearch postgres | postgres:16 | 仅限内部 |
| 求职qdrant | qdrant/qdrant | 仅限内部 |
| jobsearch valkey | valkey/valkey:7-alpine | 仅限内部 |
设置
- 克隆仓库:
git clone https://github.com/TadMSTR/jobsearch-mcp.git
cd jobsearch-mcp- 创建您的
.env文件 从模板中:
cp .env.example .env填写API密钥。看 .env.example 了解每个变量的详细信息。
- 如果使用工作观察者,也创建其env文件:
cp job-watcher.env.example job-watcher.env- 启动堆栈:
docker compose up -d- 验证它是否正在运行:
docker logs jobsearch-mcp --tail 20您应该看到FastMCP服务器在端口8383上启动。
代码更改后的重建
docker compose build jobsearch-mcp
docker compose up -d jobsearch-mcp从v1升级
嵌入模型从Voyage AI更改为Ollama bge-m3。Qdrant jobs 升级前必须删除集合——向量维度不兼容:
docker exec jobsearch-qdrant curl -X DELETE http://localhost:6333/collections/jobs集合将在下一次自动重新创建 index_job 电话。
______________________________________________________________________
连接到LibreChat
将以下内容添加到您的 librechat.yaml 在...之下 mcpServers:
mcpServers:
jobsearch:
type: streamable-http
url: http://host.docker.internal:8383/mcp
headers:
X-User-ID: "{{LIBRECHAT_USER_ID}}"
X-User-Email: "{{LIBRECHAT_USER_EMAIL}}"
X-User-Username: "{{LIBRECHAT_USER_USERNAME}}"服务器使用 X-User-ID 对每个LibreChat用户的所有状态进行分区——每个用户都有自己的管道、个人资料、笔记和查看/应用历史记录。
如果LibreChat在Docker中运行,你需要 host.docker.internal 以访问主机上的MCP服务器。确保您的LibreChat撰写文件包括:
extra_hosts:
- "host.docker.internal:host-gateway"在任何操作后重新启动LibreChat librechat.yaml 更改:
docker compose restart librechat______________________________________________________________________
项目结构
jobsearch-mcp/
├── Dockerfile
├── docker-compose.yml
├── .env.example
├── job-watcher.env.example
├── .gitignore
├── requirements.txt
├── requirements-dev.txt
├── pytest.ini
├── LICENSE
├── src/
│ ├── server.py # Thin FastMCP registry — registers tool modules
│ ├── db.py # Postgres schema, pipeline tracking, profiles (asyncpg)
│ ├── enricher.py # Multi-tier JD fetcher (Firecrawl → Crawl4AI → rawFetch) + Valkey cache
│ ├── vector.py # Qdrant + Ollama bge-m3 embedding and search
│ ├── scorer.py # Claude-powered fit scoring, profile parsing, resume tailoring
│ ├── job_watcher.py # Background poller — email alerts for new matches
│ ├── tools/
│ │ ├── jobs.py # Search, discovery, enrichment tools
│ │ ├── profile.py # Resume profile tools
│ │ ├── scoring.py # Fit scoring and cover letter tools
│ │ └── tracking.py # Application pipeline tools
│ └── sources/
│ ├── adzuna.py # Adzuna API
│ ├── rss.py # Remotive, WeWorkRemotely, Jobicy (RSS)
│ ├── usajobs.py # USAJobs API
│ ├── findwork.py # Findwork API (optional)
│ ├── themuse.py # The Muse API (optional)
│ └── jobspy.py # Indeed, Glassdoor, ZipRecruiter (python-jobspy, opt-in)
└── tests/
├── conftest.py
├── test_db.py
├── test_enricher.py
├── test_scorer.py
└── test_sources.py______________________________________________________________________
备注
- 多层富集。
get_job_detail任何在内部获取JD的工具都会首先尝试Firecrawl,如果Firecrawle失败或不可用,则会回退到Crawl4AI,然后回退到原始HTTP获取。结果缓存在Valkey中——对同一URL的重复调用是即时的。 - 事实上,Glassdoor,ZipRecruiter 是通过pythonjobspy可选的基于抓取的源代码。默认情况下不存在
search_jobscall--将它们显式添加到sources.这些网站积极打击掠夺者;服务器使用全局速率限制器(一次一个jobspy调用,最小间隔12秒)和每个站点的指数回退(60秒)→ 15min). - 美国职位 包含在默认源列表中。API键可以提高速率限制,但不是必需的。
score_fit截断内容。 JD的上限为6000个字符,简历在传递给Claude之前为3000个字符。适用于大多数列表;非常冗长的JD会失去尾巴。check_active回报active=None当页面加载但未找到明确的状态信号时,视为可能处于活动状态。- Postgres架构 启动时自动迁移(
ALTER TABLE ... ADD COLUMN IF NOT EXISTS).无需手动迁移。 - 必须拉动bge-m3 最初
index_job呼叫:ollama pull bge-m3. - Qdrant收集 (
jobs)首次使用时自动创建。 - 多用户 --所有状态都被划分为
X-User-ID同一实例上的多个LibreChat用户只能看到自己的数据。
______________________________________________________________________
安全
URL验证
所有职位列表URL都经过 _validate_url 在富集之前。只接受HTTPS URL——HTTP被阻止以防止明文凭据暴露。私有/内部IP范围(RFC 1918、环回、链路本地、IPv6 ULA)被阻止以防止SSRF。
容器硬化
Docker堆栈中的所有容器都运行:
user: 1000:1000--无根进程cap_drop: ALL--没有Linux功能no-new-privileges: true--防止特权升级- 孤立的
jobsearch-net网桥网络——数据库和缓存端口不向主机公开
凭证处理
API密钥(ANTHROPIC_API_KEY, ADZUNA_APP_KEY, USAJOBS_API_KEY等等)从环境变量读取并且仅在对其各自服务的出站请求中使用。服务器不存储或记录凭据。
简历和个人资料数据存储在Postgres中,并通过Ollama在本地嵌入——它们不会发送到任何云嵌入服务。
依赖性审计
CI运行 pip-audit 每一次推。依赖关系被固定 requirements.txt 用于可重复构建。
______________________________________________________________________
许可证
麻省理工学院
