Airpdf MCP — Tool reference
All 8 tools the Airpdf MCP server exposes. Every tool is dispatched via
the standard MCP tools/call JSON-RPC envelope:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": { "name": "<tool>", "arguments": { … } }
}
Endpoint: POST https://airpdf.app/mcp with Authorization: Bearer <mcp_live_…>.
Errors always come back in agent-friendly form:
{ "ok": false, "errors": [ { "path": "/some/field", "code": "…", "message": "…", "suggestion": "…" } ] }
Conventions
-
All tools are per-workspace. The bearer token's
McpConnectionrow carries thetenant_code, and every Ash query runs in that tenant's schema. Cross-tenant access is impossible. -
stateon a template is one of:draft | :active | :archived.list_templatesreturns:draft+:activeonly. -
Mustache placeholders in template bodies always use
{{path.to.value}}. Values are HTML-escaped server-side.
Authoring flow
author_guide # read the prompt + constraints once
↓
create_template # save as DRAFT
↓
preview_render # iterate until the output looks right
↓
validate_data # schema-check sample data quickly
↓
update_template # patch the draft as needed
↓
publish_template # promote DRAFT → LIVE + snapshot version
Tools
list_templates
List templates visible to this MCP connection (draft + active; archived excluded).
| Arg | Type | Required | Notes |
|---|---|---|---|
| (none) |
Returns: array of {slug, name, engine, state, inserted_at, updated_at}.
get_template
Fetch one template by slug.
| Arg | Type | Required | Notes |
|---|---|---|---|
slug |
string | yes |
from list_templates |
Returns: the template record with both live and draft snapshot fields
(html_template, schema, variables_schema, default_sample_data,
draft_* counterparts, timestamps).
create_template
Save a new template as a draft. Never writes to live.
| Arg | Type | Required | Notes |
|---|---|---|---|
name |
string | yes | display name |
draft_engine |
"html" | "react_pdf" |
yes | which renderer |
draft_html_template |
string |
when engine = "html" |
HTML body |
draft_schema |
object |
when engine = "react_pdf" |
JSON tree |
draft_variables_schema |
object | recommended |
JSON Schema for data |
default_sample_data |
object | recommended |
seed for preview_render |
Returns: {slug, draft_updated_at}.
update_template
Patch a draft. Live snapshot remains untouched.
| Arg | Type | Required | Notes |
|---|---|---|---|
slug |
string | yes | which template |
draft_html_template |
string | no | |
draft_schema |
object | no | |
draft_variables_schema |
object | no | |
default_sample_data |
object | no |
Returns: {slug, draft_updated_at}.
publish_template
Promote the draft to live state and snapshot a TemplateVersion. Call
only when the user explicitly asks to publish.
| Arg | Type | Required |
|---|---|---|
slug |
string | yes |
Returns: {slug, live_updated_at, version_number}.
Failure modes:
-
no_draft— template has no draft to promote. -
forbidden— connection scope doesn't allow publish.
preview_render
Render a watermarked PDF preview. Does NOT consume quota.
| Arg | Type | Required | Notes |
|---|---|---|---|
slug |
string | yes | which template |
version |
"draft" | "live" |
no |
default "draft" |
data |
object | no |
falls back to default_sample_data |
Returns:
{
"pdf_base64": "JVBERi0…",
"engine": "html",
"render_ms": 412,
"byte_size": 18742,
"watermarked": true
}
pdf_base64 is a standard base64 string (decoder hint: Base.decode64!
in Elixir / atob in JS). The watermark cannot be disabled.
validate_data
Quick JSON-Schema check of sample data against the template's
variables_schema. No render.
| Arg | Type | Required |
|---|---|---|
slug |
string | yes |
data |
object | yes |
version |
"draft" | "live" |
no |
Returns: {ok: true} or {ok: false, errors: [{path, code, message}]}.
author_guide
Static prompt + engine constraints. Call this BEFORE drafting a new template — it tells the LLM which tags, props, and node types are allowed, plus best-practice tips.
| Arg | Type | Required | Notes |
|---|---|---|---|
engine |
"html" | "react_pdf" |
no |
default "html" |
Returns:
{
"version": "2026-05-19-v1",
"guide": "You are authoring an Airpdf …",
"engine_constraints": { … },
"best_practices": [ "…", "…" ]
}
Rate limits
| Limit | Default |
|---|---|
Tool calls per minute, per McpConnection |
120 |
preview_render calls counted toward daily_usage.previews_count (no billing) |
unbounded |
publish_template audited via tenant_audit_log |
every call |
| Access token TTL | 1 hour |
| Refresh token TTL | 30 days (single-use, rotated on every refresh) |
Versioning
The MCP protocol version is currently 2025-03-26, which adds the
OAuth Authorization spec (PKCE, Authorization Server Metadata, Dynamic
Client Registration, refresh-token rotation — all of which Airpdf
implements) and Streamable HTTP transport. Older clients that request
2024-11-05 still work because protocol negotiation happens in the
JSON-RPC initialize exchange. We'll bump to 2025-06-18 once
ash_ai ships support for it. Tool schemas follow semver — additive
changes are minor, breaking changes get a major bump and a 30-day
deprecation notice.