MCP Chamba
by yrey420
README.md
# MCP Chamba
[](https://www.python.org/)
[](https://modelcontextprotocol.io/)
[](LICENSE)
> **English summary.** MCP Chamba is a local [Model Context Protocol](https://modelcontextprotocol.io/)
> server that turns Claude (Claude Code or Claude Desktop) into a job-search assistant. It pulls
> openings from public job-board APIs (Get on Board, Remotive, RemoteOK, Jobicy, Himalayas,
> Arbeitnow) and company boards (Greenhouse, Lever, Ashby), scores each one 0–100 against *your*
> profile with explainable reasons, generates an ATS-friendly CV and cover letter tailored to the
> offer (PDF), refuses any figure that is not in your profile, and tracks every application and
> follow-up in a local SQLite database. Your data never leaves your machine. The code and docs are
> in Spanish; it is tuned for candidates in Colombia / LATAM looking for remote work.
*Chamba* es "trabajo" en buena parte de Latinoamérica. Este servidor MCP le da a Claude las
herramientas para ayudarte a conseguirla: **buscar → calificar → adaptar la hoja de vida y la
carta → postularte tú → hacer seguimiento**.
---
## Contenido
- [Qué hace](#qué-hace)
- [Herramientas](#herramientas)
- [Arquitectura](#arquitectura)
- [Requisitos](#requisitos)
- [Instalación](#instalación)
- [Configuración](#configuración)
- [Conectarlo a Claude](#conectarlo-a-claude)
- [Uso: el flujo típico](#uso-el-flujo-típico)
- [Cómo se califica una oferta](#cómo-se-califica-una-oferta)
- [Privacidad](#privacidad)
- [Estructura del proyecto](#estructura-del-proyecto)
- [Desarrollo y contribuciones](#desarrollo-y-contribuciones)
- [Limitaciones conocidas](#limitaciones-conocidas)
- [Licencia](#licencia)
## Qué hace
- **Busca ofertas** en portales con API pública y en los portales de empleo de las empresas que
sigues, en paralelo, y guarda lo visto para no repetir consultas (cada fuente se consulta como
máximo cada 6 horas por búsqueda).
- **Califica cada oferta de 0 a 100** contra tu perfil y explica por qué: qué tecnologías
coinciden, cuáles se parecen a las tuyas, qué te falta, si aceptan gente desde Colombia, si
pide más años de los que tienes o si paga menos de lo que esperas.
- **Genera la hoja de vida en PDF adaptada a la oferta**: una columna, texto real y sin tablas
(la leen bien los filtros ATS), con primero los logros que más se parecen a lo que piden, en
español o inglés según el idioma de la oferta.
- **Guarda la carta de presentación** (Markdown + PDF con el mismo encabezado de la hoja de vida).
- **No deja inventar**: si el titular, el resumen o la carta mencionan una cifra que no aparece en
tu perfil (`250.000 usuarios`, `95 %`…), la herramienta lo rechaza.
- **Lleva el seguimiento** de cada postulación (guardada → preparada → postulada → entrevista →
prueba técnica → oferta → aceptada / rechazada / descartada) y te avisa cuándo toca escribirle
al reclutador.
- **Prepara entrevistas** con tus historias STAR, lo que coincide y lo que falta, y el rango
salarial de ofertas parecidas.
- **Tú envías cada postulación.** El servidor nunca postula por ti: prepara los documentos y te
da el enlace.
## Herramientas
| Herramienta | Qué hace |
| --- | --- |
| `ver_perfil` | Devuelve tu perfil (experiencia, logros bilingües con etiquetas, habilidades, proyectos, historias STAR, reglas para el asistente). Claude lo consulta antes de escribir cualquier cosa sobre ti. |
| `buscar_ofertas` | Busca en las fuentes elegidas y en las empresas seguidas, califica y devuelve las mejores con sus razones. |
| `listar_ofertas` | Las mejores ofertas guardadas de los últimos días, de mayor a menor ajuste. |
| `ver_oferta` | Detalle completo: descripción, análisis de ajuste y la postulación con su historial. |
| `agregar_oferta` | Guarda y califica una oferta pegada a mano (LinkedIn, Computrabajo, elempleo, un correo de un reclutador…). |
| `descartar_oferta` | No volver a mostrar una oferta (no interesa, no aceptan tu país, ya cerró…). |
| `generar_cv_para_oferta` | Genera la hoja de vida en PDF adaptada a la oferta y deja la postulación en estado `preparada`. |
| `guardar_carta` | Guarda la carta de presentación (Markdown y PDF) en la carpeta de la postulación. |
| `actualizar_postulacion` | Cambia el estado de una postulación y programa el próximo seguimiento (7 días al postular, 3 tras una entrevista). |
| `ver_postulaciones` | Tablero: cuántas hay por estado, qué seguimientos están vencidos y la lista completa. |
| `preparar_entrevista` | Reúne la oferta, coincidencias, faltantes, historias STAR, cómo contar tu salida del último empleo y el mercado salarial. |
| `rango_salarial` | Mínimo, cuartiles y máximo de lo que pagan las ofertas guardadas (USD/mes y COP/mes), comparado con tu expectativa. |
| `seguir_empresa` | Agrega el portal Greenhouse, Lever o Ashby de una empresa para revisarlo en cada búsqueda (comprueba que exista). |
Además expone dos **guías (prompts MCP)**, que en Claude Code aparecen al escribir `/`:
- `rutina_diaria`: seguimientos vencidos, búsquedas del día (las de `objetivo.busquedas` en tu
perfil) y un top 10 para decidir qué preparar.
- `postular`: prepara una postulación completa (hoja de vida, carta, qué te falta y el enlace).
## Arquitectura
```text
Claude Code / Claude Desktop
│ MCP por stdio
▼
┌──────────────────── empleo/servidor.py ─────────────────────┐
│ 13 herramientas + 2 guías · reglas anti-invención de cifras │
└──┬──────────────┬───────────────┬──────────────┬────────────┘
│ │ │ │
▼ ▼ ▼ ▼
fuentes.py ajuste.py cv.py base.py
APIs públicas puntaje 0-100 Jinja2 + SQLite local
de empleo y razones WeasyPrint (ofertas, postulaciones,
(httpx async) (vocabulario) → PDF historial, consultas)
│ │ │
│ ▼ ▼
│ perfil.py ◀── perfil/perfil.yaml (tuyo, fuera de git)
▼
Get on Board · Remotive · RemoteOK · Jobicy · Himalayas · Arbeitnow
Greenhouse · Lever · Ashby (portales de empresas)
```
| Módulo | Responsabilidad |
| --- | --- |
| `empleo/servidor.py` | Define el servidor MCP (`mcp` SDK, transporte stdio), las herramientas, las guías y las instrucciones para el modelo. |
| `empleo/fuentes.py` | Un conector por portal; todos devuelven `Oferta` normalizada (texto limpio de HTML, salario, moneda, periodo, remoto, fecha). |
| `empleo/ajuste.py` | Calcula el puntaje, las razones y qué etiquetas de logros van primero en la hoja de vida. |
| `empleo/vocabulario.py` | Diccionario de tecnologías (expresiones regulares), cuáles dominas y cuáles se parecen. |
| `empleo/perfil.py` | Carga el perfil y lo resume para el asistente (solo hechos del perfil). |
| `empleo/cv.py` + `plantillas/` | Arma la hoja de vida y la carta en HTML y las convierte a PDF. |
| `empleo/base.py` | Persistencia en SQLite y máquina de estados de las postulaciones. |
| `empleo/rutas.py` | Rutas de archivos, configurables con variables de entorno. |
Claude escribe los textos (titular, resumen, carta); el servidor aporta los hechos, valida las
cifras y produce los documentos. No hace falta ninguna clave de API.
## Requisitos
- **Python 3.12+** y **[uv](https://docs.astral.sh/uv/)** (gestiona el entorno y las dependencias).
- **Pango**, que WeasyPrint necesita para generar los PDF:
- Debian / Ubuntu: `sudo apt install libpango-1.0-0 libpangoft2-1.0-0`
- Fedora: `sudo dnf install pango`
- Arch: `sudo pacman -S pango`
- macOS: `brew install pango`
- Windows: ver la [guía de WeasyPrint](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html).
- Recomendado: la fuente **Noto Sans** (`fonts-noto` / `noto-fonts`), que usa la plantilla.
- Claude Code o Claude Desktop.
## Instalación
```bash
git clone https://github.com/yrey420/MCP-CHAMBA.git
cd MCP-CHAMBA
uv sync # crea .venv e instala dependencias
uv run pytest # opcional: 39 pruebas, sin salir a internet
```
## Configuración
### 1. Tu perfil
Todo lo que el asistente sabe de ti sale de un único archivo YAML. Parte del ejemplo (un perfil
**ficticio**, "Alex Ejemplo"):
```bash
cp perfil/perfil.example.yaml perfil/perfil.yaml
```
y edítalo con tus datos. `perfil/perfil.yaml` está en `.gitignore`: nunca se sube. Si no existe,
el servidor arranca con el perfil de ejemplo y lo avisa en `ver_perfil` y al generar documentos.
Secciones principales (el ejemplo trae comentarios en cada una):
| Sección | Para qué sirve |
| --- | --- |
| `datos` | Nombre, ciudad, teléfono, correo, LinkedIn, GitHub, idiomas. Van en el encabezado de la hoja de vida y la carta; `nombre_corto` nombra los PDF (`CV-Alex-Ejemplo-EN.pdf`). |
| `objetivo` | Roles, modalidades, disponibilidad, `salario.expectativa_dicha_cop_mes` (para marcar ofertas que pagan menos), `busquedas` de la rutina diaria y `ciudad` (opcional). |
| `vocabulario` | Opcional: `domina` y `lenguajes` (claves de `empleo/vocabulario.py`) si tu stack no es backend Python/Java. |
| `titular`, `resumen` | Versiones generales en español (`es`) e inglés (`en`). |
| `habilidades` | Grupos de habilidades para la hoja de vida. |
| `experiencia` | Empleos con `logros`; cada logro en `es`/`en` y con `etiquetas` (`backend`, `integraciones`, `ia`, `erp`, `devops`, `seguridad`, `datos`, `frontend`, `liderazgo`) para ordenarlos según la oferta. |
| `proyectos` | Proyectos propios; `incluir: false` los deja fuera de la hoja de vida. |
| `educacion`, `historias` | Estudios e historias STAR para entrevistas. |
| `salida_ultimo_empleo` | Cómo contar por qué saliste de tu último empleo, con sus `reglas`. |
| `reglas_para_el_asistente` | Reglas que Claude debe respetar siempre (p. ej. no nombrar clientes protegidos por un acuerdo de confidencialidad). Se agregan a las instrucciones del servidor. |
| `decisiones` | Notas y decisiones tuyas que el asistente debe conocer. |
### 2. Empresas que sigues (opcional)
No hace falta crearlo: `seguir_empresa` escribe `perfil/empresas.yaml` (también ignorado por
git). El formato está en `perfil/empresas.example.yaml`.
### 3. Variables de entorno (opcionales)
Por defecto todo vive dentro de la carpeta del proyecto. Puedes guardar tus datos en otro lugar:
| Variable | Por defecto | Contenido |
| --- | --- | --- |
| `EMPLEO_PERFIL` | `perfil/perfil.yaml` | Tu perfil maestro. |
| `EMPLEO_EMPRESAS` | `perfil/empresas.yaml` | Portales de empresas seguidas. |
| `EMPLEO_SALIDA` | `salida/` | Hojas de vida generales y una carpeta por postulación (`salida/postulaciones/AAAA-MM-DD-empresa-cargo/`). |
| `EMPLEO_BASE` | `datos/empleo.db` | Base SQLite de ofertas, postulaciones e historial. |
No se necesitan claves ni tokens: todas las fuentes son públicas.
## Conectarlo a Claude
### Claude Code
```bash
claude mcp add --scope user empleo -- uv run --directory /ruta/absoluta/a/MCP-CHAMBA empleo-mcp
```
Con tus datos fuera del repositorio:
```bash
claude mcp add --scope user empleo \
-e EMPLEO_PERFIL="$HOME/.config/chamba/perfil.yaml" \
-e EMPLEO_BASE="$HOME/.local/share/chamba/empleo.db" \
-e EMPLEO_SALIDA="$HOME/Documentos/chamba" \
-- uv run --directory /ruta/absoluta/a/MCP-CHAMBA empleo-mcp
```
Comprueba que quedó conectado con `claude mcp list` (o `/mcp` dentro de Claude Code).
### Claude Desktop
Edita `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/`, Windows:
`%APPDATA%\Claude\`, Linux: `~/.config/Claude/`) y reinicia la aplicación:
```json
{
"mcpServers": {
"empleo": {
"command": "uv",
"args": ["run", "--directory", "/ruta/absoluta/a/MCP-CHAMBA", "empleo-mcp"],
"env": {
"EMPLEO_PERFIL": "/ruta/absoluta/a/perfil.yaml"
}
}
}
}
```
Si Claude Desktop no encuentra `uv`, pon la ruta completa (`which uv`) en `command`.
> El ejecutable también se llama `mcp-chamba`; `empleo-mcp` y `mcp-chamba` son equivalentes.
## Uso: el flujo típico
Le hablas a Claude en lenguaje natural; él decide qué herramienta usar.
```text
buscar_ofertas ─▶ ver_oferta ─▶ generar_cv_para_oferta ─▶ (tú envías) ─▶ actualizar_postulacion
+ guardar_carta └─▶ ver_postulaciones
```
1. **Buscar.** *"Búscame ofertas de python django remotas"* → `buscar_ofertas` devuelve el top con
puntaje, empresa, si aceptan Colombia, salario y razones.
2. **Revisar.** *"Muéstrame la de Acme"* → `ver_oferta` con lo que coincide y lo que falta.
3. **Preparar.** *"Prepárame la postulación"* (o la guía `/postular`) → Claude lee `ver_perfil`,
escribe un titular y resumen adaptados solo con hechos de tu perfil, genera la hoja de vida con
`generar_cv_para_oferta` y la carta con `guardar_carta`. Quedan en
`salida/postulaciones/<fecha>-<empresa>-<cargo>/` junto con `oferta.md`.
4. **Postular (tú).** Abres el enlace, revisas los PDF y envías.
5. **Registrar.** *"Ya me postulé"* → `actualizar_postulacion` con estado `postulada`; el
seguimiento queda programado para dentro de 7 días.
6. **Seguimiento.** *"¿Cómo voy?"* → `ver_postulaciones` muestra el tablero y los seguimientos
vencidos; Claude puede redactar el mensaje al reclutador.
7. **Entrevista.** *"Prepárame la entrevista con Acme"* → `preparar_entrevista`: preguntas
probables, respuestas con tus historias STAR, cómo cubrir lo que falta y qué cifra pedir.
Otros ejemplos:
- *"¿Cuánto están pagando por backend con Django?"* → `rango_salarial`
- *"Sigue el portal de Acme en Greenhouse, slug `acme`"* → `seguir_empresa`
- *"Te pego una oferta de LinkedIn, agrégala"* → `agregar_oferta`
- *"Esa no, descártala: solo aceptan gente en EE. UU."* → `descartar_oferta`
Sin Claude, también puedes generar la hoja de vida general:
```bash
uv run python generar_cv.py --idioma es
uv run python generar_cv.py --idioma en --etiquetas backend,integraciones,ia --salida salida/cv-ia.pdf
```
## Cómo se califica una oferta
El puntaje (0–100) suma cuatro partes y resta lo que pesa en contra. Lo importante son las
razones, que acompañan cada resultado:
| Parte | Peso | Cómo |
| --- | --- | --- |
| Tecnologías | 45 | Las que pide y dominas cuentan entero; las "parecidas" (FastAPI si sabes Django, Vue si sabes React…) cuentan a medias. Con menos de 4 tecnologías detectadas no se infla el puntaje. |
| Rol | 25 | Backend, full-stack, integraciones o IA puntúan alto; frontend, móvil, QA, ventas, etc., no. |
| Nivel | 10 | Senior / semi-senior suma; lead / staff es "un paso arriba"; junior resta. |
| Ubicación | 20 | Remoto que acepta Colombia / LATAM / mundial suma; "solo EE. UU.", W-2 o *security clearance* resta 25; presencial fuera de tu ciudad suma poco. |
| En contra | — | Lenguaje principal ajeno (Go, .NET, Ruby…) −20; muchos más años de experiencia de los que tienes −8; paga menos de tu expectativa −10. |
Al cambiar las reglas, sube `ajuste.VERSION`: las ofertas guardadas se vuelven a calificar solas.
## Privacidad
- **Todo es local.** Tu perfil, la base SQLite, las hojas de vida y las cartas viven en tu
máquina. No hay telemetría ni servidores propios.
- **Lo único que sale a internet** son consultas de lectura a las APIs públicas de los portales
(con el término de búsqueda), identificadas con un User-Agent propio y limitadas a una por
fuente y búsqueda cada 6 horas.
- **Lo que ves en Claude** sí pasa por Claude: lo que devuelven las herramientas (incluido tu
perfil) llega al modelo como cualquier otra conversación.
- **Nada personal entra en git**: `.gitignore` excluye `perfil/` (salvo los `*.example.yaml`),
`datos/`, `salida/`, `*.db`, `*.pdf` y `.env`. Las pruebas usan el perfil ficticio y carpetas
temporales.
- Usa `reglas_para_el_asistente` para lo que no debe decirse nunca (clientes bajo acuerdo de
confidencialidad, temas personales, etc.).
## Estructura del proyecto
```text
MCP-CHAMBA/
├── empleo/
│ ├── servidor.py # servidor MCP: herramientas, guías e instrucciones
│ ├── fuentes.py # conectores a portales de empleo
│ ├── ajuste.py # puntaje 0-100 y razones
│ ├── vocabulario.py # tecnologías y cómo reconocerlas
│ ├── perfil.py # carga y resumen del perfil
│ ├── cv.py # hoja de vida y carta en PDF
│ ├── base.py # SQLite: ofertas, postulaciones, historial
│ └── rutas.py # rutas y variables de entorno
├── perfil/
│ ├── perfil.example.yaml # perfil ficticio: cópialo a perfil.yaml
│ └── empresas.example.yaml # formato de las empresas seguidas
├── plantillas/
│ ├── cv.html # plantilla Jinja2 de la hoja de vida
│ ├── carta.html # plantilla de la carta
│ └── cv.css # estilos (una columna, apta para ATS)
├── tests/ # pytest, sin red (httpx.MockTransport)
├── generar_cv.py # CLI: hoja de vida general en PDF
├── pyproject.toml
├── uv.lock
└── LICENSE
```
Se crean al usarlo (y no se suben): `perfil/perfil.yaml`, `perfil/empresas.yaml`,
`datos/empleo.db` y `salida/`.
## Desarrollo y contribuciones
¡Bienvenidas! Issues y pull requests en español o inglés.
```bash
uv sync
uv run pytest # pruebas
uv run ruff check . # lint
uv run ruff format . # formato
```
Pautas:
- **Nueva fuente de ofertas:** una función `async` en `empleo/fuentes.py` que reciba el cliente
`httpx` y la consulta y devuelva `list[Oferta]`; regístrala en `POR_BUSQUEDA` (o en
`POR_EMPRESA` si es un ATS por empresa) y agrega una prueba con una respuesta de ejemplo en
`tests/test_fuentes.py`. Solo APIs públicas que permitan la consulta; nada de *scraping* que
viole términos de uso.
- **Nueva tecnología:** agrégala a `TECNOLOGIAS` en `empleo/vocabulario.py` (y, si aplica, a
`PARIENTES` o `ETIQUETA_DE`).
- **Cambios en el puntaje:** acompáñalos de pruebas en `tests/test_ajuste.py` y sube
`ajuste.VERSION`.
- **Nunca** subas datos reales en pruebas, ejemplos o capturas: usa el perfil ficticio.
## Limitaciones conocidas
- Pensado para buscar **desde Colombia / LATAM**: las reglas de ubicación (`GEO_OK`,
`GEO_RESTRINGIDA` en `empleo/ajuste.py`) y la conversión a pesos lo reflejan. Para otro país,
ajusta esas expresiones.
- Las tasas de cambio (`ajuste.USD_POR`) son aproximadas y fijas: sirven para comparar rangos, no
para cuentas exactas.
- LinkedIn, Computrabajo y elempleo no tienen API pública: esas ofertas se agregan pegando el
texto (`agregar_oferta`).
- La detección de tecnologías es por expresiones regulares: puede tener falsos positivos o
negativos en ofertas ambiguas.
## Licencia
[MIT](LICENSE) © 2026 Yeison Rey
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues