Skip to main content

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:

  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(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:

  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 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_profile presets instead (see above).
  • cv_facts.yaml remains the immutable source of truth from the MCP's perspective. render_cv can tailor selected bullets through bullet_overrides, but those rewrites only affect the generated CV.
  • compile_cv only 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

cv_tailor_mcp-0.2.0.tar.gz (19.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cv_tailor_mcp-0.2.0-py3-none-any.whl (18.0 kB view details)

Uploaded Python 3

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

Hashes for cv_tailor_mcp-0.2.0.tar.gz
Algorithm Hash digest
SHA256 bbaa4841d1d8990db202fee3133076ebf83c15f320742ac95faa5166c9a9e35c
MD5 d23c0a41f076543ce0c898e25ac916bc
BLAKE2b-256 e5e9ec17c71a74a7911651947f24eac0b39fe761c7656a5d7cbdab011ae0b06c

See more details on using hashes here.

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

Hashes for cv_tailor_mcp-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f9334bd731030edfa74afcdfd2520f3c77c3b08718644072807e1761c57367dd
MD5 8e8ea1a058df0d493340ca7b9058b442
BLAKE2b-256 a83e5d7c2537d7defbf596547cc6c06201d839ff655a1ac45a83d51c1afb1779

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page