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 for a
fictional but complete example, and docs/SCHEMA.md for
the full schema.
What it does
Instead of:
- Reading your entire CV source file into the model's context every time,
- reading a previous tailored variant as a style reference,
- having the model write ~150-250 lines of near-identical LaTeX by hand, and
- 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(payload) |
Generate cv_<variant>.tex from ids, with optional per-CV bullet rewrites that never modify cv_facts.yaml |
compile_cv(variant) |
Run pdflatex, get back {success, pages, first_error}, auto-cleans .aux/.log/etc. |
There's no automatic "shrink until it fits one page" tool in v1 -- if
compile_cv reports 2 pages, call render_cv again with
spacing_profile: "compact" or "tight" (see docs/SCHEMA.md).
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.
git clone https://github.com/AaronUgalde/cv-tailor-mcp.git
cd cv-tailor-mcp
./setup.sh
The setup script:
- Installs the command in a project-local virtual environment.
- Creates
~/.cv-tailor/cv_facts.yamlwith fictional starter data. - Creates
~/.cv-tailor/generated/. - Safely adds
cv-tailorto~/.cursor/mcp.json, preserving other servers.
It never replaces an existing facts file or cv-tailor MCP entry unless you
explicitly pass --force.
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
The project is packaged for PyPI. After publishing it, users can install it as an isolated command with:
uv tool install cv-tailor-mcp
cv-tailor-mcp init
pipx install cv-tailor-mcp works as an alternative to uv.
Useful setup options:
cv-tailor-mcp init --no-cursor
cv-tailor-mcp init --facts-path ~/private-cv/facts.yaml --output-dir ~/CVs
cv-tailor-mcp doctor
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_cv({
"variant": "acme",
"tagline": "Backend Engineering Intern Candidate",
"summary": "...",
"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, LaTeX-ready wording for this CV only."
}
}],
"projects": [{"id": "proj_taskflow", "bullet_ids": ["taskflow_realtime_bullet"]}]
})
{"tex_path": ".../cv_acme.tex", "line_count": 118, "spacing_profile": "default"}
> compile_cv("acme")
{"success": true, "pages": 1, "first_error": null}
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 -- see
cv_tailor_mcp/templates/default_cv_template.tex.j2
for a working reference.
Scope / non-goals for v1
- No auto-fit-to-one-page spacing tool -- three manual
spacing_profilepresets instead (see above). cv_facts.yamlremains the immutable source of truth from the MCP's perspective.render_cvcan tailor selected bullets throughbullet_overrides, but those rewrites only affect the generated CV.compile_cvonly confirms the page count pdflatex reports -- it doesn't check whether the result looks good. Open the PDF yourself before you send it anywhere.
License
MIT, see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cv_tailor_mcp-0.2.0.tar.gz.
File metadata
- Download URL: cv_tailor_mcp-0.2.0.tar.gz
- Upload date:
- Size: 19.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bbaa4841d1d8990db202fee3133076ebf83c15f320742ac95faa5166c9a9e35c
|
|
| MD5 |
d23c0a41f076543ce0c898e25ac916bc
|
|
| BLAKE2b-256 |
e5e9ec17c71a74a7911651947f24eac0b39fe761c7656a5d7cbdab011ae0b06c
|
File details
Details for the file cv_tailor_mcp-0.2.0-py3-none-any.whl.
File metadata
- Download URL: cv_tailor_mcp-0.2.0-py3-none-any.whl
- Upload date:
- Size: 18.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9334bd731030edfa74afcdfd2520f3c77c3b08718644072807e1761c57367dd
|
|
| MD5 |
8e8ea1a058df0d493340ca7b9058b442
|
|
| BLAKE2b-256 |
a83e5d7c2537d7defbf596547cc6c06201d839ff655a1ac45a83d51c1afb1779
|