AetioReferral MCP Server
Enables the generate mode to use Google Gemini models as the configured LLM, writing referral letter content from a free-text clinical description before rendering the completed letter as a PDF.
Enables the generate mode to use local Ollama models as the configured LLM, writing referral letter content from a free-text clinical description before rendering the completed letter as a PDF.
Enables the generate mode to use OpenAI models as the configured LLM, writing referral letter content from a free-text clinical description before rendering the completed letter as a PDF.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@AetioReferral MCP ServerCreate a German Überweisungsbrief PDF for a 67-year-old man with chest pain to cardiology."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.
Table of Contents
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

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": nullfor 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
.envfile. 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 syncRender the example letter:
uv run aetioreferral render examples/letter.json -o letter.pdfRender from stdin with a fixed seed:
echo '{"recipient_specialty": "Kardiologie", "reason": "Abklärung einer Belastungsdyspnoe"}' \
| uv run aetioreferral render - -o letter.pdf --seed 42To 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.pdfRun 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 mcpOr 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.
Related Projects
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 toolrender_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.
| Name | Required | Description | Default |
|---|---|---|---|
| seed | No | ||
| letter | Yes |
TDQS
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.
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.
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.
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.
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.
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 tool update
v0.1.0- First observed
render_referral_letter
TDQS
Scored across 1 tool
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.
The lone name render_referral_letter follows a clean verb_noun snake_case convention, so there is no inconsistency to penalize.
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.
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
Related MCP Connectors
Turn a description into a shareable, editable PDF — invoices, certificates, reports, resumes.
PDFs, images and e-invoices (XRechnung, ZUGFeRD) from templates and JSON, rendered in the EU.
Generate invoice and receipt PDFs from JSON. Agent-callable document API with a free demo quota.
The agentic layer of letters. Agents send real printed mail worldwide, German compliance built in.
Related MCP Servers
AlicenseAqualityCmaintenancePDF/DOCX/Excel generation from HTML/Markdown with stationery overlay, ZUGFeRD/XRechnung e-invoicing, digital signing, form filling, and AI receipt OCR with DATEV/SKR03 export.40MIT- AlicenseAqualityCmaintenanceEnables creating PDF documents (invoices, certificates, reports, etc.) from natural language descriptions, returning a shareable link to preview and edit the document.115 npmMIT
- AlicenseAqualityDmaintenanceLocal MCP server that generates print-ready PDF letters with DIN 5008 compliant address positioning for window envelopes, handling structured content offline without external APIs.717 npmMIT
- AlicenseNot gradedqualityDmaintenanceGenerates DOCX letters on your organization's letterhead via MCP, returning the document as an attachment. Supports custom letterhead templates and an office registry for multi-office organizations.MIT