An MCP server, written in Rust, that lets a person read their own DICOM medical imaging through Claude — list their studies, look at individual images, and build a PDF to bring to their doctor or specialist.
It is deliberately narrow and privacy-first:
- Read-only. It never writes to, moves, or deletes a source DICOM file. Exports (PNG/PDF) are written only to the server's own output folder (or a configured path).
- CT, MR, and X-ray only (
CT,MR, and the projection-radiography familyCR/DX/XA/RF). Other modalities are filtered out at scan time. - Anonymized. Image metadata returned to the model is an allow-listed, non-identifying set. Patient names, IDs, birth dates, accession numbers, institutions, and DICOM UIDs are never read or surfaced.
- Not medical advice. The server helps surface and organize a person's imaging and prepare questions. Diagnosis belongs with a clinician.
⚕️ This tool empowers individuals to access and understand their own imaging. It is not a diagnostic device and does not replace professional radiological interpretation.
DICOM studies — whether exported from a portal or burned on a disc — are
indexed by a DICOMDIR file (a file literally named DICOMDIR, no extension).
The server is pointed at the folder the imaging lives in; it scans that folder and
all subfolders for every DICOMDIR (including a disc copied to disk), parses
the PATIENT → STUDY → SERIES → IMAGE hierarchy, keeps the CT/MR/XR series, and
reports the studies it found:
StudyName · date · # series · total images
| Tool | What it does |
|---|---|
scan_folder |
Recursively scan a folder for DICOMDIRs and load CT/MR/XR studies. Replies with the studies found. |
list_studies |
List the studies currently loaded (persists across restarts). |
get_study |
Series breakdown for one study (modality, number, name, image count) plus any attached reports. |
get_report |
Retrieve a study's report: a Structured Report rendered to text inline, or an encapsulated PDF written to the output folder. |
get_image_info |
Anonymized technical metadata for one image (see below). |
view_image |
Inline 8-bit, window-leveled preview so the image can be looked at and discussed. |
export_image_png |
Lossless PNG export — raw stored values at native bit depth by default (or window-leveled 16-bit). |
generate_pdf |
Build a PDF in adjacency or grid layout to share with a clinician. |
clear_studies |
Drop the loaded set (does not touch source files). Requires confirm: true. |
build_info |
Version, git build/commit, UTC compile time, target, rustc. |
Only technical, non-identifying attributes:
- Position: image N / M, series, series name, modality.
- Dimensions / size / type: rows × columns, samples per pixel, bits allocated/stored, signed/unsigned, photometric interpretation, frames, uncompressed stored byte size, and a one-line summary.
- Slice geometry: slice thickness, slice location, spacing between slices, pixel spacing, slice position (z), kVp.
- Acquisition date-time and scanner make/model.
- Intensity mapping: rescale slope/intercept and window center/width — so a clinician can interpret hypo/hyper-intensities.
Studies often ship the report alongside the images, as a DICOM Structured
Report (SR) or an encapsulated PDF. These live in SR/DOC series that
the CT/MR/XR filter drops, so they're captured separately and listed per study
(report_count / the reports array in get_study). get_report renders an
SR to readable text inline (radiation-dose reports, key-object selections,
findings — whatever the SR contains, walked from its content tree) or writes an
encapsulated PDF to the output folder. A report is the patient's own
document and may name them, so — unlike image metadata — it is returned as-is,
not anonymized.
Medical interpretation depends on bit depth (hypo/hyper-intensities live in the stored pixel values), so export is built around fidelity:
export_image_pngdefaults to the raw stored pixel values at native bit depth, with no modality LUT / VOI window / rescale applied — 16-bit grayscale PNG with maximum (Compression::High) lossless compression. Signed pixels are shifted into the unsigned range by a recordedapplied_offset(subtract it to recover the original signed value). Passwindowed: truefor a display-leveled 16-bit image instead.- The PDF generator embeds images losslessly at native resolution as 16-bit grayscale and merely scales them by the PDF transform to fit each cell — no resampling. Streams use lossless FlateDecode (no DCT/JPEG, no downscaling), so embedded pixels stay bit-exact while keeping the PDF compact. (Images are window-leveled for visibility.)
view_imagereturns an 8-bit, downscaled preview purely so the model can see the picture — it is explicitly not a faithful copy.
adjacency— the selected image centered, with its series neighbours in a cross: left = previous slice, right = next slice, top = two-back, bottom = two-forward. A neighbour past the end of the series is drawn as a grey placeholder of the same cell size.grid— a contact sheet of the whole series (or an inclusivefrom..torange) incolscolumns (default 4), each image captionedYYYY-MM MOD SeriesName Image##.
cargo build --releaseRequires Rust ≥ 1.85 (edition 2024) and a C compiler (cc/MSVC) — JPEG 2000
support compiles a bundled copy of the C OpenJPEG library (openjpeg-sys). No
separate GDCM/OpenJPEG/cmake install is needed.
Decoders: uncompressed, RLE, and baseline/extended JPEG via pure-Rust native;
JPEG 2000 (1.2.840.10008.1.2.4.90/.91, very common in PACS exports) via C
OpenJPEG. JPEG-LS and JPEG XL are not enabled. The transfer_syntax field in
get_image_info reports what each image uses. See
KNOWN_ISSUES.md for why the pure-Rust JPEG 2000 decoder is
deliberately avoided (it aborts the process) and how to build without the C
dependency.
The server speaks JSON-RPC over stdio (stdout is reserved for the protocol).
dicom-mcp serveFlags:
--ephemeral— don't persist loaded studies between runs.--state-file <path>— override the state file location.--allowed-root <dir>— restrictscan_folderto paths under this root (repeatable). Default: unrestricted.dicom-mcp version— print build info and exit.
{
"mcpServers": {
"dicom": {
"command": "/path/to/dicom-mcp",
"args": ["serve"]
}
}
}Everything the server writes lives under the OS-standard per-user data directory (never inside the imaging folders):
- Windows
…\AppData\Local\dicom-mcp\ - macOS
~/Library/Application Support/dicom-mcp/ - Linux
~/.local/share/dicom-mcp/
| Path | Contents |
|---|---|
state.json |
The set of loaded studies (metadata only — never pixel data). |
logs/dicom-mcp.log |
Rolling daily runtime log (also mirrored to stderr). |
error.log |
Scrubbed JSONL records for unexpected errors (version + kind only — no paths or image data). |
out/ |
Exported PNGs and generated PDFs. |
This software reads private medical data. By design it only ever surfaces the
anonymized technical metadata listed above, never patient identifiers or UIDs,
and writes nothing back to the imaging. The scrubbed error.log exists so it
can be voluntarily attached to a bug report; review it first.
MIT — see LICENSE.