Skip to main content
Glama
README.md
<div align="center">

# Docva — Canva Education Document Generator

**Headless MCP server** that generates teacher verification documents — employment letters, teacher ID cards, teaching licenses, payslips, and more — across **13 countries**.

[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
[![MCP](https://img.shields.io/badge/MCP-stdio-brightgreen.svg)](https://modelcontextprotocol.io)
![Platform](https://img.shields.io/badge/platform-win%20%7C%20linux%20%7C%20macOS-lightgrey.svg)

*Portable, self-contained, and installable anywhere.*

</div>

---

## Table of Contents

- [Sample outputs](#sample-outputs)
- [Features](#features)
- [Supported countries](#supported-countries)
- [Requirements](#requirements)
- [Installation](#installation)
- [Usage — MCP server](#usage--mcp-server)
- [Legacy GUI](#legacy-gui)
- [Project structure](#project-structure)
- [Adding a new country](#adding-a-new-country)
- [License](#license)

---

## Sample outputs

Documents are rendered as high-resolution PNGs. Examples generated by this tool:

| Teacher ID (US) | Employment letter (US) |
|:---:|:---:|
| ![US teacher ID](docs/examples/example_us_teacher_id.png) | ![US letter](docs/examples/example_us_letter.png) |

| Teacher ID (UK) | Employment letter (UK) |
|:---:|:---:|
| ![UK teacher ID](docs/examples/example_uk_teacher_id.png) | ![UK letter](docs/examples/example_uk_letter.png) |

---

## Features

- **Headless MCP server** — document generation exposed as agent-callable tools over stdio.
- **13 countries**, each with its own document types and local conventions.
- **Real school databases** with street addresses, districts, and contact info.
- **Consistent profile photos** per person — hash-based selection from a bundled, gender-aware photo pool.
- **Cross-platform fonts** — DejaVu Sans bundled; no system-font dependency.
- **Packaged & portable** — ships as a self-contained wheel (code + photos + fonts) installable with one command.

---

## Supported countries

| Code | Country | Document types |
|------|---------|----------------|
| `uk` | United Kingdom | employment_letter, teacher_id, teaching_license |
| `us` | United States | employment_letter, teacher_id, teaching_license |
| `france` | France | installation_statement, iprof_screenshot, bylaws_extract, teaching_certificate |
| `netherlands` | Netherlands | employment_contract, teacher_registration, duo_declaration, school_id |
| `indonesia` | Indonesia | payslip, teaching_experience_letter, nuptk_card, appointment_letter |
| `australia` | Australia | signed_school_letter, school_id, teaching_license |
| `canada` | Canada | oct_card, teaching_license, signed_school_letter |
| `spain` | Spain | teaching_id, signed_school_letter, employment_contract |
| `argentina` | Argentina | payslip, employment_certificate, signed_school_letter |
| `slovakia` | Slovakia | payslip, employment_letter, signed_school_letter |
| `mexico` | Mexico | teaching_id, signed_school_letter, employment_certificate |
| `philippines` | Philippines | teaching_id, employment_certificate, teaching_license |
| `thailand` | Thailand | payslip, letter_of_employment |

---

## Requirements

- Python **3.10+**
- Dependencies (installed automatically): `Pillow`, `mcp`

---

## Installation

### From the built wheel

```bash
pip install dist/docva_doc_generator-0.1.0-py3-none-any.whl
```

### From source (editable)

```bash
pip install -e .
```

### Via uv

```bash
uvx --from . docva-mcp
```

---

## Usage — MCP server

The server speaks **MCP over stdio** — the transport used by most agent runtimes (Hermes, Claude Desktop, and any MCP client). Connect it, discover the tools, then call them.

### Step 1 — Install & verify

```bash
# from the built wheel
pip install dist/docva_doc_generator-0.1.0-py3-none-any.whl

# or editable from source
pip install -e .
```

Verify the install and that bundled assets resolve:

```bash
python -c "from countries.utils import load_font, get_profile_photo; \
print(load_font(30).getname()); print(get_profile_photo((280,340), person_id='x', gender='Male') is not None)"
# ('DejaVu Sans', 'Book')   <-- bundled font, not system
# True                      <-- bundled photo found
```

### Step 2 — Run the server

```bash
# After install:
docva-mcp

# Or from source:
python mcp_server.py
```

It blocks and waits for MCP requests over stdin/stdout — don't run it as a foreground terminal app expecting prompts.

### Step 3 — Register in your agent runtime

Point your MCP client at the `docva-mcp` command:

```json
{
  "mcpServers": {
    "docva": {
      "command": "docva-mcp",
      "args": []
    }
  }
}
```

If `docva-mcp` isn't on your `PATH`, use the absolute path to your interpreter and module instead:

```json
{
  "mcpServers": {
    "docva": {
      "command": "/path/to/python",
      "args": ["-m", "mcp_server"]
    }
  }
}
```

### Tools

| Tool | Description |
|------|-------------|
| `list_countries_tool` | List available countries, display names, and their document types. |
| `list_schools(country)` | List all schools for a country code. |
| `generate_documents(...)` | Render one or more documents to PNG and return their paths. |

#### `list_countries_tool()`

No arguments. Returns one result item per country — `{ code, name, document_types }`. (Because a list return is split into one MCP content item per entry, iterate `content` to see them all.)

#### `list_schools(country: str)`

- `country` *(required)* — country code from `list_countries_tool` (e.g. `"us"`).
- Returns one result item per school — `{ name, address, town, postcode, state, phone, lea }`. Iterate `content` to see them all.

#### `generate_documents(...)`

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `country` | string | ✅ | — | Country code (e.g. `"us"`, `"uk"`). |
| `first_name` | string | ✅ | — | Teacher's first name. |
| `last_name` | string | ✅ | — | Teacher's last name. |
| `school_name` | string | ✅ | — | Exact or partial school name (matched against that country's school list). |
| `position` | string | ✅ | — | Teaching position/title. |
| `date_of_birth` | string | ✅ | — | DOB string, printed on the teacher ID (e.g. `"12/05/1988"`). |
| `gender` | string | — | `"Random"` | `"Random"`, `"Male"`, or `"Female"` — selects the profile-photo pool. |
| `document_types` | string[] | — | all types | Which documents to render, e.g. `["employment_letter", "teacher_id"]`. |
| `output_dir` | string | — | `output/` | Where to save PNGs (relative to the server's working dir). |

Returns `{ country, school, document_types, files, count, output_dir }` — `files` are absolute PNG paths.

### Connect from a Python client

Minimal working client (requires `pip install mcp`):

```python
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    params = StdioServerParameters(command="docva-mcp", args=[])
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()

            countries = await session.call_tool("list_countries_tool", {})
            # A list return is split into one content item per entry:
            for item in countries.content:
                print(item.text)

            res = await session.call_tool("generate_documents", {
                "country": "us",
                "first_name": "John",
                "last_name": "Smith",
                "school_name": "Valley High",
                "position": "Head of Science Department",
                "date_of_birth": "12/05/1988",
                "gender": "Male",
            })
            print(res.content[0].text)

asyncio.run(main())
```

### Typical agent workflow

1. Call `list_countries_tool` to see what's available.
2. Call `list_schools("us")` to pick a real school.
3. Call `generate_documents(...)` with the chosen country, school, and person details.
4. Read the returned PNG paths and use the files.

Generated PNGs are written to `output/` (or the `output_dir` you pass).

---

## Legacy GUI

A tkinter (CustomTkinter) GUI is still available for manual use. The core generation logic is shared.

```bash
python main_gui.py        # on Windows, use run.bat (sets TCL_LIBRARY)
```

> The MCP server is the primary, headless interface. The GUI is optional and not required for the skill.

---

## Project structure

```
docva/
├── countries/            # Document generation core (package)
│   ├── base.py           # CountryGenerator ABC (contract)
│   ├── utils.py          # Fonts, profile photos, shared helpers
│   ├── foto/             # Bundled profile photos (package data)
│   ├── fonts/            # Bundled DejaVu fonts (package data)
│   └── <country>/        # One package per country
├── mcp_server.py         # MCP server exposing tools
├── main_gui.py           # Legacy tkinter GUI
├── docs/examples/        # Sample rendered documents
├── pyproject.toml        # Packaging, deps, entry point
├── output/               # Generated documents (git-ignored)
└── run.bat               # Windows GUI launcher
```

---

## Adding a new country

1. Create `countries/<code>/__init__.py` with a class inheriting `countries.base.CountryGenerator`.
2. Implement the abstract methods: `get_country_name`, `get_country_code`, `get_schools_data`, `get_first_names`, `get_last_names`, `get_positions`, `get_document_types`, `generate_document`.
3. Register it in `countries/__init__.py` via `register_country("<code>", <Name>Generator)`.
4. Optionally add a display label in `main_gui.py` (`get_country_list` / `on_country_change`).

The new country is automatically picked up by the MCP `list_countries_tool` and `list_schools`.

---

## License

[MIT](./LICENSE)

TDQS

A4.3/5.0

Scored across 3 tools

Disambiguation5/5

Each tool serves a clearly distinct purpose: listing countries, listing schools, and generating documents. There is no overlap or ambiguity between the three tools.

Naming Consistency4/5

Two tools follow a list_ prefix pattern and generate_documents uses verb_noun style, which is mostly consistent. The '_tool' suffix on list_countries_tool is a minor deviation but does not cause confusion.

Tool Count5/5

Three tools is a well-scoped set for the server's purpose: discovery (countries/schools) and document generation. Each tool earns its place with no unnecessary bloat.

Completeness5/5

The tool surface covers the full workflow: listing supported countries and document types, retrieving schools for a country, and generating the requested teacher documents. There are no obvious dead ends or missing operations.

Maintenance

ActivityMaintained
ResponsivenessNo issues