# Use Resume Matcher from Claude Code & Claude Desktop (MCP)

> Connect Claude Code, Claude Desktop, Cursor or Codex to Resume Matcher over MCP. Claude writes the resume content; Resume Matcher stores, checks, scores and renders it.
- **Section**: Usage
- **URL**: https://resumematcher.fyi/docs/mcp-server/

---

## What This Is

Resume Matcher ships an [MCP](https://modelcontextprotocol.io) server. It lets Claude Code, Claude Desktop, Cursor, Codex or any other MCP client work with your resumes directly.

The split is simple. Claude (or your client's model) writes the content. Resume Matcher stores it, checks it, scores it and renders the PDF. The server needs no AI API key, because none of its tools calls an AI model. It shares the web app's database, so whatever Claude saves shows up in the app.

## Before You Start

- **A local install.** Follow [Installation](/docs/installation/), then make sure the backend dependencies are synced:

  ```bash
  cd apps/backend
  uv sync
  ```

- **The web app running, for PDF work.** PDF export, `verify_resume`, `tailor_and_verify` and `parse_check_resume` render your resume in a browser, so they need the frontend and backend running as in the installation steps. The other tools work without them.
- **Not Docker.** A [Docker install](/docs/docker-installation/) keeps its database inside the Docker volume, so a server started on your host would see an empty database. Docker installs aren't supported yet.

## Set Up Claude Code

Add the server with one command. Replace the path with the location of your clone:

```bash
claude mcp add resume-matcher -- uv run --directory /abs/path/to/Resume-Matcher/apps/backend resume-matcher-mcp
```

Then ask Claude to call `get_status`. It reports where the data lives and how many resumes and jobs it found.

## Set Up Claude Desktop

Add this to `claude_desktop_config.json`, then restart Claude Desktop:

```json
{
  "mcpServers": {
    "resume-matcher": {
      "command": "/absolute/path/to/uv",
      "args": ["run", "--directory", "/abs/path/to/Resume-Matcher/apps/backend", "resume-matcher-mcp"]
    }
  }
}
```

The file lives here:

- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`

Use the absolute path of `uv` for `command`. Claude Desktop doesn't inherit your shell's `PATH`, so a bare `uv` isn't found. Find the path with `which uv` on macOS or Linux, or `where.exe uv` on Windows. In JSON, double the backslashes in Windows paths.

## Cursor, Codex and Other Clients

They use the same command and arguments:

- **Command:** `uv`
- **Args:** `run --directory <path>/apps/backend resume-matcher-mcp`

## What You Can Ask Claude

You don't call tools yourself. You describe the job and Claude picks the tools. Some examples:

- "Import my resume from ~/Downloads/resume.pdf as a master resume."
- "Import my resume from ~/Downloads/resume.pdf and tailor it to this job: [paste the job description]. Keep it to one page, fix anything the check flags, then export a PDF to my Desktop."
- "Check whether an ATS can read the PDF at ~/Downloads/cv.pdf."
- "Write a cover letter for my latest tailored resume and save it to that resume."

Claude is told to keep your name and contact details, employers, dates and entry counts unchanged, and never to invent metrics or skills. Resume Matcher rejects a tailored save that breaks the preservation rules, and returns grounding warnings for rewrites that stray far from the source.

## The Tools

The server exposes 27 tools, grouped by what they do to your data. Nothing deletes a resume or job.

**Read-only (13).** These never change anything.

| Tool | What it does |
|------|--------------|
| `get_status` | Data location, record counts, templates and limits. Claude calls it first. |
| `get_resume_schema` | The resume JSON schema plus authoring rules. |
| `list_resumes` | Your resumes, newest first. Masters are the tailoring sources. |
| `get_resume` | A resume's data, cover letter and outreach message. |
| `get_job` | A stored job description with its keywords. |
| `get_resume_job` | The job a tailored resume was written for. |
| `extract_document_text` | Reads a local PDF, DOCX, DOC, MD or TXT file into markdown. |
| `validate_tailored_resume` | A dry run of the checks a tailored save must pass. |
| `score_resume` | Keyword match and ATS score for a saved resume or a draft. |
| `parse_check_file` | ATS parse check of a local PDF or DOCX. |
| `parse_check_resume` | ATS parse check of a saved resume as rendered. Needs the app running. |
| `verify_resume` | One render, then page count, parse check, keyword score and issues. Needs the app running. |
| `list_applications` | Tracker cards, optionally for one column. |

**Adds new records, or refreshes a value that can be recomputed (6).**

| Tool | What it does |
|------|--------------|
| `create_master_resume` | Creates a master resume from structured data Claude built. |
| `add_job` | Stores a job description, with the keywords Claude extracted for scoring. |
| `save_tailored_resume` | Saves a tailored resume as a new version. Rejected if it breaks the checks. |
| `tailor_and_verify` | Validates, saves and verifies a tailored resume in one call. |
| `recalculate_ats_score` | Recomputes and stores a tailored resume's ATS score, like the builder's Recalculate button. |
| `create_application` | Adds a tracker card. |

**Overwrites what's stored (8).** These are marked as destructive in MCP, so your client may ask you to approve them.

| Tool | What it does |
|------|--------------|
| `update_resume` | Replaces a resume's structured data. |
| `set_resume_title` | Renames a resume (80 characters at most). |
| `set_cover_letter` | Stores a cover letter, replacing the old one. |
| `set_outreach_message` | Stores an outreach message, replacing the old one. |
| `set_default_master` | Makes a master the default tailoring source. |
| `set_job_keywords` | Attaches keywords to a job, replacing the old ones. |
| `export_resume_pdf` | Renders a saved resume to PDF. Overwrites any file already at that path. Needs the app running. |
| `update_application` | Moves or edits a tracker card. |

Pick the target before an overwrite: look at what's there first. `tailor_and_verify` and `save_tailored_resume` never overwrite anything, because they add a new tailored resume.

## The Typical Flow

Claude's built-in instructions walk it through these steps:

1. **Look around.** `get_status`, then `list_resumes` and `get_resume`. To import a file: `extract_document_text`, `get_resume_schema`, then `create_master_resume`.
2. **Add the job.** `add_job` with the job description and the keywords Claude pulled out of it.
3. **Write the tailored resume** from the source resume.
4. **Tailor and verify.** `tailor_and_verify` validates the draft, saves it, renders it once, parse-checks it and scores it. If `saved` is false, nothing was written: Claude fixes the violations and calls it again.
5. **Fix the issues.** For each code in `issues`, Claude edits with `update_resume` and checks again with `verify_resume`. `recalculate_ats_score` refreshes the score the builder shows after edits.
6. **Export.** `export_resume_pdf` writes the final file. Without an output path it goes to an `exports` folder inside the data folder.

A saved tailored resume appears in the web app like any other. If the job has keywords, the save stores its ATS score, and unless Claude turns it off, it adds a card to the **Applied** column of the application tracker.

If verification fails after the save, the result says `saved: true` with a `verify_error`. Claude shouldn't save again; it calls `verify_resume` instead.

### Issue Codes

`verify_resume` and `tailor_and_verify` list what needs attention in `issues`:

| Code | What it means |
|------|---------------|
| `over_page_limit` | The PDF has more pages than the `max_pages` Claude asked for. |
| `text_layer` | The PDF has too little extractable text, under 50 visible characters. |
| `multi_column` | The parse check found a multi-column layout. |
| `garbled_glyphs` | The extracted text contains garbled characters (the check warned or failed). |
| `low_order_fidelity` | The PDF's reading order scores below 0.9 against your resume's data. |
| `fields_missing` | A field from your resume's data is missing or garbled in the PDF text. |
| `roundtrip_truncated` | The round trip stopped at its limit of 300 fields, so later fields weren't checked. |
| `injectable_keywords` | Job keywords your master resume already supports, but this resume lacks. |
| `render_failed` | The PDF couldn't be rendered. The keyword score is still returned. |

On `multi_column` or `low_order_fidelity`, Claude is told to suggest a single-column template. See [PDF Templates](/docs/pdf-templates/) for the options. The round trip checks Resume Matcher's own render against your data. It isn't a test against a real ATS.

## Limits

- **Five master resumes.** `create_master_resume` fails once you have five. Delete one in the web app first.
- **Imports up to 4 MB.** `extract_document_text` and `parse_check_file` read files up to 4 MB. Import takes PDF, DOCX, DOC, MD and TXT. Parse check takes PDF and DOCX.
- **One database.** The server uses the same database as the web app when both run from the same checkout. `DATA_DIR` moves the SQLite file. `DATABASE_URL` selects PostgreSQL instead, see [Use PostgreSQL Instead of SQLite](/docs/postgresql/).

## Troubleshooting

**Claude Desktop can't start the server or can't find `uv`**

Put the absolute path of `uv` in `command`, as shown above, and restart Claude Desktop. A bare `uv` only works where your shell's `PATH` is inherited.

**PDF tools fail with "PDF rendering failed"**

Start the web app. The message names the frontend address it tried, `http://localhost:3000` by default. If yours runs elsewhere, set `FRONTEND_BASE_URL` in `apps/backend/.env`. Also run the Playwright Chromium step in [Installation](/docs/installation/), unless Chrome or Edge is installed. `verify_resume` and `tailor_and_verify` still return the keyword score. `tailor_and_verify` has already saved the resume, so once the app is up, call `verify_resume` instead of saving again.

**Claude sees no resumes, but the web app has them**

The server is using a different database. Ask Claude to call `get_status`. It reports `data_dir`, the record counts and `backend` (`sqlite` or `postgresql`). If the counts don't match your dashboard, the web app is running from a different checkout, or `DATA_DIR` or `DATABASE_URL` differ between the two. That is the case for Docker installs, since their data sits in the Docker volume.

**"Database is busy. Retry the same call."**

Another write was in progress. Ask Claude to repeat the call.

## Next Steps

- [Installation](/docs/installation/) for the local setup the server needs
- [Features](/docs/features/) for what you can do in the web app with the same resumes
- [Use PostgreSQL Instead of SQLite](/docs/postgresql/) to point the app and the server at a PostgreSQL database