cv-tailor-mcp
by AaronUgalde
README.md
# cv-tailor-mcp
An MCP server that tailors a one-page LaTeX CV to a specific job posting
without burning tokens on repetitive work: re-reading your whole CV source,
writing the same LaTeX boilerplate over and over, or pasting `pdflatex`
compiler logs into the chat.
It works identically from **Claude Code** and **Cursor** (or any other
MCP-compliant client) -- MCP is an open protocol, so the same server binary
is just registered in each client's own config file.
**This repo contains no personal data.** You keep your own `cv_facts.yaml`
(your real experience/projects/skills) in your own private repo, and point
this server at it via environment variables. See
[`cv_tailor_mcp/examples/cv_facts.example.yaml`](cv_tailor_mcp/examples/cv_facts.example.yaml) for a
fictional but complete example, and [`docs/SCHEMA.md`](docs/SCHEMA.md) for
the full schema.
## What it does
Instead of:
1. Reading your entire CV source file into the model's context every time,
2. reading a previous tailored variant as a style reference,
3. having the model write ~150-250 lines of near-identical LaTeX by hand, and
4. pasting compiler output into the chat 3-5 times while manually tightening
spacing to fit one page...
...you get a small set of tool calls:
| Tool | Purpose |
|---|---|
| `list_tags` | Discover the tags in your `cv_facts.yaml` |
| `get_facts(tags=[...])` | Get only the relevant slice of your experience/projects/skills |
| `list_variants` | See CVs you've already generated (tagline, tags) without re-reading each file |
| `render_cv(request)` | Generate `cv_<variant>.tex` from typed selections and temporary overrides |
| `compile_cv(variant)` | Run `pdflatex`, get back `{success, pages, first_error}`, auto-cleans `.aux`/`.log`/etc. |
| `render_and_compile(request)` | Render, auto-fit to one page, compile, and validate the PDF in one call |
`render_and_compile` tries `default`, `compact`, and `tight` spacing when
`spacing_profile` is `auto`, then returns paths, page count, the selected
profile, and structured diagnostics.
## Install
Requires Python 3.10+. A LaTeX distribution is optional during setup:
without `pdflatex`, the MCP can still generate `.tex` files and reports a
clear error if `compile_cv` is called.
```bash
git clone https://github.com/AaronUgalde/cv-tailor-mcp.git
cd cv-tailor-mcp
./setup.sh
```
The setup script:
1. Installs the command in a project-local virtual environment.
2. Creates `~/.cv-tailor/cv_facts.yaml` with fictional starter data.
3. Creates `~/.cv-tailor/generated/`.
4. Safely adds `cv-tailor` to `~/.cursor/mcp.json`, preserving other servers.
It never replaces an existing facts file or `cv-tailor` MCP entry unless you
explicitly choose the corresponding `--force-facts` or `--force-config` flag.
Next, replace the fictional information in `~/.cv-tailor/cv_facts.yaml`,
restart Cursor, and ask it to call `list_tags`.
### Install as a user command
Install the published PyPI package as an isolated command with:
```bash
uv tool install cv-tailor-mcp
cv-tailor-mcp init
```
`pipx install cv-tailor-mcp` works as an alternative to `uv`.
Useful setup options:
```bash
cv-tailor-mcp init --no-cursor
cv-tailor-mcp init --facts-path ~/private-cv/facts.yaml --output-dir ~/CVs
cv-tailor-mcp init --force-config # never overwrites facts
cv-tailor-mcp init --force-facts # explicit facts reset
cv-tailor-mcp configure --client all --facts-path ~/private-cv/facts.yaml --output-dir ~/CVs
cv-tailor-mcp configure --client all --project-dir ~/my-cv --force-config
cv-tailor-mcp doctor
```
`configure` updates or migrates Cursor and Claude MCP entries without creating
or changing CV facts. Existing configuration files are backed up before a
write, and unrelated MCP servers and custom environment values are preserved.
Use `--cursor-config` or `--claude-config` for non-default locations.
`cv-tailor-mcp serve` starts the stdio server and is normally invoked by
Cursor rather than run manually.
## Configuration
### Environment variables
| Variable | Required | Default |
|---|---|---|
| `CV_FACTS_PATH` | yes | -- |
| `CV_OUTPUT_DIR` | yes | -- |
| `CV_TEMPLATE_PATH` | no | bundled `cv_tailor_mcp/templates/default_cv_template.tex.j2` |
| `PDFLATEX_PATH` | no | resolved via `PATH` (`shutil.which("pdflatex")`) |
The server fails fast if the facts or output paths are missing. A missing
`pdflatex` only disables `compile_cv`; all other tools remain available.
Other MCP clients can use the command and environment variables generated in
Cursor's `mcp.json`. The transport is standard MCP over stdio.
## Example session
```
> list_tags
{"tags": [{"name": "backend", "experience": 1, "projects": 1, ...}, ...]}
> get_facts(tags=["backend", "cloud"])
{... only the entries/bullets tagged backend or cloud ...}
> render_and_compile({
"variant": "acme",
"tagline": "Backend Engineering Intern Candidate & Cloud",
"summary": "Plain text is escaped safely by default.",
"education": [{
"id": "edu_utaustin",
"header_override": {"graduation": "Expected Graduation: December 2027"}
}],
"skill_group_ids": ["languages", "backend_cloud"],
"experience": [{
"id": "acme_backend_intern",
"bullet_ids": ["acme_api_bullet", "acme_pipeline_bullet"],
"bullet_overrides": {
"acme_api_bullet": "Tailored plain-text wording with 25% improvement."
},
"extra_bullets": [{"latex": "Explicit raw \\textbf{LaTeX} opt-in."}]
}],
"projects": [{
"id": "proj_taskflow",
"bullet_ids": ["taskflow_realtime_bullet"],
"header_override": {"subtitle": "Distributed Systems Project"}
}],
"spacing_profile": "auto"
})
{"success": true, "tex_path": ".../cv_acme.tex", "pdf_path": ".../cv_acme.pdf",
"pages": 1, "spacing_profile": "default", "diagnostics": []}
```
## Bring your own template
If the bundled one-page style doesn't match yours, write your own
`.tex.j2` (Jinja2, using `<< >>` for variables and `<% %>` for blocks/loops
instead of the default `{{ }}`/`{% %}`, since LaTeX already uses `{`/`}`) and
point `CV_TEMPLATE_PATH` at it. It must accept the same context documented in
[`docs/SCHEMA.md`](docs/SCHEMA.md) -- see
[`cv_tailor_mcp/templates/default_cv_template.tex.j2`](cv_tailor_mcp/templates/default_cv_template.tex.j2)
for a working reference.
## Safety model
- `cv_facts.yaml` remains immutable from the MCP's perspective. Education,
experience, project, and bullet overrides exist only in the generated CV.
- Existing YAML text remains trusted LaTeX for backward compatibility. New
request text is plain and escaped by default; raw LaTeX requires
`{"latex": "..."}`.
- Output validation reports malformed commands, placeholders, missing
sections, page-count problems, and suspicious PDF whitespace. Always review
the final PDF before submitting it.
## License
MIT, see [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues