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

Fit, lint and build a CV from Markdown - CLI + MCP server for AI agents

Source
npmnpm
Version
0.1.0
Version published
Weekly downloads
0
Maintainers
1
Weekly downloads
 
Created
Source

markcv

CI npm

A CV rendered by markcv
examples/engineering-lead.md — plain Markdown in, two-page A4 PDF out

Build a CV from Markdown — a CLI and an MCP server for AI agents.

Two things set it apart from ordinary markdown→PDF tools:

  • 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

git clone https://github.com/phuthuycoding/markcv.git
cd markcv
npm install          # builds dist/ via the prepare script
npm link             # puts `markcv` and `markcv-mcp` on your PATH

Or from npm:

npm install -g @phuthuycoding/markcv

The package is scoped, but the commands are not — you still type markcv and markcv-mcp.

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

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 CVEngineering lead CVData scientist CV
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.

CV with a portrait photo
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.

Install it first (see Install above), then point a client at markcv-mcp.

Check the server starts (it waits for JSON-RPC on stdin and prints nothing — that is correct; Ctrl+C to quit):

markcv-mcp

Register it with a client

After npm link the command is simply markcv-mcp. Without it, use an absolute path to dist/mcp/server.js. The repo ships .mcp.json.example to copy from.

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

{
  "mcpServers": {
    "markcv": {
      "command": "markcv-mcp",
      "cwd": "/path/to/your/cv/folder"
    }
  }
}

Or add it from the command line:

claude mcp add markcv -- markcv-mcp
claude mcp list          # confirm it connected

Restart the client afterwards so it picks the server up.

Claude Desktop — ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows), same mcpServers 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. Relative paths resolve against the server process's working directory, so either pass absolute paths or give the server a cwd:

{
  "mcpServers": {
    "markcv": {
      "command": "node",
      "args": ["/path/to/markcv/dist/mcp/server.js"],
      "cwd": "/path/to/your/cv/folder"
    }
  }
}

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. If it is in a non-standard location:

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

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.

License

MIT

Keywords

cv

FAQs

Package last updated on 06 Sep 2026

Related posts