# MCP server setup

Install `@fenfill/mcp` in Claude Code, Claude Desktop, Cursor or any client that launches a stdio server.

## Before you start

- Node.js 22 or newer. `npx` comes with it.
- A fenfill API key (`ff_live_…`): fenfill → Settings → API keys. The API is included on Pro, Team and Business: see [pricing](https://fenfill.com/pricing).

> **Privacy note:** Fills happen on your machine; only blank forms and templates go to fenfill. The agent's answers are stamped into the PDF locally and are never sent to fenfill or anywhere else. See [what is sent where](https://fenfill.com/docs/data-flow).

## Add the server to your client

**Claude Code**: Run this in a terminal.

```sh
claude mcp add fenfill --scope user -e FENFILL_API_KEY=ff_live_… -- npx -y @fenfill/mcp
```

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

```json
{
  "mcpServers": {
    "fenfill": {
      "command": "npx",
      "args": ["-y", "@fenfill/mcp"],
      "env": { "FENFILL_API_KEY": "ff_live_…" }
    }
  }
}
```

**Cursor**: Add this to ~/.cursor/mcp.json (all projects) or .cursor/mcp.json (one project).

```json
{
  "mcpServers": {
    "fenfill": {
      "command": "npx",
      "args": ["-y", "@fenfill/mcp"],
      "env": { "FENFILL_API_KEY": "ff_live_…" }
    }
  }
}
```

**Other**: Any client that launches a stdio server: run this command with FENFILL_API_KEY in its environment.

```sh
FENFILL_API_KEY=ff_live_… npx -y @fenfill/mcp
```

## Client notes

- **Claude Desktop** reads `claude_desktop_config.json` from `~/Library/Application Support/Claude/` on macOS and `%APPDATA%\Claude\` on Windows. Restart Claude Desktop after editing it.
- **Native Windows:** if the client can't find `npx`, use `"command": "cmd"` with `"args": ["/c", "npx", "-y", "@fenfill/mcp"]`.
- The installed command is `fenfill-mcp`.

This page describes version 0.2.6. To update, restart your client. If it still runs an older version, use `@fenfill/mcp@latest` in the command. See the [changelog](https://fenfill.com/docs/changelog) for what changed.

## Configuration

The server reads these environment variables, set in the client's config next to the command.

| Variable | Default | What it does |
| --- | --- | --- |
| `FENFILL_API_KEY` | (none) | Your workspace API key (`ff_live_…`), from fenfill → Settings → API keys. The tools that call fenfill need it; `fill_form` works without it once a form has been analyzed. |
| `FENFILL_API_URL` | `https://api.fenfill.com` | The API origin. https only (http is allowed for localhost). |
| `FENFILL_CACHE_DIR` | the OS cache folder | Where the server keeps form schemas and blank forms. Never answers. |
| `FENFILL_ANALYZE_WAIT_SECONDS` | `50` | How many seconds `analyze_form` and `reanalyze_template` wait for a result before they answer `"running"`. Calling again with the same arguments picks up the same run. |

## The local cache

The cache (`FENFILL_CACHE_DIR`) is private to your user. By default it is `~/Library/Caches/fenfill-mcp` on macOS, `~/.cache/fenfill-mcp` on Linux and `%LOCALAPPDATA%\fenfill-mcp\Cache` on Windows.

- It holds form schemas, blank template PDFs, the workspace logo, sha256 hashes of the PDFs the server produced and `edit_template`'s working copies, **never the answers you fill in**.
- At startup, and at most once a day, it deletes blank PDFs and the logo after 1 hour, analyses and working copies unused for 90 days, stale job records after 48 hours, and output hashes after a year.
- `fonts/` holds the Japanese font (Noto Sans JP, about 5.8 MB), downloaded the first time a value needs it and never aged out.
- Filled PDFs are written only where you ask, readable by your user only (mode 0600).
- Logs go to stderr and contain tool names, timings and error codes, never field values.

To work offline with Japanese values, copy `NotoSansJP-Regular.ttf` (from `https://fenfill.com/fonts/NotoSansJP-Regular.ttf`) into `<cache>/fonts/` (`FENFILL_CACHE_DIR` if you set it). It is used if its sha256 matches the pinned value.

## Troubleshooting

Errors come back as a code with a message. The common ones:

| Error | What to do |
| --- | --- |
| `missing_api_key` / `invalid_api_key` | Set `FENFILL_API_KEY` in the client's config and restart the client. |
| `plan_required` | Your plan doesn't include API access: see [pricing](https://fenfill.com/pricing). |
| `insufficient_scans` | Not enough page scans left. Analyze fewer `pages`, or check `get_account`. |
| `prefilled_form` / `filled_output` | Only blank forms can be analyzed: use the original, empty PDF. If it is the blank form (some ship with default text in their fields), use the fenfill web app. When the refusal names annotations or XFA values that belong to the blank form, pass `confirm_blank: true`. |
| `pdf_password_required` | Remove the open password first. PDFs with only an owner password work as they are. |
| `pdf_fill_forbidden` | The PDF's author forbids form filling, so fenfill won't fill it. Use a copy that allows filling. |
| `unsupported_script` | A value needs Japanese characters and the font could not be loaded. `ids` lists the fields and `details.reason` says why (`network`, `http_status`, `hash_mismatch`, `timeout`, `too_large`). Connect once and retry, or pre-seed the font (see [the local cache](#cache)). |
| `not_analyzed` | Run `analyze_form` on the same file (and pages) before `fill_form`. |
| `output_exists` | Pick a new `output_path`, or pass `overwrite: true`. |
| `edit_conflict` | Another fenfill-mcp process changed the form's local edits during the call, so nothing was applied. Call `edit_template` again with the same ops. |
| `rate_limited` / `queue_full` | `analyze_form` already waited about a minute (`waited_s`). Try again later. |
| `too_many_active_jobs` | Other analyses in the workspace (native-field ones too) are still running. Try again in a minute, or analyze fewer at once. |
| `template_conflict` | The template changed in fenfill since your edits began. Your local edits are kept: call `edit_template` with `discard: true`, re-apply the ops and save again. |
| `invalid_schema` | fenfill (or the local check before it) refused the edited layout. `details` lists each item and reason; fix them with `edit_template`. |
| `check_copy_failed` | `preview_page` could not build its outlined copy; `stage` and `id` say where. Fix or remove that field or group with `edit_template`, then preview again. `overlay: "none"` still shows the page meanwhile. |
| `request_too_large` | The edited template is too large to save in one request. |
| `answer_in_template` | Edited wording matches an answer from this session, so nothing was sent; `matched` lists the ids and properties. If it is an answer, put the form's own wording back with `edit_template`. If it is the form's own wording, call `save_template` again with `confirm_wording: true`. |
| `confirm_wording_needed` | The working copy has wording edited in an earlier session. Review the ids and properties in `changed`, then call `save_template` with `confirm_wording: true`. |
| `local_edits_pending` | `reanalyze_template` won't run over unsaved edits: save them with `save_template` first, or pass `discard: true`. |
| `kind_required` | The pages already have fields: pass `kind` (`find`, `relabel`, `label_missing`, or `scratch` to replace them). |
| `job_in_progress` | A job is already running on the template. Wait and retry (call `reanalyze_template` again with the same arguments if you started it). |
| `upload_outcome_unknown` | An earlier upload was interrupted. So you aren't charged twice, the same file isn't uploaded again for about 10 minutes; check `get_account` meanwhile. |

## Verify the package

- Releases are built and published by GitHub Actions from [github.com/fenfill/mcp](https://github.com/fenfill/mcp) with [npm provenance](https://docs.npmjs.com/generating-provenance-statements): the [npm page](https://www.npmjs.com/package/@fenfill/mcp) links each version to the exact commit and workflow run that built it.
- The bundle (`dist/index.js`) is not minified, so what runs on your machine can be read. `dist/pdfium.wasm` is the unmodified PDFium build from `@embedpdf/pdfium` (see `THIRD_PARTY_NOTICES`), loaded only by `preview_page`.

In a project that installs `@fenfill/mcp`, this checks the registry signature and the provenance attestation:

```sh
npm audit signatures
```

Report vulnerabilities to [security@fenfill.com](mailto:security@fenfill.com), or through GitHub private vulnerability reporting on the repository, not in public issues.

---

This is the Markdown version of https://fenfill.com/docs/mcp.
