jcs-mcp
# jcs-mcp
An [MCP](https://modelcontextprotocol.io/) server that exposes the **Joaquim Chaves Saúde (JCS)** patient portal (`webapp.jcs.pt`) as tools for Claude and other MCP clients.
It lets you ask Claude things like:
- *"Show me my upcoming appointments"*
- *"What prescriptions do I have active?"*
- *"Download my latest exam result and summarise it"*
- *"List my invoices from the past 6 months"*
## Features
11 MCP tools covering the full JCS portal:
| Tool | Description |
|------|-------------|
| `jcs_login(password)` | Authenticate; session saved to `~/.jcs_session.json` (auto-renewed) |
| `get_patient_info()` | Name, email, patient ID |
| `list_timeline(rows_to_skip?, archived?)` | Main health feed — exam results, appointment confirmations, documents |
| `list_notifications(rows_to_skip?, archived?)` | Notifications and messages |
| `get_message(id)` | Full message detail; body contains signed document URIs |
| `list_appointments()` | Upcoming and recent appointments from the calendar |
| `list_prescriptions(include_expired?)` | Active (and optionally expired) prescriptions |
| `list_invoices()` | Invoices (Faturas/Recibos) from the patient profile |
| `get_document_content(uri)` | Fetch HTML exam result content from a signed URI |
| `download_document(uri, filename?)` | Download a PDF document, saved to `./downloads/jcs/documentos/` |
| `parse_prescription(file_path, model?)` | Parse a prescription PDF with a local [Ollama](https://ollama.com/) model |
### Session management
Authentication uses OAuth2 Resource Owner Password Grant against the GatewayBox platform. After the first `jcs_login()` call the session is persisted to `~/.jcs_session.json` and reloaded automatically on subsequent calls. If `JCS_PASSWORD` is set in `.env`, the server will auto-login when the session expires — no manual intervention needed.
### Prescription parsing
`parse_prescription` extracts structured data from a downloaded prescription PDF using a local Ollama model:
```json
{
"patient": "...",
"date": "YYYY-MM-DD",
"doctor": "...",
"specialty": "...",
"medications": [
{
"name": "...", "dci": "...", "strength": "...",
"form": "...", "quantity": "...", "posology": "...", "duration": "..."
}
],
"prescription_number": "...",
"notes": "..."
}
```
For image-based PDFs it falls back to vision mode automatically (use a vision-capable model like `llava`).
## Requirements
- Python 3.10+
- [uv](https://github.com/astral-sh/uv)
- A JCS account at [webapp.jcs.pt](https://webapp.jcs.pt)
- Your device UUID and device token key (see [Auth setup](#auth-setup) below)
- [Ollama](https://ollama.com/) (optional, only needed for `parse_prescription`)
## Installation
```bash
git clone https://github.com/nathanfolkman/jcs-mcp.git
cd jcs-mcp
uv sync
```
## Configuration
Copy `.env.example` to `.env` and fill in your credentials:
```bash
cp .env.example .env
```
```dotenv
# Required
JCS_PHONE_NUMBER=+351912345678
# Optional — if set, the server will auto-login when the session expires
JCS_PASSWORD=your_password
# Required for first login (see Auth setup below)
JCS_DEVICE_UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
JCS_DEVICE_DTK=your_device_token_key
# Optional — where to save downloaded documents (default: ./downloads)
OUTPUT_DIR=./downloads
```
### Auth setup
The JCS API uses a GatewayBox device registration system. Your script must present a **device UUID** and **device token key (dtk)** that are already registered with the server — unrecognised devices are rejected.
The easiest way to obtain these is from your browser session on `webapp.jcs.pt`:
1. Open Chrome DevTools → Application → Local Storage → `https://webapp.jcs.pt`
2. Find the key that contains your `appUuid` — this is your `JCS_DEVICE_UUID`
3. Open DevTools → Network, log in normally, and look for the `POST /api/device/token` response — the `deviceToken` field is your `JCS_DEVICE_DTK`
Set both values in `.env`. After the first successful `jcs_login()` call they are also persisted to `~/.jcs_session.json`.
## Usage
### With Claude Code
Add to your MCP settings (e.g. `~/.claude.json`):
```json
{
"mcpServers": {
"jcs-health": {
"command": "uv",
"args": ["run", "--directory", "/path/to/jcs-mcp", "python", "mcp_server.py"]
}
}
}
```
### MCP Inspector (development)
```bash
uv run mcp dev mcp_server.py
```
### Standalone
```bash
uv run python mcp_server.py
```
### First-time login
If `JCS_PASSWORD` is not set in `.env`, call the login tool explicitly once:
```
jcs_login(password="your_password")
```
The session is saved and reused automatically until it expires (~24 hours).
## Project structure
```
jcs-mcp/
├── jcs_client.py # Async HTTP client (GatewayBox OAuth2 + all API methods)
├── mcp_server.py # FastMCP server — 11 tools
├── pyproject.toml # Dependencies
└── .env # Credentials (not committed)
```
## API notes
The JCS webapp is a single-page application built on the [GatewayBox](https://www.seamlink.pt/) platform by Seamlink. All API endpoints were reverse-engineered from network traffic. Key facts:
- Base URL: `https://webapp.jcs.pt`
- Auth: OAuth2 password grant via `POST /Token` with `uid`, `AppUuid`, and `dtk` custom headers
- All data endpoints use `POST` with a JSON body containing `device` (the appUuid)
- Document URIs are signed, time-limited tokens embedded in message body HTML as `data-attach-html` attributes
- HTML exam results: `GET /api/attachdata/getcontent?uri=...`
- PDF documents: `GET /api/attachdata/getfile?uri=...`
## License
MIT
TDQS
Scored across 11 tools
Most tools are clearly distinct, but list_timeline and list_notifications overlap significantly—both return lists of messages with similar arguments and point to get_message for details. Additionally, get_document_content and download_document both handle attachment URIs, which could cause misselection without careful reading.
The naming pattern is predominantly verb_noun (get_patient_info, list_appointments, download_document, parse_prescription), which is consistent and predictable. The outlier is jcs_login, which breaks the pattern by using a product prefix and lacking a clear object, introducing minor inconsistency.
With 11 tools, the server is well-scoped for a healthcare portal client. It covers authentication, data retrieval, document access, and a niche parsing feature without being bloated or too thin. Each tool serves a distinct purpose in the overall workflow.
The tool surface provides a complete read-only lifecycle for the domain: listing and getting messages, appointments, prescriptions, invoices, and documents. Minor gaps exist, such as no ability to search, mark-as-read, or perform actions like scheduling appointments, but these are not core to the apparent purpose.