Batcave-MCP
Batcave — servidor MCP de revisión de currículums
Un servidor MCP que toma dos documentos — un currículum y una descripción del puesto — y los somete a una revisión en tres fases. Cada fase alimenta a la siguiente: no puedes reescribir antes de tener un informe de coincidencia, y no puedes ejecutar la pasada ATS antes de tener una reescritura.
El proceso
Herramienta | Qué hace |
| Recepción. Toma el currículum y la descripción del puesto como texto sin formato o como ruta a un archivo |
| Fase 1. Reclutador sénior en la empresa objetivo: puntuación de coincidencia sobre 100, las 5 palabras clave que faltan más importantes, 3 señales de alerta que un responsable de contratación detecta en menos de 10 segundos. |
| Fase 2. Reescribe la sección de experiencia para incorporar las palabras clave de la fase 1 y eliminar sus señales de alerta, cada viñeta con el formato Google XYZ — logrado X, medido por Y, haciendo Z. |
| Fase 3. Pasada del analizador ATS más un responsable de contratación en el currículum n.º 147 de 200: qué secciones se omiten, y luego las reescribe para detener el desplazamiento. Devuelve el currículum final. |
| Qué fases están completadas, esperando un resultado o sin iniciar, y qué llamar a continuación. |
| Sesiones almacenadas, las más recientes primero. |
| Devuelve la revisión completa — las tres fases más el currículum final — como un único documento markdown. |
| Elimina una sesión y todo lo almacenado en ella. Nada caduca por sí solo. |
Related MCP server: ats-resume-writer
Cómo se ejecuta una fase
El servidor no llama a un modelo. Compone el informe, mantiene el estado y hace cumplir el orden; el modelo del cliente conectado hace el razonamiento. Por lo tanto, cada herramienta de fase se llama dos veces:
{ session_id }— devuelve el informe de análisis de esa fase, con el currículum, la descripción del puesto y la salida de todas las fases anteriores ya incrustadas.{ session_id, result }— registra la respuesta.resultse valida contra el esquema de la fase, por lo que un informe con cuatro palabras clave en lugar de cinco se rechaza en lugar de almacenarse.
La fase 2 lee el informe registrado de la fase 1. La fase 3 lee updated_resume de la fase 2, no el original. Una fase llamada fuera de orden falla con el nombre de la herramienta que debes llamar primero.
Dos reglas integradas en los informes
Sin métricas inventadas. Cuando el currículum original no tiene un número, la reescritura emite
[QUANTIFY: what to measure]y lo lista enplaceholders_needing_user_input.Sin relleno de palabras clave. Una palabra clave solo se incluye donde la experiencia real la respalda; el resto se devuelven en
keywords_not_addressedcon el motivo.
Transportes
Dos puntos de entrada, mismas herramientas:
Entrada | Transporte | Para |
| stdio | Un cliente en la misma máquina — Claude Code, un IDE |
| Streamable HTTP en | Un cliente remoto — esto es lo que se ejecuta en el contenedor |
stdio es una tubería entre dos procesos en una misma máquina; no se puede acceder a través de una red. Un contenedor que sirva stdio no aceptaría conexiones, por eso la ruta de EC2 usa serve.ts.
serve.ts requiere dos variables y se niega a arrancar sin cualquiera de ellas:
DB_URL— cadena de conexión de PostgresMCP_AUTH_TOKEN— secreto compartido; cada solicitud necesitaAuthorization: Bearer <token>
GET /healthz es la única ruta sin autenticación. No abre ninguna conexión de base de datos, por lo que un balanceador de carga que la consulte nunca despierta a Postgres.
Almacenamiento
Todo vive en Postgres. El servidor no escribe nada en el disco local — las únicas lecturas locales son los archivos de currículum y descripción del puesto a los que lo apuntas.
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.tsDos tablas en lugar de un solo documento, de modo que registrar una fase escribe una fila en lugar de reescribir ambos currículums, y list_sessions nunca selecciona el texto del documento. Las tablas llevan el prefijo del módulo y las migraciones se ejecutan de forma diferida en la primera consulta de ese módulo — iniciar el servidor no despierta la base de datos.
Las migraciones son de solo añadido y se registran en schema_migrations, por lo que cada una se ejecuta exactamente una vez por base de datos. bun run db:migrate aplica lo que está pendiente; el servidor también lo hace de forma diferida en la primera consulta de un módulo como respaldo.
Nada caduca. Las sesiones se acumulan hasta que delete_session las elimina.
Ejecutarlo
bun install
bun run dev # Postgres + the server, hot reload, nothing to configureEso es docker compose -f docker-compose.dev.yml up --build: levanta Postgres, crea las bases de datos de desarrollo y de prueba, ejecuta las migraciones y sirve MCP en http://127.0.0.1:3000/mcp con el token dev-token-not-a-secret. Editar cualquier cosa bajo src/ recarga el servidor en ejecución.
Para ejecutar el servidor directamente en el host en su lugar:
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_TOKENDos comandos de base de datos, ninguno de los cuales necesita que el servidor esté en ejecución:
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 repeatedlydb:check es lo único que abre una conexión sin servir. Ambos puntos de entrada validan DB_URL al arrancar, pero se conectan de forma diferida en la primera consulta, por lo que un arranque limpio no demuestra nada.
Comprobaciones:
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 testLas pruebas de extremo a extremo hablan el protocolo de red real contra un Postgres real y eliminan sus tablas al finalizar. Leen TEST_DB_URL, deliberadamente no DB_URL, por lo que apuntar el servidor a una base de datos real no puede activar la limpieza — y el stack de desarrollo incluye una base de datos batcave_test separada para que ejecutar las pruebas nunca moleste a un servidor que tengas en marcha.
.mcp.json en este directorio registra el servidor stdio para Claude Code. Para otro cliente:
{ "command": "bun", "args": ["index.ts"], "cwd": "/path/to/Batcave" }Ejecutarlo en 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 mcpMigra antes de que el servidor reciba tráfico. Se migrará a sí mismo en la primera llamada a una herramienta si te saltas esto, pero entonces una migración rota aparece como una solicitud de usuario fallida en lugar de un despliegue fallido, y el primer llamador espera por el esquema. Vuelve a ejecutar db:migrate en cada despliegue que incluya una migración nueva; es un no-op cuando no hay nada que aplicar.
Compose se niega a arrancar si alguna de las dos variables no está definida. Guárdalas en el perfil del shell o en un secreto de instancia — no en un archivo de este repositorio.
El puerto publicado es 127.0.0.1:3000, deliberadamente. El endpoint habla HTTP en texto plano y se autentica con un token de portador: a través de internet abierto, ese token es legible para cualquiera en la ruta. Pon TLS delante — un ALB que termine HTTPS y reenvíe a la instancia, o nginx/Caddy en la misma máquina haciendo de proxy a 127.0.0.1:3000. Entonces el grupo de seguridad debería permitir el 443 desde tus clientes y nada más; el puerto 3000 permanece cerrado al mundo.
Rotar el token es export MCP_AUTH_TOKEN=... && docker compose up -d, lo que reinicia el contenedor. Hay un único token para todos — no identifica a nadie, por lo que no puede distinguir tus sesiones de las de un amigo. El acceso por usuario necesita autenticación real y una columna owner en resume_sessions; ninguna de las dos existe todavía.
resume_path se resuelve dentro del contenedor, por lo que un llamador remoto no puede usarlo — las rutas en su portátil no significan nada para el servidor. Por HTTP, pasa resume_text y job_description_text. Monta un volumen si quieres que la forma de ruta funcione para archivos en la máquina.
docker-compose.yml es solo el stack de producción. El desarrollo local usa docker-compose.dev.yml, que trae su propio Postgres y no comparte nada de esta configuración.
Estructura
El servidor es un anfitrión de módulos. Un módulo es una familia autocontenida de herramientas que posee sus propias tablas y su propio vocabulario. La revisión de currículums es la única hoy; un segundo módulo no relacionado es una carpeta bajo src/features/ y una entrada en la lista de 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.tsDos reglas sostienen la estructura:
src/platformnunca importa desdesrc/features. Cualquier cosa que un segundo módulo también quisiera pertenece a platform; cualquier cosa que solo la revisión de currículums quiera se queda en la feature.Ningún módulo importa a otro módulo. Dos módulos que necesitan saber el uno del otro son un solo módulo.
stage-tool.ts vive deliberadamente dentro de la feature en lugar de en platform. La forma de informe-y-registro podría resultar reutilizable, pero hoy tiene exactamente un consumidor, y adivinar el caso general antes de que exista un segundo es como se pudre una capa de platform.
Añadir un módulo
// 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]);Ese es todo el contrato. Las migraciones se aplican una vez cada una, se registran por módulo en schema_migrations y se ejecutan de forma diferida la primera vez que ese módulo toca la base de datos — un módulo sin usar no cuesta ningún viaje de ida y vuelta. tests/modules.test.ts ejercita la unión con un módulo stub que no tiene nada que ver con currículums.
Contribuciones
Consulta CONTRIBUTING.md — bun run dev es toda la configuración. Los problemas de seguridad se gestionan a través de SECURITY.md, no mediante incidencias públicas.
Licencia
MIT.
This server cannot be installed
Maintenance
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
- FlicenseNot gradedqualityDmaintenanceAnalyzes resumes against job descriptions to identify missing skills, keywords, and improvement opportunities using AI. Provides structured feedback including gap analysis, ATS optimization suggestions, and actionable recommendations to improve job application success.
- AlicenseAqualityCmaintenanceRewrites resumes to beat ATS screening (Workday, Greenhouse, iCIMS, Taleo) against a specific job description, with strict truthfulness guardrails — never invents dates, metrics, titles, or seniority. Pay-what-you-want access codes ($0 works).2MIT
- FlicenseNot gradedqualityDmaintenanceAutomates ATS resume scanning via Jobscan, enabling AI to iteratively scan, analyze gaps, optimize, and rescan resumes against job descriptions to improve match rates.3
- FlicenseAqualityCmaintenanceEnables tailoring resumes to job descriptions by scraping JDs, applying rules, and generating optimized DOCX resumes.11
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.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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