# @leafwright/mcp

The official [Leafwright](https://leafwright.co) MCP server. It gives an AI
coding agent (Claude Code, Cursor, Codex, and any MCP client) the ability to
**generate PDFs, build templates, and debug failed renders from their logs** —
all through the Leafwright public API.

No headless Chrome, no Puppeteer in your bundle. The agent calls a tool, gets a
real signed PDF URL back.

## Connect from claude.ai (remote, no install)

Leafwright also runs as a remote MCP server with OAuth. In claude.ai →
Settings → Connectors → **Add custom connector**, enter:

- **Name**: Leafwright
- **Remote MCP server URL**: `https://leafwright.co/mcp`

Leave the OAuth fields empty — the connector registers itself and opens a
browser window where you sign in and pick an organization and default project.
Works with any MCP client that supports OAuth 2.1 + PKCE and Dynamic Client
Registration.

## Install (local stdio)

Nothing to install — run it with `npx`:

```sh
npx -y @leafwright/mcp
```

Start the server without credentials, then call `leafwright_login` from your
agent. Open the returned verification URL, sign in, choose an organization and
default project, then call `leafwright_auth_status`.

Project API keys are still supported for server automation:

```sh
LEAFWRIGHT_API_KEY=lw_live_...
```

Get an account at [leafwright.co](https://leafwright.co).

## Claude Code — install the plugin

```
/plugin marketplace add Tactic-Systems/leafwright-claude-plugin
/plugin install leafwright@leafwright
```

Connects to the hosted server, so there is nothing to install and Claude Code
handles the sign-in.

## Configure your client

One-click:

[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/install-mcp?name=leafwright&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBsZWFmd3JpZ2h0L21jcCJdfQ==)

From the command line:

```sh
# Claude Code
claude mcp add --transport http leafwright https://leafwright.co/mcp
claude mcp add --transport stdio leafwright -- npx -y @leafwright/mcp

# Codex
codex mcp add leafwright --url https://leafwright.co/mcp
codex mcp add leafwright -- npx -y @leafwright/mcp
```

By hand, **Claude Code** (`~/.claude/mcp.json` or project `.mcp.json`) and
**Cursor** (`~/.cursor/mcp.json`) share this shape:

```json
{
  "mcpServers": {
    "leafwright": {
      "command": "npx",
      "args": ["-y", "@leafwright/mcp"],
      "env": {}
    }
  }
}
```

**Codex** is the exception — it reads TOML from `~/.codex/config.toml`, so the
JSON above will not work there:

```toml
[mcp_servers.leafwright]
command = "npx"
args = ["-y", "@leafwright/mcp"]
```

Restart the client. Before login, only auth/help tools appear; after browser
auth, the full `leafwright_*` tool set appears.

## Environment

| Variable | Required | Default | Purpose |
| --- | --- | --- | --- |
| `LEAFWRIGHT_API_KEY` | no | — | Backward-compatible project API key (`lw_live_…` or `lw_test_…`). |
| `LEAFWRIGHT_MCP_TOKEN` | no | — | MCP token (`mcp_…`) for non-interactive setups. |
| `LEAFWRIGHT_PROJECT_ID` | no | selected project | Override the active default project. |
| `LEAFWRIGHT_MCP_READ_ONLY` | no | `false` | `true` exposes only read tools — no renders, no template writes, no AI. |

## Tools

**Auth and context:**

| Tool | What it does |
| --- | --- |
| `leafwright_login` | Start browser login. |
| `leafwright_auth_status` | Complete/check browser login. |
| `leafwright_logout` | Revoke and clear local MCP auth. |
| `leafwright_list_organizations` | List the selected token's organization. |
| `leafwright_list_projects` | List projects in the selected organization. |
| `leafwright_select_project` | Set the local active project. |

**Read (available after auth, no side effects):**

| Tool | What it does |
| --- | --- |
| `leafwright_list_templates` | List the project's templates. |
| `leafwright_get_template` | Fetch one template (draft HTML/CSS, sample data, versions). |
| `leafwright_validate_template` | Validate a template / draft overrides before rendering. |
| `leafwright_get_job` | Render job status and metadata. |
| `leafwright_get_job_logs` | Render logs — failed assets, fonts, timeouts, overflow. |
| `leafwright_get_usage` | Credits used/remaining for the billing period. |
| `leafwright_estimate_usage_cost` | Estimate credits and overage cost for a planned batch of renders. |

**Write (hidden when `LEAFWRIGHT_MCP_READ_ONLY=true`):**

| Tool | What it does |
| --- | --- |
| `leafwright_render_html` | Render raw HTML → PDF (signed URL). |
| `leafwright_render_url` | Render a public URL → PDF (SSRF-protected). |
| `leafwright_render_template` | Render a published template with JSON data. |
| `leafwright_create_template` | Create a draft template. |
| `leafwright_update_template_draft` | Update a draft's HTML/CSS/name/sample data. |
| `leafwright_ai_generate_template` | Generate a draft template from a prompt. |
| `leafwright_ai_revise_template` | Revise a draft from a natural-language instruction. |
| `leafwright_ai_fix_from_logs` | **Repair a template from a failed render's logs.** |

## HTML rules

Leafwright validates all HTML before rendering — the same rules apply to
`leafwright_render_html`, template create/update, and validation.

**Supported**

- Full documents: doctype, `<html>`, `<head>`, `<meta>`, `<title>`, `<body>`,
  and `<style>` blocks.
- Text, heading, list, table, and layout tags (`div`, `section`, `header`,
  `footer`, `main`, `article`, `figure`).
- `<img>` with base64 data URIs (`data:image/png`, `jpeg`, `gif`, `webp`, or
  `svg+xml`), relative paths, or `cid:` references.
- `class`, `style`, `id`, standard layout/table/meta/image attributes, and
  `aria-*` / `data-*`.
- Handlebars placeholders like `{{customer.name}}` in templates.

**Rejected**

- `<script>`, `<iframe>`, `<object>`, `<embed>`, `<form>`, `<link>`, and inline
  event handlers.
- Inline `<svg>` markup (`svg`, `path`, `rect`, `g`, and the rest of the SVG
  element family). Encode the SVG as a **base64** data URI and reference it
  from an `<img>` instead — URL-encoded SVG data URIs are rejected:

  ```html
  <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0i..." alt="Logo">
  ```

- SVG in CSS `url()` backgrounds — use an `<img>` element instead.
- External http(s) URLs in `src` attributes or CSS `url()` — the renderer never
  fetches remote assets for HTML sources. Embed images as data URIs instead.
- CSS `@import`.

Size limits: HTML 250 KB, CSS 120 KB, up to 200 pages per PDF. To render a
public web page *with* its remote assets, use `leafwright_render_url`, which
fetches the page behind SSRF protections.

## Prompts

Ready-made workflows your client can invoke: `create_invoice_template`,
`create_report_template`, `create_template_from_prompt`, `debug_pdf_layout`,
`explain_render_failure`, `integrate_nextjs`, `migrate_from_puppeteer`.

## Resources

`leafwright://docs` (API quickstart), `leafwright://templates` (your
templates), `leafwright://usage` (current usage), `leafwright://jobs/recent`
(the 20 most recent render jobs), `leafwright://jobs/{jobId}/logs` (logs for a
specific job).

## The debugging loop

A code-only agent can't see its PDF. This server closes that loop:

1. Agent renders a template → it fails or looks wrong.
2. Agent calls `leafwright_get_job_logs` → reads the real cause.
3. Agent calls `leafwright_ai_fix_from_logs` → gets a fix, re-renders, confirms.

## Safety

The server only calls the Leafwright public API. It runs no shell commands from
agent input, stores MCP tokens in the OS keychain when possible, falls back to a
permission-restricted config file, and supports read-only mode. AI **drafts**
templates; publishing always requires a human in the dashboard.

## Listed in the MCP registry

`co.leafwright/leafwright` in the
[official MCP registry](https://registry.modelcontextprotocol.io/v0.1/servers?search=co.leafwright).

## License

MIT · [leafwright.co](https://leafwright.co)
