Skip to main content
Glama
README.md
# resume-pdf-mcp

Production-grade **Model Context Protocol (MCP)** server that compiles LaTeX resumes into PDFs.

**GitHub:** [https://github.com/shashimehta03/mcp-resume](https://github.com/shashimehta03/mcp-resume)

Works with **Claude Desktop**, **Claude Code**, **Cursor**, VS Code, and any MCP-compatible client.

---

## What it does

Exposes MCP tools so your AI client can:

| Tool | Purpose |
|---|---|
| `compile_resume` | Compile LaTeX → PDF (XeLaTeX or pdfLaTeX) |
| `validate_resume` | Check LaTeX before compile (structure + safety) |
| `preview_resume` | Page count, PDF size, compile duration |
| `export_tex` | Save `.tex` into the output folder |
| `list_templates` | List built-in resume templates |
| `compile_template` | Fill a template with JSON variables → PDF |
| `version` | Server / engine / build info |

Security defaults: filename sanitization, path isolation, no shell interpolation, `-no-shell-escape`, size/timeout limits, stderr-only logging (LaTeX source is never logged in full).

---

## Complete setup checklist (follow in order)

Do every step once after cloning. Details for each step are in the sections below.

1. **Install Node.js 20+** and a LaTeX engine (`pdflatex` and/or `xelatex`) — see [Requirements](#requirements)
2. **Clone the repo** and run `npm install` then `npm run build` — see [Clone and install](#clone-and-install-do-this-first)
3. **Confirm build output exists:** `dist/index.js` and `dist/templates/` must be present
4. **Note your absolute paths:** Node binary + clone folder — see [What you must change after cloning](#what-you-must-change-after-cloning)
5. **Configure your MCP host** (pick one):
   - Claude Desktop → [Configure Claude Desktop](#configure-claude-desktop)
   - Cursor → [Configure Cursor](#configure-cursor)
   - Claude Code → [Configure Claude Code](#configure-claude-code)
6. **Replace every example path** in the config with *your* paths (Windows: use `\\` in JSON)
7. **Do not leave `npm start` running** — Claude/Cursor start the server themselves
8. **Fully quit and reopen** the host app (Claude: tray → Quit, not just close the window)
9. **Verify the server is running** (Claude: Settings → Developer → `resume-pdf` = **running**)
10. **Use Chat mode** (Claude Desktop) — Cowork/Code may ignore this MCP server
11. **Smoke-test in chat:** ask Claude/Cursor to call the `version` tool
12. **Optional:** run `npm run test:tools` locally to confirm TeX works without an MCP host

After you change code later: `npm run build` → quit/reopen the MCP host again.

---

## Requirements

1. **Node.js 20+** (22 LTS recommended)
2. **A LaTeX engine on PATH**
   - Windows: [MiKTeX](https://miktex.org/) or TeX Live (`pdflatex` / `xelatex`)
   - macOS: `brew install --cask mactex-no-gui` (or BasicTeX)
   - Linux: `texlive-xetex` / `texlive-latex-recommended`

Verify:

```bash
node -v
pdflatex --version
# and/or
xelatex --version
```

---

## Clone and install (do this first)

```bash
git clone https://github.com/shashimehta03/mcp-resume.git
cd mcp-resume
npm install
npm run build
```

Confirm the build:

```bash
# Windows PowerShell
Test-Path .\dist\index.js
Test-Path .\dist\templates\index.json

# macOS / Linux
ls dist/index.js dist/templates/index.json
```

Optional local env file (MCP hosts do **not** auto-load `.env` — still set `env` in their JSON):

```bash
cp .env.example .env
```

You do **not** need to keep `npm start` running for Claude/Cursor. Those apps **spawn** the server themselves. Only run `npm start` / `npm run test:tools` for local smoke tests.

---

## What you must change after cloning

Repo examples keep sample paths on purpose. **After clone, change them to your machine.**

| What | Default in repo / examples | Change to |
|---|---|---|
| Project folder | `E:\\mcp_resume` / `/Users/YOU/mcp-resume` | Where you cloned the repo |
| `args` entrypoint | `.../dist/index.js` | `<your-clone>/dist/index.js` (after `npm run build`) |
| `OUTPUT_DIR` | `./output` or example absolute path | `<your-clone>/output` (use absolute in MCP hosts) |
| `TEMP_DIR` | `./temp` | `<your-clone>/temp` |
| `TEMPLATES_DIR` | `dist/templates` | `<your-clone>/dist/templates` (or leave unset) |
| `command` (Node) | `node` or `C:\\Program Files\\nodejs\\node.exe` | Full path to `node` (Claude Desktop usually needs the full path) |
| `DEFAULT_ENGINE` | `xelatex` in `.env.example`, `pdflatex` in some examples | Whichever engine you installed |
| `cwd` (optional) | project root | Same as your clone path |

More notes: [`examples/README.md`](examples/README.md).

### Find your paths

**Windows (PowerShell):**

```powershell
# Node
(Get-Command node).Source

# Project (run from inside the clone)
(Get-Location).Path

# Real Claude config path on Microsoft Store / MSIX installs
$pkg = (Get-AppxPackage -Name "*Claude*").PackageFamilyName
Join-Path $env:LOCALAPPDATA "Packages\$pkg\LocalCache\Roaming\Claude\claude_desktop_config.json"
```

**macOS / Linux:**

```bash
which node
pwd   # from inside the clone
# Config is usually:
# ~/Library/Application Support/Claude/claude_desktop_config.json   (macOS)
```

**Windows JSON tip:** every backslash in a path must be doubled: `E:\\mcp_resume\\dist\\index.js`.

---

## Configure Claude Desktop

### 1) Build the project

```bash
npm install
npm run build
```

### 2) Open the config Claude actually reads

In Claude Desktop:

**Settings → Desktop app → Developer → Edit Config**

That opens `claude_desktop_config.json`.

#### Windows Microsoft Store / MSIX builds (important)

“Edit Config” may open:

`%APPDATA%\Claude\claude_desktop_config.json`

…but the app often **reads**:

`%LOCALAPPDATA%\Packages\Claude_*\LocalCache\Roaming\Claude\claude_desktop_config.json`

Edit the **LocalCache** file (or copy your finished config into **both**).  
If you only edit the `%APPDATA%` file, Claude may ignore your server with no error.  
Details: [MSIX config path issue](https://github.com/anthropics/claude-code/issues/26073).

### 3) Add the server (merge into existing JSON)

If the file already has `preferences` / `coworkUserFilesPath`, **keep them**. Only add or update the top-level `mcpServers` key (do **not** paste a second `{ ... }` object).

Valid shape (copy, then replace paths):

```json
{
  "mcpServers": {
    "resume-pdf": {
      "command": "C:\\Program Files\\nodejs\\node.exe",
      "args": ["E:\\mcp_resume\\dist\\index.js"],
      "cwd": "E:\\mcp_resume",
      "env": {
        "OUTPUT_DIR": "E:\\mcp_resume\\output",
        "TEMP_DIR": "E:\\mcp_resume\\temp",
        "TEMPLATES_DIR": "E:\\mcp_resume\\dist\\templates",
        "DEFAULT_ENGINE": "pdflatex",
        "LOG_LEVEL": "info",
        "NODE_ENV": "production"
      }
    }
  }
}
```

What to change in that block (defaults left above on purpose):

| Key | Change? |
|---|---|
| `command` | **Yes** → your `node.exe` / `node` absolute path |
| `args[0]` | **Yes** → your clone’s `dist/index.js` |
| `cwd` | **Yes** → your clone folder |
| `OUTPUT_DIR` / `TEMP_DIR` / `TEMPLATES_DIR` | **Yes** → folders under your clone |
| `DEFAULT_ENGINE` | If needed → `pdflatex` or `xelatex` |
| `LOG_LEVEL` / `NODE_ENV` | Optional |

**macOS / Linux example** (same keys — only paths change):

```json
{
  "mcpServers": {
    "resume-pdf": {
      "command": "/usr/local/bin/node",
      "args": ["/Users/YOU/mcp-resume/dist/index.js"],
      "cwd": "/Users/YOU/mcp-resume",
      "env": {
        "OUTPUT_DIR": "/Users/YOU/mcp-resume/output",
        "TEMP_DIR": "/Users/YOU/mcp-resume/temp",
        "TEMPLATES_DIR": "/Users/YOU/mcp-resume/dist/templates",
        "DEFAULT_ENGINE": "xelatex",
        "LOG_LEVEL": "info"
      }
    }
  }
}
```

Repo template: [`examples/claude-desktop-config.json`](examples/claude-desktop-config.json).

### 4) Fully quit and reopen Claude

Closing the window is not enough. Use the tray / menu bar → **Quit**, confirm no `Claude` process is left, then start Claude again.  
MCP servers are loaded **only at startup**.

### 5) Verify

1. **Settings → Developer → Local MCP servers**
2. Select `resume-pdf`
3. Status should be **running** (not failed / disconnected)
4. If failed: open **View Logs** on that server
5. Start a **new Chat** (not Cowork, not Code)
6. Ask:

```text
Call the resume-pdf version tool and show the result.
```

To steer Claude every time:

```text
Always use resume-pdf MCP tools (compile_resume, validate_resume, compile_template)
for any resume/LaTeX/PDF work. Do not compile LaTeX yourself in the sandbox.
```

PDFs land in your `OUTPUT_DIR` (for example `<clone>/output`).

### Optional: install as a Claude Desktop Extension

If the JSON `mcpServers` entry does not stay connected on your Claude build:

```bash
npm run build
npm run install:claude
```

Then fully quit/reopen Claude and check **Settings → Developer / Extensions** for **Resume PDF** (enabled).

Before relying on that helper on another machine, point these at **your** clone (repo defaults are examples — leave them until you change them):

- `mcpb/manifest.json` → `user_config.*.default`
- `mcpb/server/index.js` → fallback `RESUME_PDF_MCP_ROOT`

---

## Configure Cursor

1. Open Cursor MCP settings (or project `.cursor/mcp.json` / global MCP config, depending on your Cursor version).
2. Add a server entry like:

```jsonc
{
  "mcpServers": {
    "resume-pdf": {
      // CHANGE: "node" is usually fine in Cursor if Node is on PATH
      "command": "node",

      // CHANGE: absolute path to YOUR clone
      "args": ["E:/mcp_resume/dist/index.js"],

      "env": {
        // CHANGE these to YOUR clone paths
        "OUTPUT_DIR": "E:/mcp_resume/output",
        "TEMP_DIR": "E:/mcp_resume/temp",
        "DEFAULT_ENGINE": "pdflatex"
      }
    }
  }
}
```

See also [`examples/cursor-mcp.json`](examples/cursor-mcp.json).

Restart Cursor / reload MCP servers after saving.

---

## Configure Claude Code

Add to your Claude Code MCP config (see [`examples/claude-code-mcp.json`](examples/claude-code-mcp.json)):

```jsonc
{
  "mcpServers": {
    "resume-pdf": {
      "command": "node",
      // CHANGE: YOUR clone's dist entry
      "args": ["./dist/index.js"],
      // CHANGE: YOUR clone directory
      "cwd": "/absolute/path/to/mcp-resume",
      "env": {
        "DEFAULT_ENGINE": "xelatex",
        "OUTPUT_DIR": "./output",
        "TEMP_DIR": "./temp"
      }
    }
  }
}
```

Or from the project folder:

```bash
claude mcp add resume-pdf -- node ./dist/index.js
```

(Adjust to your Claude Code version’s exact `mcp add` syntax.)

---

## Environment variables

Copy [`.env.example`](.env.example). Values below are defaults — change only if you need to.

| Variable | Default | Notes |
|---|---|---|
| `OUTPUT_DIR` | `./output` | Prefer absolute path in MCP host `env` |
| `TEMP_DIR` | `./temp` | Per-request workspaces |
| `DEFAULT_ENGINE` | `xelatex` | Use `pdflatex` if XeLaTeX isn’t installed |
| `MAX_LATEX_SIZE` | `512000` | Max LaTeX source bytes |
| `COMPILE_TIMEOUT` | `60000` | Compile timeout (ms) |
| `MAX_MEMORY_MB` | `1024` | Soft memory guidance |
| `LOG_LEVEL` | `info` | `silent` \| `error` \| `warn` \| `info` \| `debug` |
| `TEMPLATES_DIR` | `dist/templates` (runtime) | Override if you move templates |
| `KEEP_TEMP_ON_FAILURE` | `false` | Set `true` to debug failed compiles |

MCP hosts pass these via the `env` block in their JSON config (they do not automatically load `.env`).

---

## How to use (from the AI chat)

Once the server is **running** in the client:

### Compile raw LaTeX

```text
Use compile_resume with engine pdflatex and this LaTeX:

\documentclass{article}
\begin{document}
Hello from resume-pdf-mcp
\end{document}
```

### Validate first

```text
Use validate_resume on this LaTeX before compiling: ...
```

### Use a template

```text
Use list_templates, then compile_template with template_name "modern-simple"
and variables for name, email, summary, skills, experience, education.
```

Example payload shape: [`examples/sample-template-request.json`](examples/sample-template-request.json).

### Expected success response

```json
{
  "success": true,
  "pdf_path": ".../output/resume.pdf",
  "tex_path": ".../output/resume.tex",
  "compilation_logs": "...",
  "execution_time": 0.82
}
```

---

## Local testing (without Claude)

```bash
npm test                 # unit + integration
npm run test:coverage
npm run test:tools       # hits all tools; watch USAGE logs on stderr
npm run test:smoke       # compile minimal-ats template once
```

---

## Docker

```bash
docker compose build
docker compose run --rm resume-pdf-mcp
```

Image includes TeX Live. See [docs/docker.md](docs/docker.md).

For Claude Desktop via Docker, point `command` at `docker` and pass your compose/run args (stdio must stay attached).

---

## Project layout

```
src/          TypeScript source (tools, services, latex, config)
dist/         Build output (what MCP hosts should run)
mcpb/         Claude Desktop extension manifest + launcher
examples/     Sample MCP configs and requests
docs/         Installation, usage, architecture, contributing
tests/        Vitest suite
output/       Generated PDFs/TeX (gitignored contents)
temp/         Compile workspaces (gitignored contents)
```

Architecture:

```
Tool → ResumeService → LatexCompiler → FileSystemService → XeLaTeX/pdfLaTeX
```

---

## Troubleshooting

| Problem | Fix |
|---|---|
| Tool not in Claude | Use **Chat** mode; fully Quit/reopen; confirm Developer shows **running** |
| Config saved but ignored (Windows) | Edit the MSIX `LocalCache\Roaming\Claude\claude_desktop_config.json` path |
| `ENGINE_NOT_FOUND` | Install MiKTeX/TeX Live; put `pdflatex`/`xelatex` on PATH; set `DEFAULT_ENGINE` |
| `Server disconnected` | Rebuild (`npm run build`); use full `node` path; set `cwd`; Quit Claude and reopen |
| Claude compiles in sandbox instead | Server not connected — fix Developer status; instruct Claude to use `compile_resume` |
| Wrong output folder | Set absolute `OUTPUT_DIR` in the MCP `env` block |

Logs (Windows MSIX):

`%LOCALAPPDATA%\Packages\Claude_*\LocalCache\Roaming\Claude\logs\mcp-server-resume-pdf.log`

---

## Documentation

- [Installation](docs/installation.md)
- [Usage](docs/usage.md)
- [Docker](docs/docker.md)
- [Development](docs/development.md)
- [Architecture](docs/architecture.md)
- [Contributing](docs/contributing.md)

---

## Development scripts

```bash
npm run dev            # tsx stdio server
npm run build
npm start
npm run lint
npm run typecheck
npm run install:claude # install local Claude Desktop extension helper
```

---

## License

MIT — see [LICENSE](LICENSE).

---

## Links

- Repository: [https://github.com/shashimehta03/mcp-resume](https://github.com/shashimehta03/mcp-resume)
- Issues: [https://github.com/shashimehta03/mcp-resume/issues](https://github.com/shashimehta03/mcp-resume/issues)

TDQS

B3.3/5.0

Scored across 7 tools

Disambiguation4/5

Most tools are clearly distinct: compilation, validation, preview, export, templates, and version all target different operations. The main confusion is between compile_template and compile_resume—one renders a named template while the other compiles raw LaTeX source, but the distinction (template vs source) is reasonably clear from names.

Naming Consistency4/5

Tools consistently use a verb_noun pattern throughout (compile_template, compile_resume, preview_resume, export_tex, list_templates, validate_resume). The pattern is mostly regular, though having both compile_template and compile_resume with the same 'compile' verb is a minor deviation from strict one-verb-per-action.

Tool Count5/5

Seven tools is well within the ideal 3-15 range for a resume/pdf generation server. Each tool has a distinct role in the workflow: template management, compilation, validation, preview, export, and metadata.

Completeness3/5

Core workflows are covered: templates can be listed and compiled, source can be validated, compiled, previewed, and exported. However, there's no obvious update/delete operation for templates, and no tool to manage or inspect output artifacts beyond preview metadata, leaving some minor lifecycle gaps.

Maintenance

ActivitySlowing
ResponsivenessNo issues