- name
- deploying-openmed-mcp
- description
- Run OpenMed's Model Context Protocol (MCP) server so coding agents (Claude Code, Codex) and chat clients can call clinical NER, PII extraction, and de-identification as tools, on-device. Use when the user wants to add OpenMed to an agent's MCP config, expose de-id/NER as MCP tools, run an MCP server over stdio or Streamable HTTP, give Claude/Codex access to OpenMed, or containerize the MCP server. Covers the mcp extra, create_mcp_server, the 7 tools (openmed_analyze_text, openmed_extract_pii, openmed_deidentify, openmed_list_models, openmed_list_pii_languages, openmed_loaded_models, openmed_unload_model), the resources and prompts, stdio vs streamable-http transports, ServiceRuntime env config, and MCP client config snippets.
- license
- Apache-2.0
- metadata
- {"project":"OpenMed","category":"deployment-ops","pairs":"adjacent","version":"1.0"}
# Deploying the OpenMed MCP server
`openmed.mcp.server` exposes OpenMed's clinical NLP as **Model Context Protocol**
tools, so coding agents (Claude Code, Codex) and chat clients can de-identify and
analyze clinical text by calling tools instead of writing glue code. It runs
**on-device** — models are local, no telemetry — and the server instructs
clients to send real PHI only to instances the user operates.
## When to use this skill
When an agent or LLM client should be able to *invoke* OpenMed: add it to a
coding agent's MCP config, give a chat client de-id/NER tools, or run a shared
MCP endpoint for a team. For programmatic HTTP from your own services, prefer
`serving-openmed-rest-api`; for corpora, `batch-processing-clinical-text`.
## Quick start
```bash
pip install "openmed[mcp]" # FastMCP / MCP SDK
# stdio transport (what coding agents spawn): default
python -m openmed.mcp.server
# Streamable HTTP transport (network-reachable):
python -m openmed.mcp.server --transport streamable-http --host 127.0.0.1 --port 8081
```
```python
# Or embed it:
from openmed.mcp.server import create_mcp_server
server = create_mcp_server() # FastMCP("OpenMed", ...) with tools+resources+prompts
server.run(transport="stdio") # or "streamable-http"
```
CLI flags (`build_arg_parser`): `--transport {stdio,streamable-http,http}`,
`--host`, `--port`, `--streamable-http-path` (default `/mcp`), `--version`.
Env equivalents: `OPENMED_MCP_TRANSPORT`, `OPENMED_MCP_HOST`,
`OPENMED_MCP_PORT` (8081), `OPENMED_MCP_PATH`.
## The 7 tools (confirmed in `openmed/mcp/server.py`)
| Tool | What it does | Key args |
| --- | --- | --- |
| `openmed_analyze_text` | clinical NER | `text`, `model_name` (`disease_detection_superclinical`), `confidence_threshold`, `group_entities`, `aggregation_strategy`, `sentence_*`, `keep_alive` |
| `openmed_extract_pii` | detect PII/PHI spans | `text`, `model_name` (default PII model), `confidence_threshold` (0.5), `use_smart_merging`, `lang`, `normalize_accents` |
| `openmed_deidentify` | mask/remove/replace/hash/shift dates | `text`, `method` (`mask`), `confidence_threshold` (0.7), `keep_year`, `shift_dates`, `date_shift_days`, `keep_mapping`, `lang` |
| `openmed_list_models` | list registry models | `category`, `pii_language`, `limit` |
| `openmed_list_pii_languages` | supported PII languages + default models | — |
| `openmed_loaded_models` | resident-model status of the MCP runtime | — |
| `openmed_unload_model` | free one model or all inactive models | `model_name`, `all_models` |
It also registers **resources** — `openmed://models`, `openmed://pii-languages`,
`openmed://examples` (synthetic) — and **prompts** `openmed-clinical-ner` and
`openmed-pii-deidentify` that nudge the agent toward safe, correct calls.
## Adding it to a coding agent
```json
// Claude Code: .mcp.json (or ~/.claude.json) — stdio transport
{
"mcpServers": {
"openmed": {
"command": "python",
"args": ["-m", "openmed.mcp.server"],
"env": { "OPENMED_PROFILE": "prod" }
}
}
}
```
For a shared HTTP deployment, run `--transport streamable-http` and point the
client at `http://<host>:8081/mcp`. The agent then sees the 7 tools and can call
e.g. `openmed_deidentify` on a snippet before sending it elsewhere.
## Runtime config
The MCP server shares OpenMed's `ServiceRuntime` (`ServiceRuntime.from_env()`),
so the same env vars as the REST service apply: `OPENMED_PROFILE`,
`OPENMED_SERVICE_PRELOAD_MODELS`, `OPENMED_SERVICE_KEEP_ALIVE`,
`OPENMED_SERVICE_MAX_RESIDENT_MODELS`. Preload to avoid first-call latency;
`openmed_unload_model`/`openmed_loaded_models` let an agent manage memory.
## Running in Docker
```dockerfile
FROM python:3.11-slim
RUN pip install --no-cache-dir "openmed[mcp]"
ENV OPENMED_MCP_TRANSPORT=streamable-http \
OPENMED_MCP_HOST=0.0.0.0 OPENMED_MCP_PORT=8081 \
OPENMED_SERVICE_PRELOAD_MODELS="OpenMed/OpenMed-PII-SuperClinical-Small-44M-v1"
EXPOSE 8081
CMD ["python", "-m", "openmed.mcp.server"]
```
stdio servers are spawned by the client and don't need a port; use HTTP only for
shared/remote access, behind your own auth proxy. Mount the model cache so the
container starts offline.
## Workflow
1. **Install + launch.** `pip install "openmed[mcp]"`, then
`python -m openmed.mcp.server` (stdio) or `--transport streamable-http`
for a shared endpoint.
2. **Configure the runtime** via the `ServiceRuntime` env vars (profile,
preload, keep-alive, max resident) so first calls aren't cold.
3. **Register with the client.** Add the `mcpServers` entry (stdio command, or
HTTP URL) to the agent's config; the 7 tools, resources, and prompts appear.
4. **Front HTTP with auth/TLS** if remote — the server has none built in; keep
stdio/local for untrusted-network scenarios.
5. **Let the agent call tools** (`openmed_deidentify` before sharing a snippet,
`openmed_analyze_text` for NER), and discover models via
`openmed_list_models` rather than hardcoding.
6. **Manage memory** with `openmed_loaded_models` / `openmed_unload_model`.
## Hand-off to / from OpenMed
- **Same engine:** each tool calls `openmed.analyze_text` / `extract_pii` /
`deidentify` through the shared runtime — identical results to the library and
the REST service.
- **REST sibling:** `serving-openmed-rest-api` exposes the same operations as
HTTP routes for non-agent callers.
- **Discovery:** `openmed_list_models` / `openmed_list_pii_languages` mirror the
library's `list_*` functions — agents should query, not hardcode.
## Edge cases & gotchas
- **stdio vs HTTP.** Coding agents spawn the server over **stdio** (default) and
manage its lifecycle; use **streamable-http** only for a shared endpoint, and
put auth/TLS in front of it (the server has none built in).
- **PHI trust boundary.** The server's instructions tell clients to send real
PHI only to instances the user controls. Keep it local/self-hosted; don't
point agents at an OpenMed MCP you don't operate.
- **`keep_mapping=True` returns a re-identification map** in the
`openmed_deidentify` response — only enable for trusted agents, treat the
mapping as PHI, never log it.
- **No raw PHI in logs.** Don't add transcript/body logging around the server.
- **Use synthetic examples** in docs/tests/prompts — the bundled
`openmed://examples` resource is synthetic on purpose.
- **`--transport http`** is accepted as an alias for `streamable-http`.
## Standards & references
- Model Context Protocol specification: https://modelcontextprotocol.io/
- MCP transports (stdio, Streamable HTTP):
https://modelcontextprotocol.io/docs/concepts/transports
- Claude Code MCP configuration:
https://docs.anthropic.com/en/docs/claude-code/mcp
- OpenMed source: `openmed/mcp/server.py` (`create_mcp_server`, the 7 tools,
resources, prompts, `main`/`build_arg_parser`).
Ver no GitHub