MCP Server
The API ships a built-in Model Context Protocol (MCP) server, so AI agents such as Claude Code and Codex can generate accessible PDF/A-3a + PDF/UA documents directly as a tool call — no glue code required.
The server is exposed at /mcp and offers three tools:
| Tool | Description | Required input |
|---|---|---|
render_template | Render a pdf-ua-api JSON template | template |
render_html | Render raw HTML | html |
render_url | Fetch a URL and render its HTML | url |
Each tool returns the same payload: the document UUID, the content type, a file name, and the PDF
itself as a base64 string (pdfBase64).
Endpoint & transport
Section titled “Endpoint & transport”- URL:
http://<host>:8080/mcp(the path is/mcp) - Transport: HTTP + SSE (the agent opens an SSE stream on
GET /mcpand posts messages back). - Auth & limits: the
/mcpendpoint sits behind the same envelope as the render endpoints — the configured authentication and rate limits apply. When anAPI_KEYis set, send it asAuthorization: Bearer <API_KEY>. When neitherAPI_KEYnor JWT is configured, the endpoint is open (handy for local testing).
Running the server
Section titled “Running the server”The MCP server is always on; it is part of the standard application, so any way you run the API also
serves /mcp.
# open (no auth) — good for local testingdocker run -p 8080:8080 ghcr.io/bambamboole/pdf-ua-api:latest
# protected with an API keydocker run -p 8080:8080 \ -e API_KEY="super-secret-key" \ ghcr.io/bambamboole/pdf-ua-api:latestConfirm it is reachable — the SSE stream opens and emits an endpoint event:
curl -N http://localhost:8080/mcp# event: endpoint# data: ?sessionId=...Add -H "Authorization: Bearer super-secret-key" if you started the server with an API_KEY.
Connect Claude Code
Section titled “Connect Claude Code”Claude Code talks to remote MCP servers over SSE directly. Register the server with claude mcp add:
# local, open serverclaude mcp add --transport sse pdf-ua-api http://localhost:8080/mcp
# remote / protected serverclaude mcp add --transport sse pdf-ua-api https://your-host/mcp \ --header "Authorization: Bearer super-secret-key"Then verify and use it:
claude mcp list # pdf-ua-api should report "connected"Inside a Claude Code session, run /mcp to see the render_template, render_html, and
render_url tools, then ask it to “render this HTML to an accessible PDF” and Claude will call the
tool and save the returned base64 PDF.
Use --scope user to make the server available across all your projects, or --scope project to
share it with your team via a committed .mcp.json.
Connect Codex
Section titled “Connect Codex”Codex launches MCP servers as local processes (stdio), so reach the HTTP/SSE endpoint through the
mcp-remote bridge. Add the server to
~/.codex/config.toml:
# local, open server[mcp_servers.pdf-ua-api]command = "npx"args = ["-y", "mcp-remote", "http://localhost:8080/mcp"]For a protected server, pass the bearer token as a header (and keep the secret in env):
[mcp_servers.pdf-ua-api]command = "npx"args = ["-y", "mcp-remote", "https://your-host/mcp", "--header", "Authorization: Bearer ${PDF_UA_API_KEY}"]env = { PDF_UA_API_KEY = "super-secret-key" }Restart Codex; the three render tools then appear in its tool list.
Local development
Section titled “Local development”To run the server straight from a checkout while you iterate:
# starts http://localhost:8080 with /mcp open (no API_KEY set)./gradlew run
# disable rate limiting if you are hammering it during testingRATE_LIMIT_ENABLED=false ./gradlew runPoint Claude Code or Codex at http://localhost:8080/mcp as shown above. To remove the server again
from Claude Code, run claude mcp remove pdf-ua-api.
Tool reference
Section titled “Tool reference”render_html
Section titled “render_html”{ "html": "<html lang=\"en\"><head><title>Doc</title></head><body><h1>Hi</h1></body></html>", "baseUrl": "https://example.com", "embedColorProfile": true}render_url
Section titled “render_url”{ "url": "https://example.com", "embedColorProfile": true}render_template
Section titled “render_template”Takes a template object (see Template structure) plus optional per-block
data overrides:
{ "template": { "version": 2, "rows": [{ "blocks": [{ "type": "text", "text": "Hello from MCP" }] }] }}All three tools also accept an optional attachments array to embed files in the PDF/A-3 container.
On a validation error the tool result is flagged as an error with a structured error payload (for
example validation_failed with the offending issues), so the agent can correct the input and retry.