Skip to main content
Glama

documents_create

Render a document (PDF / HTML / PPTX / DOCX) and save it to the workspace.

This tool has two input pipelines — pass exactly one of content_html or content_markdown.

Pipeline A — content_html (canonical for decks, proposals, designed pages)

You author full HTML+CSS. A baked-in design-system preamble ships first (<style> with Inter/Manrope as data-URI fonts, CSS-variable palette tokens, 8px spacing scale, and pre-styled layout helpers); your markup and any of your own <style> blocks land after the preamble so you can override anything. Chromium renders the assembled document into a static PDF — JavaScript is disabled and DNS is blackholed, so external font / image / script fetches will fail by configuration.

Required when this pipeline is used:

  • title — human-readable, used for PDF metadata and the saved filename.

  • content_html — the <body> and any custom <style> blocks. The renderer wraps this in <html>…</html> and injects the preamble + a canonical <meta charset> + <title>. Do NOT emit <script>, <iframe>, <object>, <embed>, <meta>, <link>, <base>, <form>, or event handlers — the sanitizer strips them.

  • output_type — "pdf" or "html". ("pptx" and "docx" require content_markdown since they need structured markdown intermediates.)

Optional:

  • page_preset — "slide_16_9" (default for any deck), "a4" (default for flowing documents — used if omitted), "letter", or "none" (you declare your own @page rule). For a web-styled page (dark background, full-bleed sections) use "none" and declare @page { margin: 0 }, set the background on html as well as body, and add print-color-adjust: exact — the a4/letter presets keep 24mm paper margins, which paint as a white frame around dark designs.

  • design_tokens — flat dict overriding the preamble's CSS variables. Whitelisted keys: brand_primary, accent, surface_dark (hex color), font_display, font_body (font name from ['Inter', 'Manrope', 'monospace', 'sans-serif', 'serif', 'system-ui', 'ui-monospace', 'ui-sans-serif', 'ui-serif']).

  • language — BCP-47 tag (default "en"). Drives <html lang>.

Slide structure (page_preset="slide_16_9")

Each slide is <section class="slide …">…</section>. The base .slide class is what sizes it to the viewport and forces the page break — do not drop it. Composable variants (apply alongside .slide):

  • .slide-cover — gradient hero, big display title.

  • .slide-split — two equal columns, image + narrative.

  • .slide-stats — three-up KPI cards (use <div class="stat"> with .stat-value + .stat-label inside).

  • .slide-quote — centered pull quote + <cite> attribution.

Layout helpers (work in any preset): .grid-2, .grid-3, .split, .stack, .cluster, .callout, .muted, .kbd.

Speaker notes

<aside class="notes">…text…</aside> inside a <section class="slide">. The sanitizer strips them from the rendered PDF and returns them as slide_notes[] (parallel to slide order). Orphan notes outside any slide are dropped with a warning.

Images

Only these src schemes resolve:

  • file:NNN — workspace file_id.

  • data:image/...;base64,... — inline.

  • https://<host> where <host> ∈ DOCUMENTS_MEDIA_URL_ALLOWLIST. Other URLs are dropped and replaced with an HTML comment placeholder.

Pipeline B — content_markdown (invoice / contract only)

Required:

  • title, content_markdown, output_type.

Optional:

  • theme — "invoice" or "contract". Triggers the corresponding exemplar styling and (for invoices) the arithmetic validator that fail-closes on missing or mismatched totals.

  • language — BCP-47 (default "en").

Delivery contract (CRITICAL)

After this tool returns file_id, deliver the file with messages.send(attachments=[file_id], text="<short caption>"). Embedding the file_id in a markdown link, sandbox: URL, or /api/files/<id>/download text will render as plain text on the recipient's channel — the attachments parameter is the only way the file actually attaches.

Exemplars

INVOICE (English):

Invoice INV-{YYYYMMDD-HHMMSS}

From: {Issuer Legal Name}, {Address}, {Tax ID} To: {Customer Name}, {Customer Address}, {Customer Tax ID} Issue date: {YYYY-MM-DD} Due date: {YYYY-MM-DD}

Description

Qty

Unit price

Total

{Service 1}

1

1500.00

1500.00

{Service 2}

2

500.00

1000.00

Subtotal: USD 2500.00 Tax (20%): USD 500.00 Total: USD 3000.00

Payment: {bank details OR crypto wallet — never both}

INVOICE (Russian):

Счёт-фактура № INV-{YYYYMMDD-HHMMSS}

От: {Юридическое название организации}, {Адрес}, ИНН {Tax ID} Кому: {Название клиента}, {Адрес клиента}, ИНН {Tax ID} Дата: {YYYY-MM-DD} Срок оплаты: {YYYY-MM-DD}

Описание

Кол-во

Цена

Сумма

{Услуга 1}

1

1500.00

1500.00

{Услуга 2}

2

500.00

1000.00

Подытог: USD 2500.00 НДС (20%): USD 500.00 Итого: USD 3000.00

Реквизиты: {банковские реквизиты ИЛИ криптокошелёк — не оба сразу}

CONTRACT (English):

Service Agreement

Between: {Provider Legal Name}, {Address} ("Provider") And: {Client Legal Name}, {Address} ("Client") Effective date: {YYYY-MM-DD}

1. Scope of services

{Concise description of what Provider agrees to deliver.}

2. Term

This Agreement begins on the Effective date and continues until {termination condition or end date}.

3. Compensation

Client pays Provider {amount and currency} according to {payment schedule}.

4. Confidentiality

Both parties agree to keep proprietary information of the other party confidential during and after the term of this Agreement.

5. Termination

Either party may terminate with {N} days' written notice.

6. Governing law

{Jurisdiction}.


Provider: ____________________ Client: ____________________ {Provider signatory name} {Client signatory name}

CONTRACT (Russian):

Договор оказания услуг

Между: {Юридическое название Исполнителя}, {Адрес} ("Исполнитель") И: {Юридическое название Заказчика}, {Адрес} ("Заказчик") Дата вступления в силу: {YYYY-MM-DD}

1. Предмет договора

{Краткое описание услуг, которые Исполнитель обязуется оказать.}

2. Срок действия

Договор вступает в силу с указанной даты и действует до {условие прекращения или дата окончания}.

3. Стоимость и порядок оплаты

Заказчик оплачивает услуги Исполнителя в размере {сумма и валюта} в порядке {график платежей}.

4. Конфиденциальность

Стороны обязуются сохранять конфиденциальность сведений, полученных в ходе исполнения настоящего Договора, в течение срока его действия и после его прекращения.

5. Расторжение

Любая из сторон вправе расторгнуть Договор, направив письменное уведомление не менее чем за {N} дней.

6. Применимое право

{Юрисдикция}.


Исполнитель: ____________________ Заказчик: ____________________ {ФИО подписанта Исполнителя} {ФИО подписанта Заказчика}

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
themeNoInvoice or contract styling for content_markdown. Rejected with content_html (use design_tokens + your own CSS instead). OMIT for default (unthemed) styling.
titleYesShort human-readable title for the document.
languageNoBCP-47 language tag (e.g. 'en', 'ru', 'zh', 'ja'). Drives <html lang> and (markdown path) font fallback for non-Latin scripts.en
output_typeYesRenderer target: 'pdf' | 'pptx' | 'docx' | 'html'. PPTX/DOCX require content_markdown.
page_presetNoPage geometry for content_html. 'slide_16_9' = 1280x720 deck, 'a4'/'letter' = flowing document, 'none' = LLM declares its own @page. Defaults to 'a4' inside the html branch when omitted. Rejected with content_markdown.
content_htmlNoFull HTML body (with optional <style> blocks) for the canonical Chromium pipeline. Mutually exclusive with content_markdown.
in_workspaceNoRun this one call in this workspace id instead of the session's. Nothing is stored; other sessions are not affected.
design_tokensNoFlat dict of CSS-variable overrides for content_html. Whitelisted keys: brand_primary, accent, surface_dark (hex color), font_display, font_body (Inter|Manrope|system-ui|ui-sans-serif|ui-serif|ui-monospace|sans-serif|serif|monospace). Unknown keys / invalid values are dropped with a warning. Rejected with content_markdown.
content_markdownNoMarkdown body for the invoice/contract pipeline. Mutually exclusive with content_html.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • addedInput schema / properties / in_workspace
      Added value: +{
      +  "description": "Run this one call in this workspace id instead of the session's. Nothing is stored; other sessions are not affected.",
      +  "type": "integer"
      +}
  2. Added
  3. Removed
  4. Changed1 schema field changed
    • changedInput schema / properties / theme / description
      Previous value: -"Invoice or contract styling for content_markdown. Rejected with content_html (use design_tokens + your own CSS instead)."New value: +"Invoice or contract styling for content_markdown. Rejected with content_html (use design_tokens + your own CSS instead). OMIT for default (unthemed) styling."
  5. Changed12 schema fields changed
    • addedInput schema / properties / content_html
      Added value: +{
      +  "description": "Full HTML body (with optional <style> blocks) for the canonical Chromium pipeline. Mutually exclusive with content_markdown.",
      +  "type": "string"
      +}
    • changedInput schema / properties / content_markdown / description
      Previous value: -"Markdown body authored by the agent. Slides separated by '---' on its own top-level line."New value: +"Markdown body for the invoice/contract pipeline. Mutually exclusive with content_html."
    • addedInput schema / properties / design_tokens
      Added value: +{
      +  "description": "Flat dict of CSS-variable overrides for content_html. Whitelisted keys: brand_primary, accent, surface_dark (hex color), font_display, font_body (Inter|Manrope|system-ui|ui-sans-serif|ui-serif|ui-monospace|sans-serif|serif|monospace). Unknown keys / invalid values are dropped with a warning. Rejected with content_markdown.",
      +  "type": "object"
      +}
    • removedInput schema / properties / engine
      Removed value: -{
      -  "description": "PDF/HTML engine for presentations. 'marp' (default for format=presentation) renders via headless Chromium with full CSS3, web fonts, and layout classes (.cover, .hero, .split, .stats, .dark). 'weasyprint' is the legacy renderer. Rejected for output_type=pptx (always uses python-pptx). OMIT to use the per-format default engine. python-pptx for editable text — use output_type=pdf or html, or remove the engine parameter). Rejected for format=document (always weasyprint).",
      -  "enum": [
      -    "weasyprint",
      -    "marp"
      -  ],
      -  "type": "string"
      -}
    • removedInput schema / properties / format
      Removed value: -{
      -  "description": "'document' for a single flowing body, 'presentation' for slides.",
      -  "enum": [
      -    "document",
      -    "presentation"
      -  ],
      -  "type": "string"
      -}
    • changedInput schema / properties / language / description
      Previous value: -"BCP-47 language tag (e.g. 'en', 'ru', 'zh', 'ja'). Drives font fallback for non-Latin scripts."New value: +"BCP-47 language tag (e.g. 'en', 'ru', 'zh', 'ja'). Drives <html lang> and (markdown path) font fallback for non-Latin scripts."
    • changedInput schema / properties / output_type / description
      Previous value: -"Renderer target: 'pdf' | 'pptx' | 'docx' | 'html'."New value: +"Renderer target: 'pdf' | 'pptx' | 'docx' | 'html'. PPTX/DOCX require content_markdown."
    • addedInput schema / properties / page_preset
      Added value: +{
      +  "description": "Page geometry for content_html. 'slide_16_9' = 1280x720 deck, 'a4'/'letter' = flowing document, 'none' = LLM declares its own @page. Defaults to 'a4' inside the html branch when omitted. Rejected with content_markdown.",
      +  "enum": [
      +    "slide_16_9",
      +    "a4",
      +    "letter",
      +    "none"
      +  ],
      +  "type": "string"
      +}
    • removedInput schema / properties / theme / default
      Removed value: -"default"
    • changedInput schema / properties / theme / description
      Previous value: -"Visual theme. invoice/contract trigger the corresponding exemplar styling."New value: +"Invoice or contract styling for content_markdown. Rejected with content_html (use design_tokens + your own CSS instead)."
    • changedInput schema / properties / theme / enum
      Previous value: -[
      -  "default",
      -  "corporate",
      -  "minimal",
      -  "pitch",
      -  "invoice",
      -  "contract",
      -  "cinema",
      -  "editorial"
      -]New value: +[
      +  "invoice",
      +  "contract"
      +]
    • changedInput schema / required
      Previous value: -[
      -  "title",
      -  "content_markdown",
      -  "format",
      -  "output_type"
      -]New value: +[
      +  "title",
      +  "output_type"
      +]
  6. First observed

TDQS

A4.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=false, destructiveHint=false, idempotentHint=false and openWorldHint=false, so the write/safety profile is covered. The description adds genuine behavioral context beyond annotations: JS disabled and DNS blackholed, sanitizer strips script/iframe/link tags, image src allowlist, and speaker notes stripped from the PDF and returned as slide_notes[]. It does not, however, discuss rate limits, quotas, or cost, so it stops short of a 4-5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and pipeline rules are front-loaded, headers segment the material logically, and the exemplars genuinely earn their space as few-shot guidance. The definition is very long and the four localized exemplars make it heavier than strictly necessary, but almost every block adds actionable instruction.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the return-value burden and does so: it tells the agent the tool returns file_id (and slide_notes[]), and it closes the loop with the attachment delivery contract. Given the two pipelines, nested design_tokens object, and four output formats, nothing an agent needs to call this correctly appears missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description adds real meaning on top: it explains why a4/letter presets paint a white 24mm frame around dark designs (motivating page_preset='none'), clarifies theme is rejected with content_html, and details the slide/notes/image conventions the schema only names. Some content (design_tokens whitelist, page_preset defaults) duplicates the schema, keeping it from a 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence gives a specific verb (render) plus the exact output formats and the side effect (save to workspace), so an agent instantly knows what the tool does. It is clearly distinguishable from siblings like artifacts_export_pdf or images_generate.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Routes the agent explicitly: 'pass exactly one of content_html or content_markdown', with named conditions ('canonical for decks, proposals, designed pages' vs 'invoice / contract only'). It also spells out the required delivery step (messages.send with attachments=[file_id]) and warns what will NOT attach.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.