Skip to main content
Glama
README.md
# 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

A3.8/5.0

Scored across 11 tools

Disambiguation3/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness4/5

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.

Maintenance

ActivityInactive
ResponsivenessNo issues