Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dicom-mcp

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 family CR/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.

How it works

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

Tools

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.

What image metadata is returned

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.

Reports

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.

Lossless export & fidelity

Medical interpretation depends on bit depth (hypo/hyper-intensities live in the stored pixel values), so export is built around fidelity:

  • export_image_png defaults 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 recorded applied_offset (subtract it to recover the original signed value). Pass windowed: true for 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_image returns an 8-bit, downscaled preview purely so the model can see the picture — it is explicitly not a faithful copy.

PDF layouts

  • 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 inclusive from..to range) in cols columns (default 4), each image captioned YYYY-MM MOD SeriesName Image##.

Building

cargo build --release

Requires 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.

Running

The server speaks JSON-RPC over stdio (stdout is reserved for the protocol).

dicom-mcp serve

Flags:

  • --ephemeral — don't persist loaded studies between runs.
  • --state-file <path> — override the state file location.
  • --allowed-root <dir> — restrict scan_folder to paths under this root (repeatable). Default: unrestricted.
  • dicom-mcp version — print build info and exit.

Claude Desktop config

{
  "mcpServers": {
    "dicom": {
      "command": "/path/to/dicom-mcp",
      "args": ["serve"]
    }
  }
}

Files & logging

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.

Privacy

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.

License

MIT — see LICENSE.

About

Read-only, privacy-first MCP server (Rust) that lets you explore your own CT/MR/X-ray DICOM imaging through Claude — list studies, view images, and build a PDF for your doctor. Anonymized; not a diagnostic device.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages