Get your API key
How to generate PDFs from Cursor with an MCP server

How to generate PDFs from Cursor with an MCP server

Wire PDF4.dev into Cursor over MCP so the AI editor can create templates and render PDFs from your codebase. One-click config, the tools exposed, and a real workflow.

9 min read

Cursor can generate PDFs without leaving the editor once you connect the PDF4.dev MCP server. Add the Streamable HTTP endpoint https://pdf4.dev/api/mcp to Cursor's MCP config with your API key, and the agent gains tools like list_templates, create_template, and render_pdf. The cleanest setup is MCP with delivery: "url": Cursor reads your code, builds a template, renders it on PDF4.dev's headless Chromium, and hands you back a link.

This guide covers the exact mcp.json block, every tool Cursor sees, and one real workflow: turning a React invoice component into a reusable PDF template and rendering a sample, all from the Cursor chat.

MCP vs raw REST from a Cursor terminal: which should you use?

MCP lets the Cursor agent call PDF4.dev tools directly and chain steps on its own. A raw REST call from the terminal gives you manual control but no autonomy. Pick MCP when you want the agent to read code and produce a PDF in one prompt, pick REST for scripted one-offs.

FactorMCP serverRaw REST (curl in terminal)
SetupOne mcp.json block, onceNone, but key handling is manual each call
Agent autonomyAgent chains read, create, render itselfYou paste the command and key every time
Key exposureStored once in config, not in chatRisk of pasting p4_live_ into the terminal/history
Best for"Turn this component into a PDF" promptsOne-off renders, CI scripts, cron jobs
DiscoverabilityAgent sees tool names + schemasYou must remember the endpoint and body shape
Result handlingdelivery: "url" returns a clean linkSame, but you parse JSON yourself

If you already script renders in CI, keep the REST call there. Add MCP for the interactive part: the moment in Cursor where you want the agent to design and render a template from your code in one go.

How do you add the PDF4.dev MCP server to Cursor?

Add a Streamable HTTP server entry to Cursor's mcp.json pointing at https://pdf4.dev/api/mcp, with your PDF4.dev API key in an Authorization header. Cursor reads MCP config from two places: a global file at ~/.cursor/mcp.json (all projects) or a per-project file at .cursor/mcp.json in the repo root. Use the project file when only one repo needs PDF generation.

First, create an API key in the PDF4.dev dashboard under Settings, in the API keys section. Keys start with p4_live_ and are shown once, so copy it immediately.

Then add the server block:

{
  "mcpServers": {
    "pdf4": {
      "url": "https://pdf4.dev/api/mcp",
      "headers": {
        "Authorization": "Bearer p4_live_xxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

After saving, open Cursor Settings, go to the MCP section, and confirm the pdf4 server shows a green status with its tools listed. If it stays red, the key is wrong or has the wrong scope. Use a full_access key so the agent can create templates, not just render.

Do not commit a file that contains a live p4_live_ key. Either keep mcp.json out of version control, or use a render-only key with reduced scope for shared configs. You can revoke any key from the dashboard at any time.

What tools does PDF4.dev expose to Cursor?

PDF4.dev exposes 14 MCP tools to Cursor. The ones that matter for an editor workflow are template CRUD and rendering. Each tool returns structured content, so Cursor parses results without guessing at JSON shapes.

ToolWhat the agent does with it
get_infoRead account context and capabilities
list_templatesSee existing templates before creating a duplicate
get_templateRead one template's HTML and sample data
create_templateSave a new HTML template with Handlebars variables
update_templateEdit an existing template's HTML or format
render_pdfRender a template or raw HTML to a PDF (returns a URL or base64)
preview_templateRender a quick preview image of a template
list_componentsList reusable header/footer/block fragments
create_componentSave a reusable header, footer, or block
list_logsRead recent render logs for debugging

The full set includes get_component, update_component, delete_component, and delete_template. In practice, a Cursor session that produces an invoice PDF touches three: create_template, then render_pdf, with an optional list_templates first to avoid duplicates.

How do you turn a React component into a PDF from Cursor?

Ask Cursor to read your React component, rewrite the markup as an HTML template with Handlebars variables, save it with create_template, then render a sample with render_pdf. Cursor cannot render JSX to PDF directly, but it can translate JSX into static HTML that PDF4.dev's headless Chromium renders faithfully, CSS and all.

Say you have an invoice component like this in your codebase:

// components/Invoice.tsx
export function Invoice({ company, number, items, total }: InvoiceProps) {
  return (
    <article className="invoice">
      <header>
        <h1>{company}</h1>
        <span>Invoice #{number}</span>
      </header>
      <table>
        {items.map((it) => (
          <tr key={it.sku}>
            <td>{it.name}</td>
            <td>{it.price}</td>
          </tr>
        ))}
      </table>
      <p className="total">Total: {total}</p>
    </article>
  )
}

A single prompt in the Cursor chat drives the whole flow:

Read components/Invoice.tsx. Create a PDF4.dev template named "Invoice" that mirrors its layout, using Handlebars variables for company, number, the items array, and total. Then render a sample with realistic data and give me the URL.

Cursor calls create_template with HTML where the props become variables. The dynamic row becomes a Handlebars {{#each items}} block, and {{company}}, {{number}}, and {{total}} map to the props one to one. The saved template HTML looks like this:

<article class="invoice">
  <header>
    <h1>{{company}}</h1>
    <span>Invoice #{{number}}</span>
  </header>
  <table>
    {{#each items}}
    <tr>
      <td>{{this.name}}</td>
      <td>{{this.price}}</td>
    </tr>
    {{/each}}
  </table>
  <p class="total">Total: {{total}}</p>
</article>

Because the prompt asks for a URL, Cursor passes delivery: "url" and gets back a short signed link valid for 24 hours instead of a base64 blob that would crowd its context window. You click the link, the PDF opens, done. No Chromium install, no serverless function to deploy.

Need to verify the markup before wiring MCP? Paste your HTML into the free Html To PdfTry it free tool to see exactly how PDF4.dev's Chromium renders it. Same engine, no API key needed.

What does the render request look like under the hood?

The render_pdf MCP tool maps to a single POST to https://pdf4.dev/api/v1/render. Whether Cursor calls it over MCP or you run it from the terminal, the body is the same JSON: a template_id or raw html, a data object for the variables, an optional format, and a delivery mode.

If you prefer to test from the Cursor terminal before trusting the agent, the equivalent curl is:

curl -X POST https://pdf4.dev/api/v1/render \
  -H "Authorization: Bearer p4_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "invoice",
    "delivery": "url",
    "data": {
      "company": "Acme Corp",
      "number": "INV-001",
      "total": "$1,500.00",
      "items": [
        { "name": "Design work", "price": "$1,000.00" },
        { "name": "Hosting", "price": "$500.00" }
      ]
    }
  }'

The response in url mode is { url, expires_at, size_bytes, duration_ms }. The url points at GET /api/v1/renders/[id]?token=... and expires after 24 hours. For PDFs over roughly 1 MB, and for any agent use, the URL mode keeps payloads small.

Which option should you choose?

Choose MCP in Cursor when you want the agent to read your code and produce a PDF in one prompt. Choose raw REST when you want explicit, scriptable control. Here is the recommendation by scenario.

  • You want Cursor to design templates from your components: use the MCP server. This is where the agent chaining create_template then render_pdf saves the most time. The React-to-template workflow above is the canonical case.
  • You render PDFs in CI or a cron job: use raw REST with a render-only key. No editor in the loop means no need for MCP. Store the key as an environment variable, not in mcp.json.
  • You render the same template many times with different data: create the template once (via MCP or the dashboard), then call render_pdf or the REST endpoint with just the data object. Templates are reusable by template_id or slug.
  • You are testing markup or one-off conversions: skip both and use the free Html To PdfTry it free tool, or send raw html to render_pdf without saving a template.
  • You need the PDF link in another tool (Slack, email, a webhook): always use delivery: "url". The signed link is portable and avoids passing megabytes of base64 around.

The same MCP server works in any MCP-capable client. If you also use Claude or ChatGPT for PDF tasks, the tool names (create_template, render_pdf) and the delivery: "url" recommendation carry over unchanged. See the related guides below for the per-client config.

Connecting PDF4.dev to Cursor over MCP collapses "design a document, then render it" into one chat turn. The agent reads your code, writes the template, and returns a link, with headless Chromium doing the rendering server-side so there is nothing to install and no cold-start to fight. Add the mcp.json block, create a full_access key, and ask Cursor to turn your next component into a PDF.

Free tools mentioned:

Html To PdfTry it free

Start generating PDFs

Build PDF templates with a visual editor. Render them via API from any language in ~300ms.