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 follow Your first CV, step by step.

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 first CV, step by step

This walkthrough uses Claude Code; any MCP client works the same way, only the setup differs.

1. Install uv and add honest-cv. One time only. On macOS or Linux:

curl -LsSf https://astral.sh/uv/install.sh | sh        # Windows: see docs.astral.sh/uv
claude mcp add honest-cv -- uvx honest-cv serve --profile ~/cv-profile

~/cv-profile is the folder where your profile will live. It doesn't have to exist yet, and it should not be inside a public repository. Install the skill too (Claude Code above).

2. Start the interview. Open Claude Code in any folder (claude) and write, in your own language, "Interview me for my CV", or run /mcp__honest-cv__interview. The assistant keeps your language for the whole conversation. You can answer by voice with your system's dictation (on macOS: System Settings → Keyboard → Dictation): it reads names and numbers back to you before saving them, since dictation mishears them. It goes like this:

You: Interview me for my CV.

Assistant: The folder ~/cv-profile doesn't exist yet. Shall I create it? … The whole interview can take a few hours, but everything is saved as we go. A first session with your contact details, your two most recent roles and the skills checklist is enough for a first CV. Do you have a LinkedIn data export or a JSON Resume to start from?

  • Import, if you can (optional). Download LinkedIn's data export (see Import) and give the assistant the path to the ZIP. Your roles, education and achievements come in, and skills arrive without a level for you to mark.
  • Contact, roles and education. It asks a few at a time and reads names and numbers back before saving. Then it asks once about certifications, courses and anything you wrote.
  • Achievements, role by role. For each role: what the team did, what you did, and what changed, with numbers. It drafts a bullet and saves it only after you approve the wording.
  • Skills. It offers a checklist, e.g. backend-jvm + engineering-lead, or your own for any profession. Open ~/cv-profile/checklist.md in your editor, put P, L, F or X on each line, add where you used it after " — ", save, and tell the assistant "done". It asks for the evidence it is missing, in groups. Then a form asks you to confirm the upgrades: only what you answer "sí" is saved.
  • Your biggest achievements. One achievement usually covers several skills. It then asks about your main P skills that no achievement shows yet; the rest can wait for a job post.

3. Build your first CV. The assistant proposes one variant per kind of role you apply to (for example "platform" or "engineering manager"): title, summary and which bullets to show. Once you approve it:

You: Build my platform CV in English and Spanish.

The PDF and DOCX land in ~/cv-profile/out/, already checked for ATS safety. Without an AI client, the same is uvx honest-cv build ~/cv-profile -v platform -l en.

4. Tailor it to a job post. Paste the post, or run /mcp__honest-cv__tailor_for_job:

You: Here is a job post: (paste it). How well do I fit, and what should I change?

You get the coverage of each variant, the missing keywords split into you have it but this CV doesn't show it, familiarity only and you don't have it, plus the ones you marked in your checklist but haven't backed with evidence yet (it asks where you used them), a tailored variant to approve, and the build. Then it can draft a cover letter, which check_claims checks before it is built, and log the application with the post's text and what to prepare for the interview. When you get the interview, "help me prepare the interview" drafts likely questions with answers from your own achievements.

5. Come back any day. Say "let's continue my CV interview": it starts from what is missing. When you need to update your LinkedIn profile, say "update my LinkedIn".

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.

Checklists come in English and Spanish (--lang es): the Spanish names are synonyms of the English ones, so a skill you mark in Spanish still matches a job post in English.

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 cover software (backend, frontend, mobile, data, platform/SRE, QA, security, ML/AI, embedded, game development, architecture, engineering leadership) and data analysis/BI, product and project management, UX design, marketing, sales, customer support, finance and accounting, human resources, supply chain, nursing and teaching. 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/event-planning.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.
  • Running it on a hosted service takes it off your machine. Directories such as Glama offer to deploy honest-cv on their servers or try it in the browser. It works, but then your profile and your CVs live on that service, under its terms. For your real data, run it on your own computer with uvx honest-cv.
  • 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 keeps a copy of each file you sent in sent/, since the next build overwrites out/, and 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?".

When you get the interview, say "help me prepare the Acme interview" (or run the interview_prep prompt). For each keyword of the saved post, honest-cv lays out your level, your evidence and the bullets that back it, starting with the ones in the CV you sent. Your assistant drafts the likely questions with STAR answers taken only from those bullets, honest answers for what you only know at familiarity level or don't have, and questions to ask them. The notes you approve are saved in prep/. Without an AI client: honest-cv apps prep ~/cv-profile acme_sre -k "Kafka,Scala".

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 apps prep ~/cv-profile acme_sre -k "Kafka,Scala"           # what backs each keyword, for the interview
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.
interview_prep For an application: the saved post and, per keyword, your level, evidence and the bullets that back it. Saves the prep notes.
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.
prompt interview_prep Prepares an interview: likely questions, STAR answers from your own bullets, honest answers for the gaps.

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.3.2

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.3.2
File Size Uploaded Provenance
honest_cv-0.3.2.tar.gz 721.1 kB With provenance Details

Built distribution (wheel)

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

Total release size: 1.4 MB

Release files / honest_cv-0.3.2.tar.gz

Download URL honest_cv-0.3.2.tar.gz
Size 721.1 kB
Tags Source
SHA-256 checksum
How to use checksums
710f8256634ab422c1873d93abc7befb60f8203d6df9794e7bc681f7789c5aa2
BLAKE2b-256 checksum
How to use checksums
3b5e1827b8405dd5be5cd0f00e6cbb1d3d605e8e121e6defca1e4d10f6a1ba32
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 16, 2026.

Transparency log

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

Download URL honest_cv-0.3.2-py3-none-any.whl
Size 696.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
67493090c1d4b9776fd0a66ac131d87e17aba9380d38af3ba439fc300f73b16d
BLAKE2b-256 checksum
How to use checksums
f97290353d12f27ac77a4a77dab7e200c5a5337530bb80d3848cf9bb6164e0b5
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 16, 2026.

Transparency log

Security for honest-cv 0.3.2

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for honest_cv-0.3.2.tar.gz

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 16, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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