Skip to main content

honest-cv

CI License: MIT

Honest, ATS-safe CVs tailored to each role, generated by your AI agent. honest-cv is an MCP server, a CLI and a Claude Code skill. It keeps one source of truth about your experience and turns it into a PDF and DOCX for each type of role. It never claims a skill you don't have.

Leer en español

Demo: skill checklists including your own, scoring a job post, a verified build, a cover letter that refuses a skill the person lacks, LinkedIn texts and logging the application

Why

Many CVs that get no callbacks don't fail on design. They fail on targeting, and on details that quietly break text extraction. AI makes the opposite problem easy: a model will happily stuff a CV with keywords you can't defend in an interview. honest-cv is built around three ideas:

  • The code enforces honesty, not a prompt. Every skill has a level. A skill at familiarity level is always labeled as such, and a skill you don't have is never printed. Raising a level takes written evidence and your confirmation, which the server asks you for directly. The model can't confirm on your behalf.
  • Gaps are measured against your own inventory. For each keyword a job post asks for, honest-cv tells you which of three cases it is:
    • you have it, but this CV doesn't show it;
    • you only know it at familiarity level;
    • you don't have it.
  • ATS safety is checked on every build. Every PDF goes through two text extractors (pypdf and pdfminer.six). The build checks that the text is one column, has no ligatures, keeps each date on its role's line, follows the right reading order and includes every bullet.

The server is deterministic and needs no API key. Your MCP client's model runs the conversation; honest-cv validates, builds, verifies and measures. It keeps your profile in a folder on your machine and never sends it anywhere: see Privacy for what your AI client does with it.

A generated one-page CV for the fictional Ada Example

Quick start

honest-cv needs uv. Typst and the fonts ship inside the package, so there is nothing else to install. It runs on Linux (glibc) on x86_64 and ARM64, macOS on Intel and Apple Silicon, and Windows on x64. Alpine Linux and Windows on ARM aren't supported, because the typst package doesn't publish wheels for them.

Claude Code

claude mcp add honest-cv -- uvx honest-cv serve --profile ~/cv-profile

To install the skill as well, which guides the interview and the tailoring:

mkdir -p ~/.claude/skills/honest-cv
curl -fsSL https://raw.githubusercontent.com/JesusCabreraReveles/honest-cv/master/skills/honest-cv/SKILL.md \
  -o ~/.claude/skills/honest-cv/SKILL.md

Then start with "interview me for my CV", or run /mcp__honest-cv__interview. You can answer by voice with your system's dictation (on macOS: System Settings → Keyboard → Dictation): the interview reads names and numbers back to you before saving them, since dictation mishears them.

The first interview is long. With a long career and many skills it can take several hours. You don't have to do it in one sitting: everything is saved as you go, so stop whenever you like and say "let's continue my CV interview" later. A good first session covers your contact details, your two most recent roles and the skills checklist; with that you can already build a CV, and older roles can wait.

Cursor

Add this to ~/.cursor/mcp.json, or to .cursor/mcp.json in a project:

{
  "mcpServers": {
    "honest-cv": {
      "command": "uvx",
      "args": ["honest-cv", "serve", "--profile", "/absolute/path/to/cv-profile"]
    }
  }
}

Claude Desktop

Edit claude_desktop_config.json:

  • on macOS, ~/Library/Application Support/Claude/;
  • on Windows, %APPDATA%\Claude\.

Add the same mcpServers block as in the Cursor example and restart the app. If the app can't find uvx, use its absolute path (which uvx).

How each client confirms a skill upgrade

Raising a skill's level needs your own confirmation, which the server asks for through MCP elicitation. Not every client supports it yet:

Client Where you confirm
Claude Code In the chat: a form with one "no / sí" field per upgrade, up to 10 at a time. Only the ones you answer "sí" are saved; Decline saves nothing.
Claude Desktop In a terminal. The server refuses the change and gives you the command to run: uvx honest-cv skill <profile> "<name>" <level> --evidence "...".
Other clients In the chat if they support elicitation, otherwise in a terminal as above.

Your profile

A profile is a folder with four YAML files. Create one with honest-cv init ~/cv-profile, or let the interview prompt fill it in with you. Keep it out of public repositories.

File What it holds
profile.yaml Contact details, roles, education, certifications, publications, courses and languages.
bullets.yaml A pool of achievements, each with an id, a role and tags.
skills.yaml Your skill inventory, with levels and evidence.
variants.yaml One CV plan per type of role. A variant can inherit from another with extends:.

Any text can be a single string or one string per language: {en: ..., es: ...}. CVs can be built in English and in Spanish.

Level Meaning On the CV
P Used in production at a job Shown
L Your own project or lab Shown
F Familiarity Only on the line labeled "Familiarity"
X You don't have it Never shown, but recorded so gap reports are honest

Skill checklists

Recording dozens of skills one question at a time is slow. A checklist is a Markdown file you mark in your editor, one letter per line:

## Messaging and streaming
evidence: Payments platform at Acme, 2020–2026
- [P] Kafka — reconciliation flow, 2M events a day · years: 6 · last year: 2026
- [F] RabbitMQ — a workshop and the official tutorials
- [X] NATS

Text after " — " is the evidence, and an evidence: line under a heading covers that section. Lines without evidence are never saved: they come back as pending, and your assistant asks you about them in groups. Upgrades are confirmed once for the whole file.

honest-cv checklist list                                     # backend-jvm, platform-sre, ...
honest-cv checklist new ~/cv-profile backend-jvm engineering-lead
honest-cv checklist apply ~/cv-profile ~/cv-profile/checklist.md

Any profession works. The built-in checklists are for software roles, but a template is just a Markdown list in this format: save your own (or one someone shared) in ~/cv-profile/checklists/ and it shows up in checklist list and combines with the others in checklist new, which also takes a path. A template can declare synonyms, such as same: Work Breakdown Structure = WBS, and the job score, the gaps and the letter check use them, along with its skill names. Start from examples/checklists/project-management.md. You can also apply a file you marked yourself, in English or Spanish (años, último uso). When you're done, honest-cv names the P and L skills that no achievement shows, and your assistant asks you for one real example of each.

honest-cv checklist list ~/cv-profile                        # yours too, e.g. accounting
honest-cv checklist new ~/cv-profile accounting engineering-lead

New checklists for other kinds of roles are welcome: see CONTRIBUTING.md.

See examples/ada/ for a complete, fictional profile with three variants, a job post and its keywords.

Privacy

  • honest-cv itself makes no network calls. No accounts, no telemetry, no API keys. Your profile is a folder of YAML files on your machine, and the PDF engine and fonts run locally. You can check it: every command works with the network turned off.
  • Your AI client is a different matter. Whatever the client's model reads through the tools goes to that model's provider, under the provider's terms, like anything else you share in a chat. That includes your skills, bullets and the text of your CVs.
  • Some clients copy files to the cloud. To edit files directly, Claude Desktop may ask for access to the profile folder and say that the files leave your device. honest-cv's editing tools make that unnecessary: decline the request and ask the assistant to use the tools.
  • If you want nothing to leave your machine, use only the CLI (build, verify, score, skill) and edit the YAML files yourself.

Import what you already have

honest-cv import reads a JSON Resume file or LinkedIn's data export (Settings → Data privacy → Download your data, a ZIP of CSVs, or its folder if your browser unzipped it). Ask for the full archive: the basic one, offered when you pick files, has only your headline and summary, without roles. Contact, roles, education, certifications, languages and each role's achievements go into your profile; with a headline and a summary it also creates a first variant, so build works right away.

  • It only adds what your profile lacks and never overwrites. Try --dry-run first.
  • Imported achievements are tagged imported: review their wording.
  • Skills arrive without a level, because neither format says whether you used a skill in production. They go to imported-skills.md, a checklist for you to mark.

LinkedIn's "Save to PDF" profile changes layout without notice, so it isn't parsed: with an MCP client, the import_linkedin_pdf prompt reads its text and proposes each section for you to approve.

Track your applications

honest-cv apps keeps a log in applications.yaml, inside your profile folder: which variant and files went to which company, and each status with its date (draft, applied, interview, offer, rejected, withdrawn). Logging the same company and role again updates it. With an MCP client, your assistant offers to log an application after building the CV or letter, and list_applications points out the ones with no news for two weeks. It also keeps the job post's text in posts/, since links expire, and what to prepare for the interview: ask your assistant "what did I need to prepare for Acme?".

LinkedIn

honest-cv linkedin turns a variant into LinkedIn's texts, each within its limit: the headline (your title and up to five production skills), About (the summary and impact bullets), one description per position from its bullets, and the skills list. It writes them to a Markdown file to copy from. The skills are P and L only: LinkedIn can't label familiarity, so F skills stay out, and X skills never appear. With an MCP client, the linkedin_profile prompt polishes the wording and checks it with check_claims.

Export

To go the other way, honest-cv export writes a variant as JSON Resume for other CV tools and sites, with the same rules as the CV: X skills never appear, and F skills only in a "Familiarity" group with that level.

Cover letters

The cover_letter prompt drafts a one-page letter for a job post from your own bullets. Before it is built, check_claims reads the text against your profile:

  • Problems block the build: a skill you marked X, or a familiarity skill presented as experience ("I use Rust in production" when Rust is F; "I am familiar with Rust" is fine).
  • Warnings are for you to review: a technology neither your inventory nor your bullets show, or a number that appears nowhere in your profile. If it is true, record it first; if not, it comes out of the letter.

The letter uses the CV's fonts and passes the same ATS checks. Without an AI client, write it in a text file (paragraphs separated by blank lines) and use honest-cv letter check and letter build.

CLI

Run any command with uvx honest-cv ..., or install it once with uv tool install honest-cv to type just honest-cv.

honest-cv init ~/cv-profile                        # create the profile templates
honest-cv build ~/cv-profile -v backend -l en -l es # PDF + DOCX, verified on every build
honest-cv verify any-cv.pdf --text                  # check any PDF, even one made elsewhere
honest-cv score ~/cv-profile -k "Kubernetes,Go" -p "Rust"   # coverage per variant, with gaps
honest-cv skill ~/cv-profile "Rust" L --evidence "CLI tool on GitHub"  # asks you to confirm
honest-cv import ~/cv-profile linkedin-export.zip --dry-run         # or resume.json
honest-cv export ~/cv-profile -v backend -l en                       # JSON Resume
honest-cv apps log ~/cv-profile --company Acme --role "SRE" -s applied -v platform
honest-cv apps list ~/cv-profile                                     # latest first, with counts
honest-cv linkedin ~/cv-profile -v platform -l en                    # texts to paste into LinkedIn
honest-cv remove skill ~/cv-profile "Kafka"                          # asks you to confirm
honest-cv letter check ~/cv-profile letter.txt -k "Kubernetes,Go"     # claims your profile doesn't support
honest-cv letter build ~/cv-profile letter.txt --company Acme --role "Platform Engineer" -l en
honest-cv serve --profile ~/cv-profile              # the MCP server, over stdio

verify works on any PDF. Use --text to see exactly what a parser extracts. Its single_column check also catches a common defect: a date aligned to the right, which parsers split away from its role.

MCP tools and prompts

Name What it does
init_profile Creates a profile folder with commented templates.
get_inventory Returns your skills, grouped by level, with years and evidence.
set_skill Adds or changes a skill. To raise its level it needs evidence and asks you to confirm.
set_skills Adds or changes several skills at once. Upgrades are confirmed with one question that lists each change.
checklist_list Lists the built-in checklists.
checklist_new Merges built-in checklists into one file, prefilled from your inventory.
checklist_apply Saves the skills marked in a checklist and returns the lines that still need evidence.
add_bullet Adds an achievement to the pool.
remove_skill, remove_bullet, remove_role, remove_publication Delete for good, and remove every use in the variants. A role with bullets needs with_bullets. Clients ask before running them.
set_contact Changes contact details: name, email, phone, location, links.
set_profile_details Replaces the certifications list, the courses list and the spoken-languages line.
upsert_role Adds a role in date order, or changes fields of an existing one.
upsert_education Adds or changes an education entry.
upsert_publication Adds or changes a book, article or paper the person wrote. Each variant shows all of them, or the ones it lists.
save_variant Adds or replaces a variant. Every reference is checked before saving.
list_variants Lists the variants after inheritance, with their bullet texts and the bullets no variant uses.
build_cv Renders a PDF and/or DOCX and verifies it.
verify_pdf Runs the ATS checks on any PDF and returns its extracted text.
score_job Measures the keyword coverage of each variant. The client extracts the keywords.
gap_report Groups missing keywords by what you can honestly do about each one.
prompt interview Fills your inventory and bullet pool, one question at a time.
check_claims Checks any text against your profile: blocks X skills and familiarity sold as experience, flags what the profile doesn't show.
build_letter Renders a cover letter to PDF and DOCX, one page, with the CV's ATS checks. Refuses while check_claims finds problems.
prompt tailor_for_job Scores the variants, explains the gaps, adapts a variant and rebuilds.
prompt cover_letter Drafts a letter for a job post from your bullets, checks its claims and builds it.
import_profile Imports JSON Resume or a LinkedIn data export, adding only what is missing. For a LinkedIn PDF it returns the text.
export_json_resume Exports a variant as JSON Resume; X skills never appear and F skills keep their label.
log_application Records an application (company, role, CV variant, files sent, the post's text, what to prepare) or a new status, with its date.
linkedin_profile LinkedIn headline, About, position descriptions and skills (P and L only) from a variant, within LinkedIn's limits.
list_applications Lists applications, latest first, with a count per status and the ones with no news for 14+ days.
prompt import_linkedin_pdf Turns the PDF from LinkedIn's "Save to PDF" into a profile, section by section, with your approval.
prompt linkedin_profile Polishes the LinkedIn texts of a variant without adding claims, checked with check_claims.

ATS guarantees

The template enforces these. verify checks them on every build, and the test suite runs in CI on Linux, macOS and Windows:

  • One column. No layout tables, images, icons, headers or footers.
  • Ligatures and kerning off, no hyphenation, and terms like "CI/CD", URLs and emails never split across lines.
  • Reading order equals source order. Dates sit on the same line as their role.
  • Text is extractable by both pypdf and pdfminer.six, and the two agree.
  • The fonts (Carlito, metric-compatible with Calibri) are embedded and identical on every machine.

Development

uv sync
uv run pytest
uv run honest-cv build examples/ada -l en -l es

The repository's .mcp.json registers honest-cv-dev with the fictional profile, so Claude Code in this folder can use your working copy. See CONTRIBUTING.md.

License

MIT. The bundled Carlito font is under the SIL Open Font License; see src/honest_cv/render/fonts/OFL.txt.

Metadata

Release files for honest-cv 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for honest-cv 0.2.0
File Size Uploaded
honest_cv-0.2.0.tar.gz 682.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for honest-cv 0.2.0
File Interpreter ABI Platform
honest_cv-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.3 MB

Release files / honest_cv-0.2.0.tar.gz

Download URL honest_cv-0.2.0.tar.gz
Size 682.4 kB
Tags Source
SHA-256 checksum
How to use checksums
897ce0d037eb820e06795ffa28967339911b24dbbbd70889d631b02cca2b053f
BLAKE2b-256 checksum
How to use checksums
02c14642d8f0621177cbdeafeb89ee27d1a6f5e8f049fc60203f7c9f79bcbd34
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / honest_cv-0.2.0-py3-none-any.whl

Download URL honest_cv-0.2.0-py3-none-any.whl
Size 647.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
689773231a36b2ad24f6b2f35cb2365b341e337fceafe64e89174833db4dd30d
BLAKE2b-256 checksum
How to use checksums
eef631ccdad6c7be28edcb28d8e6cf28a6646aba09d91e450bbe8c6ce7c77096
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page