Skip to main content
Glama

AetioReferral: Synthetic Medical Referral Letter Generator

AetioReferral is an open-source Python MCP server and CLI that generates realistic synthetic medical referral letters as PDF: a German Überweisungsbrief (Arztbrief) from a general practitioner to a specialist, or the same letter in any other language with a locale file. It is built for medical education, virtual-patient platforms and document-AI datasets.

Build Python 3.13 MCP server License: MIT

Table of Contents

  1. What is AetioReferral?

  2. Example Letter

  3. Key Features

  4. Use Cases

  5. How AetioReferral Compares

  6. Quick Start

  7. Use with Claude and Other MCP Clients

  8. Letter Input

  9. Languages

  10. FAQ

  11. Related Projects

  12. License

  13. Developer Guide

Related MCP server: PDFMakerAPI MCP Server

What is AetioReferral?

AetioReferral turns letter content into a finished, realistic referral letter PDF. The caller supplies the clinical content, such as the reason for referral, history, findings and medication. AetioReferral adds everything around it: letterhead, recipient address, patient line, salutation, closing, handwritten-style signature and practice stamp, laid out as a DIN 5008 business letter.

There are two ways to use it:

  • Render: pass the letter content as structured JSON. No LLM is needed, and the result is deterministic.

  • Generate: pass a free-text description such as "GP refers a 67-year-old man with exertional chest tightness to cardiology". A configured LLM writes the letter content the way a real referring doctor would, then the letter is rendered.

Patient, doctor and practice details that the caller leaves out are generated with Faker, so every letter is fictional. AetioReferral is not for clinical use.

Example Letter

First page of a synthetic German referral letter (Überweisungsbrief) to gastroenterology generated by AetioReferral, with letterhead, urgent marker, Verdachtsdiagnose, Anamnese, Befunde and Medikation

Rendered from examples/letter.json with --seed 7. Every name, address and identifier on it is generated.

Key Features

  • Realistic letter layout: DIN 5008 Form B with letterhead, address window, fold marks, patient line, urgent marker, signature in a handwriting font and a practice stamp with BSNR and LANR.

  • Real letter sections: Verdachtsdiagnose (suspected diagnosis), Diagnosen, Fragestellung (reason for referral), Anamnese, Befunde, Bisherige Therapie, Medikation, Allergien, free extra sections and Anlagen (enclosures). Empty sections leave no trace.

  • Field-by-field identity filling: anything you give is kept, anything missing is generated. Send "patient": null for a letter without patient details.

  • Reproducible: the same input and seed always produce the same letter, and every result returns its seed.

  • Language-agnostic: every label and phrase the service adds lives in one locale file per language. German and English are included.

  • Optional LLM: Anthropic, OpenAI, Google Gemini, OpenRouter, Ollama or any other pydantic-ai provider, configured by environment variables or a .env file. Without an LLM, the generate path does not exist at all.

  • Two interfaces: an MCP server (stdio or streamable HTTP) for LLM clients and agents, and a command-line tool for scripts.

Use Cases

  • Medical education: give students a referral letter as part of a virtual patient case, as in the sister project AetioMed.

  • Document AI and OCR datasets: produce many varied, realistic clinical letters with known ground truth for training and testing extraction models, without touching real patient data.

  • Testing clinical software: feed realistic letters into document-management, inbox or parsing pipelines.

  • Agents and chat clients: let Claude or another MCP client create a letter during a conversation.

How AetioReferral Compares

Approach

Realistic letter layout

Structured, reproducible output

Real patient data needed

Writes clinical content

AetioReferral

Yes, DIN 5008 PDF with signature and stamp

Yes, JSON in, PDF and filled JSON out, seeded

No

Optional, via LLM

Prompting an LLM directly

No, plain text

No

No

Yes

Word or HTML templates

Depends on the template

Partly

No

No

De-identified real letters

Yes

No

Yes, plus legal review

Not applicable

Quick Start

Requirements: Python 3.13 and uv.

git clone https://github.com/janthoXO/AetioReferral.git
cd AetioReferral
uv sync

Render the example letter:

uv run aetioreferral render examples/letter.json -o letter.pdf

Render from stdin with a fixed seed:

echo '{"recipient_specialty": "Kardiologie", "reason": "Abklärung einer Belastungsdyspnoe"}' \
  | uv run aetioreferral render - -o letter.pdf --seed 42

To write letters from free text, configure an LLM. Copy .env.example to .env and fill in a provider, model and API key, then:

uv run aetioreferral generate "Hausärztin überweist eine 54-jährige Patientin mit Oberbauchschmerzen dringend zur ÖGD" -o letter.pdf

Run uv run aetioreferral --help for all commands.

Use with Claude and Other MCP Clients

AetioReferral speaks the Model Context Protocol. Add it to Claude Code:

claude mcp add aetioreferral -- uv run --directory /path/to/AetioReferral aetioreferral mcp

Or to any client that reads an mcpServers config, such as Claude Desktop:

{
  "mcpServers": {
    "aetioreferral": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/AetioReferral", "aetioreferral", "mcp"]
    }
  }
}

For a networked deployment, run uv run aetioreferral mcp --http and connect to http://127.0.0.1:8000/mcp. The HTTP transport has no authentication yet, so keep it on localhost or a private network.

Tools:

  • render_referral_letter(letter, seed?): always available.

  • generate_referral_letter(description, language?, seed?): only when an LLM is configured.

Both return the PDF as an embedded resource (application/pdf) and the completed letter with its seed as structured content.

Letter Input

The input is a LetterSpec JSON object. Only recipient_specialty and reason are required; every section is plain text.

{
  "recipient_specialty": "Gastroenterologie",
  "reason": "Bitte um Ösophagogastroduodenoskopie und Mitbeurteilung.",
  "suspected_diagnosis": "Ulcus ventriculi",
  "history": "Seit zwei Monaten postprandiale Oberbauchbeschwerden, Gewichtsverlust von 4 kg.",
  "medication": "Ramipril 5 mg 1-0-0\nIbuprofen 600 mg bei Bedarf",
  "patient": {"sex": "f", "age": 54}
}

The patient has three states:

  • Omitted or {}: the whole patient is generated.

  • Partly filled: given fields are kept and the rest is generated.

  • null: the letter has no patient block.

Sender and recipient work the same way, except that they cannot be set to null. See examples/letter.json for every field and the Developer Guide for the full reference.

Languages

language takes a BCP 47 tag; de is the default and en is included. Region tags fall back to their base language, so de-AT uses the German locale. Content you write is never translated: the locale only supplies what the service adds, such as headings, salutation, closing, date format and the kind of generated names and identifiers. To add a language, copy src/aetioreferral/locales/de.toml and translate it; no code changes are needed.

FAQ

What is AetioReferral?

AetioReferral is an open-source Python tool that generates synthetic medical referral letters as PDF. It runs as an MCP server and as a CLI, takes structured JSON or a free-text description, and lays the letter out like a real German Überweisungsbrief.

What is an Überweisungsbrief?

An Überweisungsbrief is the letter a German doctor, usually a general practitioner (Hausarzt), writes when referring a patient to a specialist (Facharzt). It names the patient, states the reason for referral (Fragestellung) and summarises history, findings, suspected diagnosis and medication.

Does AetioReferral use real patient data?

No. Every patient, doctor, practice, address and identifier is either supplied by the caller or generated with Faker. The output is fictional and must not be used as a medical document.

Do I need an LLM?

No. The render path takes structured JSON and needs no LLM. An LLM is only needed to write letter content from a free-text description.

Which LLMs does AetioReferral support?

Any provider that pydantic-ai supports, set with LLM_PROVIDER. Anthropic, OpenAI, Google Gemini, OpenRouter and Ollama work out of the box. Small local models can struggle with the letter schema; mid-size models such as gemma4:12b and cloud models work well.

Which languages does AetioReferral support?

German and English are included. Any language can be added with one TOML locale file.

How do I reproduce a letter?

Pass the same input and the seed from the earlier result (--seed on the CLI, seed in MCP). The seed is printed by the CLI and returned by every MCP call.

How does AetioReferral relate to AetioMed?

AetioMed generates synthetic patient cases for medical education. AetioReferral renders the referral letter that such a case can start with. The Developer Guide describes the integration.

  • AetioMed: LLM-based synthetic medical case generator for medical education.

License

AetioReferral is released under the MIT License. The bundled fonts Carlito and Herr Von Muellerhoff are under the SIL Open Font License; see src/aetioreferral/template/fonts/.

Developer Guide

Architecture, the full letter model, rendering, localisation, the LLM drafter, MCP and CLI details, testing and the AetioMed integration are in README-DEV.md.

Available Tools

1 tool
render_referral_letterA

Render a referral letter from a referring doctor to a specialist as a PDF. Write the letter content like a real referral letter: every section is plain text in the letter language, and empty sections are left out. Leave patient, sender and recipient fields empty to have them generated; set patient to null for a letter without patient details. Salutation, opening sentence, closing, signature and stamp are added automatically. Returns the PDF as an embedded resource and the completed letter (with generated fields) plus the seed as structured content.

ParametersJSON Schema
NameRequiredDescriptionDefault
seedNo
letterYes

TDQS

A4.3/5.0
Behavior4/5

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

With no annotations, the description carries the full burden and does disclose non-obvious behavior: salutation, opening sentence, closing, signature and stamp are added automatically, missing fields are generated, and caller content is never translated. It omits failure modes, validation errors, and any limits on letter size or rendering, which keeps it short of a 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?

Five tight sentences, front-loaded with the purpose before the field-filling rules and the return shape. It borders on dense but every sentence carries information an agent needs; nothing is padding.

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

Completeness4/5

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

With no output schema and no annotations, the description steps in to describe the return payload (PDF as an embedded resource plus the completed letter and seed as structured content), which is exactly the missing piece. It is not exhaustive about error conditions, but it is complete enough to invoke correctly.

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?

The reported 0% coverage is a top-level artifact: the sole top-level parameter is a $ref whose nested $defs document nearly every field, so the baseline is high. The description still adds value by summarizing the generate-if-missing and null semantics and by explaining that empty sections are omitted, though it does not touch the required fields (reason, recipient_specialty) or the language/seed parameters.

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 first sentence states a specific verb and resource ('Render a referral letter ... as a PDF') with source and target roles made explicit (referring doctor to specialist). There are no sibling tools to disambiguate from, and the opening clause leaves no doubt about what the tool produces.

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

Usage Guidelines4/5

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

The description gives concrete calling guidance: leave patient/sender/recipient empty to have them generated, set patient to null for a letter with no patient block, and write sections as plain text in the letter language. It does not discuss alternatives or exclusions, but none exist in this toolset, so the gap is minor.

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

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 1 tool updatev0.1.0
    • First observedrender_referral_letter

TDQS

A4.2/5.0

Scored across 1 tool

Disambiguation5/5

With a single tool there is no possibility of selection confusion; the one tool's purpose (rendering a referral letter PDF) is unambiguous and clearly stated.

Naming Consistency5/5

The lone name render_referral_letter follows a clean verb_noun snake_case convention, so there is no inconsistency to penalize.

Tool Count3/5

A one-tool server is borderline thin; while the tool is non-trivial and covers a full render pipeline, the surface offers no companion operations for a domain that plausibly needs more than a single call.

Completeness3/5

The core action of producing a referral letter PDF is covered, but there is no way to list, retrieve, validate, or re-render existing letters, leaving agents without lifecycle operations around the generated output.

Maintenance

ActivityMaintained
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Enables creating PDF documents (invoices, certificates, reports, etc.) from natural language descriptions, returning a shareable link to preview and edit the document.
    1
    15 npm
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Local MCP server that generates print-ready PDF letters with DIN 5008 compliant address positioning for window envelopes, handling structured content offline without external APIs.
    7
    17 npm
    MIT