# The openEHR MCP Server

> **Status: retired.** This repository is no longer maintained. The code is outdated and should not be used for new work. Issues and pull requests are not monitored.

The MCP server that connects AI assistants and agents to [openEHR](https://openehr.org/) REST APIs.

The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) is an open standard that enables AI agents to connect to external data sources and tools in a secure and standardized way. MCP servers act as bridges between AI clients (like Claude Desktop, Cursor, or LibreChat) and domain-specific APIs and data stores.

The **openEHR MCP Server** exposes tools for openEHR Templates, EHR, Compositions, stored queries, and AQL execution, following the [openEHR REST](https://specifications.openehr.org/releases/ITS-REST/development/overview.html) specification. It is designed to work alongside the [openEHR Assistant MCP Server](https://github.com/Cadasto/openehr-assistant-mcp): use the Assistant for modelling, archetypes, and CKM; use this server for EHR and composition operations against an openEHR REST endpoint.

- Optional guided Prompts help orchestrate multi-step workflows (vital signs, assessments, medication review, AQL, template and composition management).

> NOTE:
> For production-grade EHR integrations, ensure your AI model and deployment meet your organization's data privacy and compliance requirements.

> WARNING:
> OAuth2 authorization flows are not supported yet. This server is for now intended for development and testing purposes only. 

## Table of Contents

- [Features](#features)
- [Available MCP Elements](#available-mcp-elements)
- [Transports](#transports)
- [Quick Start](#quick-start)
- [Common client configurations](#common-client-configurations)
- [Development tips](#development-tips)
- [Documentation](#documentation)
- [Contributing](#contributing)
- [Acknowledgments](#acknowledgments)

----

## Features

- Works with MCP clients such as Claude Desktop, Cursor, LibreChat, etc.
- Exposes tools for openEHR Template definition, stored queries, AQL execution, EHR and Composition management.
- Guided Prompts for vital signs capture, patient assessment, medication review, AQL query running, and template/EHR/composition management.
- Run locally via Docker (transports: streamable HTTP and stdio)

### Implementation aspects

- Made with PHP 8.4; PSR-compliant codebase
- Attribute-based MCP tool discovery (via [modelcontextprotocol/php-sdk](https://github.com/modelcontextprotocol/php-sdk))
- Docker images for production and development (PHP-FPM + Caddy ingress in `.docker/`)
- Transports: streamable HTTP (Caddy on ports 80, 443, and 8342 for dev) and stdio
- Structured logging with Monolog
- Simple, environment-driven configuration

----

## Available MCP Elements

### Tools

Template Definition
- `template_list` - List templates (path segment e.g. adl1.4 or adl2). Optional keyword search and maxResults; when keyword is set, results are scored and sorted by relevance.
- `template_get` - Get a template by ID (format: e.g. opt, json, xml)
- `template_upload` - Upload a template (format: e.g. opt, json, xml)
- `template_example_get` - Get an example composition for a template (format: flat, json, xml, canonical, structured)

Stored Query Definition
- `stored_query_upload` - Upload a stored AQL query (optional version)
- `stored_query_list` - List stored queries (name filter supports `namespace::name`). Optional keyword search and maxResults; when keyword is set, results are scored and sorted by relevance.
- `stored_query_get` - Get a stored query definition by name and version

Query Execution
- `query_adhoc` - Execute AQL (supports offset/fetch)
- `stored_query_execute` - Execute stored AQL by name/version with parameters

EHR Management
- `ehr_create` - Create an EHR (optional subject id/namespace)
- `ehr_get` - Get EHR by `ehr_id` or by subject id/namespace (use this instead of the removed `ehr_get_by_subject`)
- `ehr_status_get` - Get `EHR_STATUS` for an EHR
- `ehr_contribution_create` - Create a Contribution for an EHR
- `ehr_contribution_get` - Get a Contribution for an EHR

Composition Management
- `composition_create` - Create a composition (format: json, flat, xml, structured)
- `composition_get` - Get a composition by UID (format: json, flat, xml, structured)
- `composition_update` - Update a composition (If-Match with preceding version)
- `composition_delete` - Delete a composition (versioned delete)
- `composition_revision_history` - Get composition revision history

### Prompts

Optional prompts that guide AI assistants through common openEHR workflows using the tools above.
- `vital_sign_capture` - Capture vital signs: fetch a flat JSON example for a chosen template, populate values (use UCUM units), then create a COMPOSITION.
- `patient_assessment` - General clinical assessment: pick a template, optionally fetch an example, then record observations as a COMPOSITION.
- `medication_review` - Review or update medication lists: select a template, optionally fetch an example, then submit medication changes as a COMPOSITION.
- `composition_create` - Create a new COMPOSITION; template/type inferred from conversation (generic workflow, like vital_signs/medication but template-agnostic).
- `composition_management` - Get, update, delete COMPOSITIONs and view revision history (use `composition_create` for creating).
- `aql_query_runner` - Run AQL queries: ad hoc (supply AQL content) or stored (supply query name/version and parameters); same workflow, different inputs.
- `template_management` - Manage template definitions (list/get/upload) and fetch example compositions.
- `query_management` - Manage stored query definitions (list/get/upload), aligned with template_management.
- `ehr_create` - Create a new EHR for a subject (or without subject) when one does not exist yet.
- `ehr_search` - Find or retrieve an EHR by ehr_id or by subject id and namespace.
- `ehr_management` - Inspect EHR status and manage contributions (create/get); use `ehr_create` or `ehr_search` to create or find an EHR first.

----

## Transports

MCP Transports are used to communicate with MCP clients.

- `streamable-http` (default): Caddy serves the app (ports 80, 443; dev: 8342). Use the URL your client needs (e.g. `http://localhost:8342/` or `https://openehr-server-mcp.local/`).
- `stdio`: Suitable for process-based MCP clients, or for local development.
 - Start option: pass `--transport=stdio` or `--transport=streamable-http` to `public/index.php`.

---

## Quick Start

To get started, use one of the following options:

1. **Local via Docker (production):** run the MCP server with `make up`.
2. **Local via Docker (recommended for contributors):** run the dev container with `make up-dev` and use Makefile targets.
3. **Local via stdio:** run as a process for MCP clients that prefer stdio.

----

### Option 1: Run production server locally

If you want to run the MCP server as a single service (e.g. for LibreChat or another HTTP client).

#### Prerequisites

- Docker + Docker Compose
- Git

#### 1) Clone the repository

```bash
git clone https://github.com/cadasto/openehr-server-mcp.git
cd openehr-server-mcp
```

#### 2) Prepare environment

```bash
make env
```

> Tip: Edit `.env` to set `OPENEHR_API_BASE_URL` (e.g. `http://localhost:8080/ehrbase/rest/openehr`) and other variables. See [Environment Variables](#environment-variables) below.

#### 3) Start the MCP server

```bash
make up
```

Caddy and the app start; Caddy listens on ports 80 (HTTP) and 443 (HTTPS). With the dev compose (`make up-dev`), Caddy also exposes port 8342 for HTTP.

#### 4) Optional: Start EHRbase stack for testing

```bash
make ehrbase-up
```

EHRbase will be at http://localhost:8080.

----

### Option 2: Run locally with Docker (recommended for contributors)

Use this when editing tools/prompts and wanting immediate feedback.

#### Prerequisites

- Docker + Docker Compose
- Git

#### 1) Clone the repository

```bash
git clone https://github.com/cadasto/openehr-server-mcp.git
cd openehr-server-mcp
```

#### 2) Prepare environment

```bash
make env
```

Edit `.env` as needed (see [Environment Variables](#environment-variables)).

#### 3) Start dev container

```bash
make up-dev
```

#### 4) Install Composer dependencies

```bash
make install
```

#### 5) Run the server

- Streamable HTTP: with `make up-dev`, Caddy serves the app (e.g. `http://localhost:8342/`).
- Stdio (one-off): `make run-stdio`.

Or open a shell: `make sh`, then run `php public/index.php --transport=stdio` or `--transport=streamable-http`.

----

### Option 3: Run locally via stdio

Use stdio when your MCP client launches the server process directly.

Make sure your MCP client supports stdio transport and runs one of the following.

#### 1) From dev container

```bash
make run-stdio
```

Or after `make up-dev` and `make sh`: `php public/index.php --transport=stdio`.

#### 2) From a built Docker image

```bash
docker run --rm -i -e OPENEHR_API_BASE_URL=http://host.docker.internal:8080/ehrbase/rest/openehr cadasto/openehr-server-mcp:latest php public/index.php --transport=stdio
```

---

## Common client configurations

### Typical configuration

In most cases, add **one** of the following server configurations:

```json
{
  "mcpServers": {
    "openehr-server-mcp": {
      "type": "streamable-http",
      "url": "http://localhost:8342/"
    },
    "openehr-server-mcp-stdio": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm", "--network=host",
        "-e", "OPENEHR_API_BASE_URL=http://localhost:8080/ehrbase/rest/openehr",
        "cadasto/openehr-server-mcp:latest",
        "php", "public/index.php", "--transport=stdio"
      ]
    }
  }
}
```

### Claude Desktop (`mcpServers`)

Use **Menu** → **Developer** → **Edit Config** and add one of the server configurations above. Adjust `OPENEHR_API_BASE_URL` in the stdio example if your openEHR REST server is not at `localhost:8080`.

### LibreChat (streamable HTTP)

Run the MCP server first (e.g. `make up` or `make up-dev`). Then in your LibreChat MCP config use the Caddy URL (e.g. port 8342 for dev, or 80/443 for prod):

```yaml
mcpServers:
  openehr-server-mcp:
    type: streamable-http
    url: http://host.docker.internal:8342/
```

### Cursor

1. Open **Cursor Settings** → **MCP**.
2. Add a new MCP server.
3. Choose:
 - **Streamable HTTP**: `type=streamable-http`, `url=http://localhost:8342/` (dev) or your Caddy URL (e.g. `http://host.docker.internal:8342/` if Cursor runs in Docker).
 - **Stdio**: use the Docker command from the typical configuration above.

### IntelliJ Junie

1. Open **Settings/Preferences** → **Tools** → **Junie** → **MCP Servers** (wording may vary).
2. Add a server using either the **Streamable HTTP** URL (e.g. `http://localhost:8342/`) or the **Stdio** Docker command above.
3. Save and refresh Junie so tools are discovered.

---

## Development tips

### MCP Inspector

Run the MCP Inspector to inspect requests/responses and debug behavior:

```bash
make inspector
```

Open http://localhost:6274/ in your browser.

### Makefile shortcuts

- Build images: `make build` (prod) or `make build-dev` (dev)
- Start services: `make up` (prod) or `make up-dev` (dev with live volume mounts)
- Prepare `.env`: `make env`
- Install dependencies in dev container: `make install`
- Tail logs: `make logs`
- Open shell in dev container: `make sh`
- Run server: `make run-stdio`; for HTTP, use Caddy after `make up-dev` (e.g. `http://localhost:8342/`)
- Run MCP inspector: `make inspector`
- Show help: `make help`
- Optional EHRbase stack: `make ehrbase-up` / `make ehrbase-down`

### Environment Variables

- `APP_ENV`: application environment (`development`/`production`). Default: `production` (set to `development` for local dev if desired).
- `LOG_LEVEL`: Monolog level (`debug`, `info`, `warning`, `error`, etc.). Default: `info`
- `OPENEHR_API_BASE_URL`: base URL for your openEHR REST server (e.g. EHRbase: `http://localhost:8080/ehrbase/rest/openehr`)
- `HTTP_TIMEOUT`: HTTP client timeout in seconds (float). Default: `10.0`
- `HTTP_SSL_VERIFY`: set to `false` to disable verification or provide a CA bundle path. Default: `true`

> Note: Authorization headers are not configured by default. If your openEHR server requires auth, extend the HTTP client setup in `public/index.php` (e.g. via `OpenehrApi`) to add the appropriate headers.

### Testing and QA

- Unit tests: `make test` (PHPUnit 12)
- Test with coverage: `make test-coverage`
- Static analysis: `make phpstan`

You can also `make sh` and run `composer test` or `composer check:phpstan` inside the container.

### Project Structure

- `public/index.php`: MCP server entry point (stdio and streamable HTTP)
- `src/`
 - `Tools/`: MCP Tools (Definition, EHR, Composition, Query)
 - `Prompts/`: MCP Prompts
 - `Helpers/`: Internal helpers (e.g. `CliOptions`, content type mapping)
 - `Clients/`: Internal API clients (e.g. `OpenehrApi`)
 - `constants.php`: app identity, env, and defaults (`APP_VERSION`, `APP_TITLE`, `APP_DATA_DIR`, etc.)
- `resources/prompts/`: prompt markdown files (one per prompt); `shared/policy.md` is merged into each by the loader
- `resources/`: optional `server-instructions.md` is loaded and advertised to MCP clients as the server instruction set
- `.docker/`: Docker assets — `docker-compose.yml`, `docker-compose.dev.yml`, `Dockerfile`, `Caddyfile`, `php/` and `php-fpm.d/` config
- `Makefile`: handy shortcuts (run from project root)
- `tests/`: PHPUnit and PHPStan config and tests

----

## Documentation

- **[CONTRIBUTING.md](CONTRIBUTING.md)** — Setup, testing, style, and how to propose changes (for human contributors).
- **[AGENTS.md](AGENTS.md)** — Notes for AI agents and automation: code layout, prompts, tools, and commit conventions.

---

## Contributing

We welcome contributions! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on setting up your environment, coding style, testing, and how to propose changes. Most routine tasks can be executed via the Makefile.

See [CHANGELOG.md](CHANGELOG.md) for notable changes and update it with every release.

### License

MIT License - see [LICENSE](LICENSE).

----

## Acknowledgments

This project is inspired by and grateful to:
- The original Python openEHR MCP Server: https://github.com/deak-ai/openehr-mcp-server
- The [openEHR Assistant MCP Server](https://github.com/Cadasto/openehr-assistant-mcp) (Cadasto) - companion server for modelling, archetypes, and CKM
