Skip to main content
Glama
pnaskardev

Batcave-MCP

by pnaskardev

Batcave — 简历审阅 MCP 服务器

一个 MCP 服务器,接收两份文档——一份 简历 和一份 职位描述——并让它们经过三阶段审阅。每个阶段都送入下一个阶段:未拿到匹配报告之前不能重写,未完成重写之前不能运行 ATS 检查。

管道

工具

作用

start_review

开始输入。接收简历和职位描述,可以以原始文本形式,也可以作为 .pdf / .docx / .txt / .md 文件的路径,提取文本,然后开启一个会话。

resume_match_report

阶段1。 扮演目标公司的资深招聘人员:给出以 100 为满分的匹配分数、最缺失的 5 个关键词,以及招聘经理在 10 秒内就会注意到的 3 个危险信号。

rewrite_experience_xyz

阶段2。 重写经历部分,以吸收阶段1的关键词并消除危险信号,每条经历都遵循 Google XYZ 格式——通过做 Z,完成了 X,以 Y 作为衡量。

ats_scroll_stopper_pass

阶段3。 模拟 ATS 解析器,同时扮演一位看到 200 份简历中第 147 份的招聘经理:识别哪些部分会被跳过,然后重写它们以阻止滚动。返回最终简历。

session_status

显示哪些阶段已完成、正在等待结果、尚未开始,以及下一步该调用什么工具。

list_sessions

显示已存储的会话,最先更新优先。

export_dossier

将整个审阅——三个阶段加上最终简历——作为一份 Markdown 文档返回。

delete_session

删除一个会话及其存储的所有数据。没有任何数据会自己过期。

Related MCP server: ats-resume-writer

阶段如何运行

服务器本身不会调用模型。它负责整合简报、保存状态并强制执行的顺序;实际推理由所连接客户端的模型完成。因此每个阶段工具会被调用两次:

  1. 返回 { session_id } { result } —— 返回该阶段的分析简报,其中已经嵌入了简历、职位描述以及之前所有阶段的输出。

  2. { session_id, result } —— 记录答案。result 会按该阶段的模式校验,因此一个只有 4 个关键词而不是 5 个的报告会被拒绝而非存储。

阶段2 读取已记录阶段1的报告。阶段3 读取阶段2 中的 updated_resume,而不是原始简历。乱序调用阶段时,会以报错方式返回你应当先调用的工具名。

简报中内置的两条规则

  • 不能编造指标。 如果原始简历中没有数字,重写时输出 [QUANTIFY: what to measure],并将其列入 placeholders_needing_user_input

  • 不要堆砌关键词。 只有在真实经历能够支撑某个关键词时,才将其加入;其余关键词连同原因放入 keywords_not_addressed 中返回。

传输方式

两个入口点,同一套工具:

入口

传输方式

适用场景

index.ts

stdio

同一台机器上的客户端: Claude Code, IDE

serve.ts

Streamable HTTP 上的 /mcp

远程客户端——这就是容器中运行的方式

stdio 是同一台机器上两个进程之间的通道;它不能通过网络访问。只提供 stdio 的容器将无法接受任何连接,这就是 EC2 路径使用 serve.ts 的原因。

serve.ts 要求两个变量,缺少其中任意一个都不会启动:

  • DB_URL — Postgres 连接字符串

  • MCP_AUTH_TOKEN — 共享密钥;每个请求都需要 Authorization: Bearer <token>

GET /healthz 是唯一一个无需认证的路由。它不会打开数据库连接,因此轮询它的负载均衡器永远不会唤醒 Postgres。

存储

一切都保存在 Postgres 中。服务器不会向本地磁盘写任何内容——它唯一进行的本地读取只是你指向的简历和职位描述文件。

resume_sessions(id, created_at, updated_at, company, role,
                resume jsonb, job_description jsonb)
resume_stages(session_id -> resume_sessions.id on delete cascade, stage, status,
              issued_at, completed_at, result jsonb, primary key (session_id, stage))
schema_migrations(module, id, applied_at)     -- shared, owned by src/platform/db.ts

采用两个表而不是单一个文档,这样记录一个阶段时只需写入一行,而不需要重写既有的简历,而且 list_sessions 根本不会去查询文档文本。表按模块加前缀,迁移会在该模块第一次查询时惰性运行——启动服务器并不会唤醒数据库。

迁移是只追加的,并记入 schema_migrations,因此每个迁移在每个数据库中正好执行一次。bun run db:migrate 应用所有尚未执行的迁移;服务器也会在该模块首次查询时,作为备选方案惰性执行。

没有任何数据会自动过期。会话积累到 delete_session 将其删除。

运行

bun install
bun run dev        # Postgres + the server, hot reload, nothing to configure

这就是 docker compose -f docker-compose.dev.yml up --build:它会启动 Postgres,创建 dev 和 test 数据库,运行迁移,并在 http://127.0.0.1:3000/mcp 上提供 MCP 服务,使用 token dev-token-not-a-secret。编辑 src/ 下任何内容都会重新加载正在运行的服务器。

或者,要在宿主机直接运行服务器:

export DB_URL='postgres://postgres:postgres@localhost:55432/batcave'
bun start          # stdio, for a client on this machine
bun run serve      # HTTP on :3000, also needs MCP_AUTH_TOKEN

有两个数据库命令,都不需要服务器运行:

bun run db:check      # can this machine reach DB_URL, and what is in it?
bun run db:migrate    # create or update the tables; safe to run repeatedly

db:check 是唯一以非服务形式打开连接的代码。两个入口点时会在启动时校验 DB_URL 参数,但只会在首条查询上惰性连接,因此顺利启动并不证明这没毛病。

检查:

bun run check      # Biome format + lint  (check:fix to apply)
bun run typecheck
bun test           # unit tests; no database needed

TEST_DB_URL='postgres://postgres:postgres@localhost:55432/batcave_test' bun test

端到端测试使用真实的有线协议,连接真实 Postgres,并在收尾时删除自己的表。它们读取 TEST_DB_URL,特意不是 DB_URL,这既能防止服务器连到真实数据库时会触发清理;同时 dev 堆栈自带一个独立的 batcave_test 数据库,所以测试不会干扰你正在运行的服务器。

本目录的 .mcp.json 为 Claude Code 注册了 stdio 服务器。若使用其他客户端:

{ "command": "bun", "args": ["index.ts"], "cwd": "/path/to/Batcave" }

在 EC2 上运行

export DB_URL='postgres://user:pass@host/db?sslmode=require'
export MCP_AUTH_TOKEN="$(openssl rand -hex 32)"

bun run db:check                       # confirm the instance is reachable from this box
docker compose run --rm mcp bun scripts/migrate.ts   # create the tables
docker compose up -d --build
docker compose logs -f mcp

迁移操作必须在服务器接收流量之前执行。 如果跳过这一步,服务器会在第一次工具调用时自行迁移,但那时迁移失败会表现为用户请求失败,而不是部署失败,而且第一个用户会等待数据库模式。每次发布包含新迁移时都再次运行 db:migrate,若没有待应用的迁移,就会什么都做。

Compose 会在任一变量未设置时拒绝启动。请把它们放在 shell profile 或 instance secret 中,不要放在本仓库里的文件中。

对外公布的端口是 127.0.0.1:3000,这是设计如此。 该端点采用明文 HTTP 并用 bearer token 认证;在开放的互联网上,任何能够看到数据包的人都能读取这个 token。请在其前配置 TLS——可以用终止 HTTPS 并转至实例的 ALB,或用 nginx/Caddy 在本地代理到 127.0.0.1:3000。然后,安全组只允许来自客户端的 443 端口,其余端口全网关闭;3000 端口保持不外暴露。

轮换 token 的做法是执行 export MCP_AUTH_TOKEN=... && docker compose up -d,这会重启容器。目前所有使用同一个 token——它不能标识用户,因此无法区分你的会话和别人的会话。基于用户的访问权限需要真正的 auth 以及 resume_sessions 表上的 0 字段;这两者目前都不存在。

resume_path 是在容器内部解析的,因此远程调用者无法使用它——他们笔记本电脑上的路径对服务器毫无意义。通过 HTTP 传递时,请提供 resume_textjob_description_text。如果你的文件位于容器中,并想让路径形式也可用,请挂载一个 volume。

docker-compose.yml 是纯生产环境的配置。本地开发应使用 docker-compose.dev.yml,它会启动自己的 Postgres,与此处生产配置没有任何共享。

布局

服务器是模块的宿主机。一个模块是自包含的工具全家,拥有自己的表和自己的词汇。目前唯一的模块是简历审阅;未来如果再出现与它无关的第二个模块,就是一个 src/features/ 下的文件夹,再在 index.ts 的列表中加入一个条目。

index.ts                          stdio entrypoint
serve.ts                          HTTP entrypoint (the container runs this)
src/modules.ts                    the one list of mounted modules, shared by both entries
src/module.ts                     the ToolModule contract every feature implements
src/server.ts                     mounts modules onto an McpServer
src/http.ts                       Streamable HTTP handler, bearer auth, /healthz
src/platform/                     feature-agnostic; knows nothing about resumes
  db.ts                             lazy Postgres pool + per-module migration runner
  documents.ts                      text / pdf / docx extraction
  stored-document.ts                what an extracted document looks like
  tool-result.ts                    keeps `content` and `structuredContent` in step
src/features/resume-review/
  index.ts                          the ToolModule: name, migrations, register()
  migrations.ts                     this module's tables
  sessions.ts                       repository, domain types, stage gating
  briefs.ts                         the three briefs
  schemas.ts                        zod schema per stage result
  stage-tool.ts                     the brief-then-record tool shape
  dossier.ts                        markdown rendering
  tools/                            one file per group of registered tools
    intake.ts, stages.ts, dossier.ts, session-admin.ts

支撑这一结构的两条规则如下:

  • src/platform 绝不从 src/features import。 任何第二个模块同样需要的东西放入 platform;只有简历审阅需要的东西留在 feature 内。

  • 模块之间不得互相 import。 相互需要了解对方的两个模块,其实就应该是一个模块。

stage-tool.ts 有意放在 feature 里,而不是平台层中。这种“先 briefing,再记录”的形态可能将来会变为可复用,但它今天只有唯一的消费者;在第二个消费者出现前就猜测通用形式,正是平台层糜烂的原因。

添加模块

// src/features/interview-prep/index.ts
export const interviewPrep: ToolModule = {
  name: "interview-prep",
  migrations,                       // its own tables, namespaced in schema_migrations
  register(server) {
    registerWhateverTools(server);
  },
};
// index.ts
const server = createServer([resumeReview, interviewPrep]);

这就是全部契约。迁移只执行一次,按模块记录在 schema_migrations 中,并在该模块第一次访问数据库时惰性运行——一个未使用的模块不会带来任何必须连接到数据库的消耗。tests/modules.test.ts 使用一个与简历毫无关系的桩模块来验证这个接缝。

参与贡献

参见 CONTRIBUTING.md。只需 bun run dev 即可完成完整环境配置。安全方面问题请通过 SECURITY.md 提交,不要在公开的 issue 里报告。

YouTube 许可

MIT.

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Tailor resumes, generate cover letters, render CVs as PDF, and browse 22+ templates.

  • Search 6.3M+ live jobs from companies' own career pages, plus resume tailoring & cover letters.

  • Search AI-native jobs, inspect application forms, and fetch free interview-prep resources.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pnaskardev/Batcave-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server