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.
| Factor | MCP server | Raw REST (curl in terminal) |
|---|---|---|
| Setup | One mcp.json block, once | None, but key handling is manual each call |
| Agent autonomy | Agent chains read, create, render itself | You paste the command and key every time |
| Key exposure | Stored once in config, not in chat | Risk of pasting p4_live_ into the terminal/history |
| Best for | "Turn this component into a PDF" prompts | One-off renders, CI scripts, cron jobs |
| Discoverability | Agent sees tool names + schemas | You must remember the endpoint and body shape |
| Result handling | delivery: "url" returns a clean link | Same, 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.
| Tool | What the agent does with it |
|---|---|
get_info | Read account context and capabilities |
list_templates | See existing templates before creating a duplicate |
get_template | Read one template's HTML and sample data |
create_template | Save a new HTML template with Handlebars variables |
update_template | Edit an existing template's HTML or format |
render_pdf | Render a template or raw HTML to a PDF (returns a URL or base64) |
preview_template | Render a quick preview image of a template |
list_components | List reusable header/footer/block fragments |
create_component | Save a reusable header, footer, or block |
list_logs | Read 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_templatethenrender_pdfsaves 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_pdfor the REST endpoint with just thedataobject. Templates are reusable bytemplate_idor slug. - You are testing markup or one-off conversions: skip both and use the free Html To PdfTry it free tool, or send raw
htmltorender_pdfwithout 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:
Start generating PDFs
Build PDF templates with a visual editor. Render them via API from any language in ~300ms.



