Beta

MCP server

An AI assistant that speaks the Model Context Protocol (MCP) can use Koutei with your API key: list the templates, read one, check a request, preview it and make the PDF. It uses the same templates, checks, credits and limits as the API.

Works with

The server needs a client that sends your API key in a request header. Where each one stands today:

MCP clients and whether they connect
ClientStatusHow it sends the key
Claude CodeDocumented by the vendor; not yet tested by usA request header, in .mcp.json or with claude mcp add (below).
CursorDocumented by the vendor; not yet tested by usA request header, in mcp.json (below).
VS Code (GitHub Copilot)Documented by the vendor; not yet tested by usA request header, in the server's headers in mcp.json.
OpenAI Responses APIDocumented by the vendor; not yet tested by usA request header, in the mcp tool's headers.
claude.aiOnly organisations with the request-headers betaA request header, where the beta is on.
ChatGPTNeeds OAuth sign-in; not supported yet—

If your client doesn't offer you the PDF from a result, check the document with the preview image, and make the final with the API instead: a final made here uses its credit even if your client drops the file.

Before you start

  • You need an API key from Settings. While you try it, give the assistant a Preview only key: it can check and preview, and can never use a credit. A Full access key can also make final documents.
  • You need a client where you can set a request header, such as Claude Code or Cursor. Every message carries the key in Authorization: Bearer kt_live_….

Connectors that sign in with OAuth can't use it yet. The server takes a key in the Authorization header and offers no OAuth sign-in, so a connector that can only sign in with OAuth, and cannot send a header of your choosing, cannot connect.

Connect a client

The server is at https://kouteisystems.com/api/mcp. It speaks Streamable HTTP and keeps no session: each message stands on its own.

Claude Code

Put your key in an environment variable named KOUTEI_API_KEY, and add the server to a project's .mcp.json. Claude Code expands ${KOUTEI_API_KEY} there from your environment when it connects, so the key itself stays out of the file:

{
  "mcpServers": {
    "koutei-pdf": {
      "type": "http",
      "url": "https://kouteisystems.com/api/mcp",
      "headers": {
        "Authorization": "Bearer ${KOUTEI_API_KEY}"
      }
    }
  }
}

Or add it from the command line:

claude mcp add --transport http koutei-pdf https://kouteisystems.com/api/mcp \
  --header 'Authorization: Bearer ${KOUTEI_API_KEY}'

Keep the single quotes, so your shell does not put the key into the command. Claude Code stores the header as written, ${KOUTEI_API_KEY} included, in your own user config (~/.claude.json). If your Claude Code doesn't expand ${KOUTEI_API_KEY} there, use the .mcp.json form above. Never put the key itself in a project's .mcp.json or add it with --scope project, and never commit it.

Cursor

In .cursor/mcp.json (one project) or ~/.cursor/mcp.json (every project):

{
  "mcpServers": {
    "koutei-pdf": {
      "url": "https://kouteisystems.com/api/mcp",
      "headers": {
        "Authorization": "Bearer ${env:KOUTEI_API_KEY}"
      }
    }
  }
}

Another client

Give it the address above, the Streamable HTTP transport and the header Authorization: Bearer kt_live_…, with your whole key after Bearer.

The tools

The server offers 3 tools. Ask the assistant to start with the first.

list_templates

The document templates this server can make (invoice, quote and receipt), each with its version, its paper sizes and the names of its required and optional fields. Costs nothing.

Inputs of list_templates
InputNeededTypeWhat it is
categoryNostringOnly templates in this category, for example "invoices". Leave it out (or null) for all of them.

get_template

One template in full: the JSON Schema of its fields, its cross-field rules, a complete example request, and what the document is and is not. Read it before generate_document. Costs nothing.

Inputs of get_template
InputNeededTypeWhat it is
templateYesstringA template slug from list_templates: invoice, quote or receipt.

generate_document

Make an invoice, a quote or a receipt as a PDF from the facts in fields, and make a free preview first. mode is required and nothing is assumed: "validate" checks the request and makes nothing; "preview" makes a watermarked preview that is not a valid document, costs nothing and returns page 1 as an image; "final" makes the real document and uses 1 credit. When a person is in the conversation, show them a preview and get their approval before "final". Never invent a fact: names, tax numbers, bank details, amounts, dates and document numbers must come from the person or their data; ask when one is missing. Every answer that is not an error includes totals: the subtotal, discount, tax, total, amount paid and balance due the document prints, as decimal strings in its currency. Read the numbers from there instead of working them out. On an error, error.details lists every problem: fix those fields and call "validate" again. An identical final repeated in the following 24 hours returns the same document and is not charged again. Nothing is sent to anyone, no document number is allocated or checked, and no document is asserted to be tax-compliant, an e-invoice or legally valid.

Inputs of generate_document
InputNeededTypeWhat it is
templateYesstringA template slug from list_templates: invoice, quote or receipt.
fieldsYesobjectThe document's facts for this template. get_template returns their JSON Schema and an example.
modeYes"validate" | "preview" | "final"Required. "validate": check only. "preview": free and watermarked. "final": the real document, 1 credit.
template_versionNostringOptional. Pins the template's major version, as a string of digits such as "2".
referenceNostringOptional. Your own label for this document, such as a job number. Never printed; stored with the document's record for 90 days. Never personal data.
paperNo"a4" | "letter"Optional. "a4" (the default) or "letter".
localeNo"en"Optional. Only "en".
idempotency_keyNostringOptional. Your own key for a safe retry (the REST Idempotency-Key), sent as it is, not in double quotes: the same key with the same request returns the same document, uncharged, for 24 hours. Without it, a final still gets one, derived from your API key and the request, so an identical final is charged once.
include_pdfNobooleanOptional, for mode "preview": also return the watermarked preview PDF. A final always returns its PDF; true is refused with any other mode.

The fields of each template are the API's: get_template returns them, and the API reference lists them.

Modes and costs

generate_document needs mode. Nothing is assumed: a request never uses a credit unless it says final.

Modes of generate_document
ModeWhat it doesCreditsKey needs
validateChecks the request exactly as a document request is checked, and makes nothing.0any key
previewMakes a watermarked preview that is not a valid document, and returns page 1 as an image.0documents:preview
finalMakes the real document and returns the PDF.1documents:create

A final's credit is taken only after the PDF has been made. The template tools, a check and a preview cost nothing, and so does every error.

What a result holds

  • A preview or a final returns a receipt with document, filename, credit_used, credits_remaining, idempotent_replay and totals, as JSON in the result and as its first text block.
  • A preview adds page 1 as an image (image/png, at most 1,000,000 bytes). With include_pdf set to true it also returns the watermarked PDF.
  • A final adds the PDF as an embedded resource (application/pdf, base64, at most 2,000,000 bytes before base64). Its uri, such as koutei://documents/doc_….pdf, is a label: the server offers no resources to fetch, and Koutei keeps no copy, so save the PDF from the result.
  • Every result's _meta carries com.kouteisystems/status (the HTTP status the API would have answered with) and com.kouteisystems/request-id.

Retries

An identical final repeated within 24 hours returns the same document and is not charged again, even if the assistant sends no idempotency_key: the server makes one from your API key and the request. Send your own idempotency_key to choose it; it works like the API's Idempotency-Key, and the same key with a different request is refused with 422 idempotency_key_reused.

Errors and limits

  • A refused call is a tool result with isError: true. Its JSON is the API's error envelope, the same code, details and credit_used. Errors explains each code. When the API would have said wait, com.kouteisystems/retry-after-seconds gives the seconds.
  • The API's limits apply. Each tool call counts toward the key's 120 requests a minute; previews share the account's preview limits with the API and the web app. The API reference lists them.
  • Repeating an identical final is free, but limited: a key may replay 60 final documents a minute. Over that, the call is refused with 429 rate_limited and the detail replay_limit; nothing is charged, and the same call replays once the wait is over.
  • With no credits left, a final is refused with 402 insufficient_credits.

What the server tells the assistant

These are the server's instructions to the assistant, word for word:

  • Koutei PDF makes invoice, quote and receipt PDFs from facts you send. Start with list_templates, read get_template for the fields, then generate_document.
  • Human review: when a person is in the conversation, make a "preview", show it and get their approval before "final". A preview is watermarked and is never a valid document: never present one as final.
  • No invented facts: the seller, the client, tax numbers, bank details, amounts, dates and document numbers must come from the person or their data. If a required value is unknown, ask.
  • No sending: this server has no email or delivery tools and never contacts anyone. Delivering the document is the person's own action.
  • No legal or tax claims: a document is not asserted to be tax-compliant, an e-invoice or legally valid.
  • Credits: only "final" uses a credit (1). "validate", "preview", a repeat of an identical final in the following 24 hours, and every error cost nothing; each result states credit_used.
  • Totals: every validate, preview and final result states totals, the figures the document prints (subtotal, discount, tax, total, amount paid, balance due). Quote them from there; do not recompute them.
  • Errors: read error.details, fix the named fields and call "validate" again.

The same rules are in the description of generate_document, for clients that don't show instructions.