New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

@phuthuycoding/markcv

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@phuthuycoding/markcv

Markdown resume builder that lints your CV and explains why it will not fit two pages. ATS-safe PDF, job-description tailoring, CLI + MCP server for AI agents.

latest
Source
npmnpm
Version
0.2.0
Version published
Weekly downloads
3
-70%
Maintainers
1
Weekly downloads
 
Created
Source

markcv

CI npm docs

Two-page ATS-safe resume PDF generated from Markdown by markcv
examples/engineering-lead.md — plain Markdown in, two-page A4 PDF out

Markdown resume builder that does two things other markdown-to-PDF tools do not: it tells you why your CV will not fit on two pages, and it lints the writing itself. Runs as a CLI and as an MCP server, so an AI agent can do both on its own.

  • fit tells you why your CV does not fit on 2 pages. "Content too long" and "bad page break" are different illnesses with opposite cures — trimming words while the real culprit is a heading sitting 31px from the bottom of page 1 just wastes your time.
  • lint checks content, not formatting. It catches overselling and underselling — claiming less than you did is a mistake too, and it costs you something while gaining nothing.

Install

npm install -g @phuthuycoding/markcv

The package is scoped; the commands are not. You type markcv and markcv-mcp.

Or run it without installing:

npx @phuthuycoding/markcv fit cv.md --pages 2

Requires a Chromium-based browser already on your machine (Chrome, Chromium, Edge, Brave). markcv deliberately does not download its own Chromium — it uses puppeteer-core, so the install stays small. If your browser lives somewhere unusual, point at it with MARKCV_CHROME=/path/to/chrome.

Usage

markcv render cv.md -o Output.pdf     # build the PDF, report the page count
markcv fit cv.md --pages 2            # why it does not fit yet
markcv lint cv.md                     # audit the content
markcv tailor cv.md --jd jd.txt       # compare against a job description
markcv new techlead --from cv-master.md
markcv list                           # every CV in the folder
markcv diff cv-a.md cv-b.md           # what a tailored copy dropped
markcv build --pages 2                # build every cv-*.md
markcv skills install                 # install the agent skills (see below)

fit — page-break diagnosis

✗ 3 pages (target 2)
· content 1986px / 2080px available
· 31px wasted by page breaks

Blocks pushed to a new page:
  PROJECT HIGHLIGHTS (H2, y=1009) → wastes 31px at the end of page 1

Suggestions:
  Content HAS ROOM (94px to spare) — length is not the problem, the page break is.
  "PROJECT HIGHLIGHTS" at y=1009 has only 31px left before the end of page 1, so the whole block moved down.
  Fix: reorder sections, cut ~3 lines above it, or use --theme compact.

lint — rules

RuleWhat it catches
over-claimspearheaded, rigorous, comprehensive, excellence… — self-praise nobody can verify
under-claimadvised on / worked on next to real scale — you are probably selling yourself short
tensea finished job still described in the present tense
unsupported-skilla skill listed under SKILLS with no experience line backing it
duplicatetwo bullets saying the same thing
role-mismatchan IC job title paired with people-management language
no-metrica long bullet with no number in it
ats-emojiemoji in a heading — risky for ATS parsers
long-bulleta bullet long enough that skimmers will skip it

Examples

Three complete CVs in examples/, written for different roles so you can see how the same format stretches. Each one builds with the default theme and no photo.

Backend engineer resume built from MarkdownEngineering lead resume, two pages, ATS-safeData scientist resume, one page
backend-engineer.md
1 page
engineering-lead.md
2 pages
data-scientist.md
1 page

With a portrait photo

Markets differ: a CV in Berlin or Toronto normally carries no photo, while one in Vietnam, Japan or Germany's more traditional employers usually does. Drop a photo.jpg next to the markdown file and it lands in the top-right corner.

Markdown resume with a portrait photo in the header
examples/with-photo/fullstack-engineer.md — one page, photo auto-detected

The photo lives in its own folder because detection is per-directory: any .md file next to a photo.* picks it up. Keep photo-less CVs in a separate folder, or pass --no-photo.

Build them yourself:

markcv build examples --pages 2
markcv render examples/with-photo/fullstack-engineer.md --pages 1

Note what the bullets in those samples have in common: a number, or a before and after. lint exists to push a CV in that direction — the samples are what it is aiming at, and test/fixtures/bad-cv.md is what it is aiming away from.

MCP server

Lets an AI agent (Claude Code, Claude Desktop, Cursor…) build and audit CVs on its own.

Nothing to install: npx fetches the package on first use and caches it.

Register it with a client

Claude Code — add to .mcp.json in your project (shared with the team), or ~/.claude.json (just you):

{
  "mcpServers": {
    "markcv": {
      "command": "npx",
      "args": ["-y", "@phuthuycoding/markcv", "mcp"],
      "cwd": "/path/to/your/cv/folder"
    }
  }
}

Or from the command line:

claude mcp add markcv -- npx -y @phuthuycoding/markcv mcp
claude mcp list          # confirm it connected

Restart the client afterwards so it picks the server up.

If you installed the package globally, "command": "markcv-mcp" with no args works too and starts marginally faster. The repo ships .mcp.json.example with both shapes.

Claude Desktop — ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows), same shape, then restart the app.

Cursor — .cursor/mcp.json in the project, same shape.

File paths in tool arguments

Every tool takes a file path, and relative paths resolve against the server process's working directory — hence the cwd above. Without it, pass absolute paths instead.

Tools

ToolPurpose
render_cvbuild the PDF, return the real page count plus layout numbers
check_fitwhy it does not fit in N pages: too long, or bad page breaks
lint_cvaudit content (overselling, underselling, tense, unbacked claims…)
tailor_to_jdcompare against a job description (jd_file or jd_text)
list_variantslist every CV in a folder
new_variantstart a tailored copy from a master file
diff_variantscompare two versions, see what a tailored copy dropped

Every tool returns structured JSON, not prose — so an agent can loop on it: edit the markdown → check_fit → read slackPx and culprits → edit again, until it fits.

Requirements

Node >= 18 and a Chromium-based browser (only render and check_fit need it). If it is in a non-standard location:

{ "mcpServers": { "markcv": { "command": "node", "args": ["..."],
  "env": { "MARKCV_CHROME": "/path/to/chrome" } } } }

Agent skills

The MCP server gives an agent the tools. Skills give it the judgement — when a page-break diagnosis means "reorder", when it means "cut", and what a bullet should sound like. They are plain Markdown files an agent reads on demand.

markcv skills               # what ships with the package
markcv skills install       # copy into ~/.claude/skills
markcv skills install topcv # just one
SkillWhat it covers
markcvWriting and auditing a CV with markcv: reading a fit report correctly, the lint rules, bullet style, keeping several variants in sync.
topcvPublishing a Markdown CV to TopCV.vn (Vietnamese job market) — both the CV builder and the profile page.

Existing skills are never overwritten, so your own edits survive; pass --force when you do want the bundled version back. Restart the agent afterwards so it sees them.

The topcv skill drives a real browser and additionally needs the chrome-devtools MCP server:

{
  "mcpServers": {
    "chrome-devtools": {
      "command": "npx",
      "args": ["-y", "chrome-devtools-mcp@latest", "--autoConnect"]
    }
  }
}
claude mcp add chrome-devtools -- npx -y chrome-devtools-mcp@latest --autoConnect

CV format

Plain Markdown. The only convention lives in the header:

# Your Name

**Email:** you@example.com
**Phone:** +84 9xx xxx xxx

***

## OBJECTIVE
...

## WORK EXPERIENCE

### Company Name

**Job Title** | Jan 2020 - Dec 2023

* Bullet...

**Label:** value lines directly under # Your Name become the contact block. A line containing | is read as job title + dates — and lint uses those dates to know whether a job has ended.

Portrait photo: drop photo.jpg (or photo.png, avatar.jpg) next to the .md file and it is embedded in the top-right corner. Without one you get an empty placeholder box. Photos over 400KB trigger a warning, because they push the PDF past the upload limit many job portals enforce.

Skip a file in markcv build: put <!-- markcv:no-build --> near the top. Useful for a master file that is a content store rather than something you submit.

Themes

classic (default) and compact. Both are single-column, emoji-free, with a real text layer — safe for ATS parsers.

How this differs from other Markdown resume tools

Rendering Markdown into a good-looking CV is well covered — @resumx/resumx, markdown-resume and others do it well, and if that is all you need, use one of them.

markcv exists for the part that comes after the rendering works:

Typical Markdown→PDF toolmarkcv
Render to PDFyesyes
Themesyestwo, ATS-safe
Why it does not fit N pages—names the block, the wasted pixels, and whether length or a page break is at fault
Content audit—overselling, underselling, tense, unbacked skills, duplicates
Job-description match—missing requirements, SKILLS-only claims, unrelated bullets
Usable by an AI agent—MCP server, structured JSON

Author

Built by Ta Manh Quyen — a staff engineer who got tired of guessing why a CV spilled onto a third page. More writing at quyentm.dev.

License

MIT

Keywords

resume

FAQs

Package last updated on 09 Sep 2026

Related posts