CLI quickstart¶
dcmclient is one static binary (also installed as the shorter dcmcli —
dcmcli <subcommand> is identical). Every subcommand is self-describing, and the
whole roster is agent-discoverable over manifest and mcp.
$ dcmclient --help # the full command list (or: dcmcli --help)
$ dcmclient dcmdump --help # per-tool flags
Networking¶
# DICOMweb — convenience verbs, or the raw RS verbs (qido / wado / stow)
$ dcmclient search https://pacs.example.com --patient-id 42 --json
$ dcmclient pull https://pacs.example.com --study 1.2.3 -o ./out
$ dcmclient push https://pacs.example.com ./series/*.dcm --study 1.2.3
# DIMSE — verification / storage / query-retrieve (conventional tool names; dashed aliases like echo-scu also work)
$ dcmclient echoscu PACS 4242 --aec PACS
$ dcmclient storescu PACS 11112 ./*.dcm --aec PACS
$ dcmclient findscu PACS 11112 -S -k QueryRetrieveLevel=SERIES --json
$ dcmclient getscu PACS 11112 -S -k StudyInstanceUID=1.2.3 --out-dir out/ # C-GET pull
$ dcmclient movescu PACS 11112 --move-dest STORESCP -S -k StudyInstanceUID=1.2.3 # C-MOVE to a third party
$ dcmclient storescp 11112 --aet STORESCP --out-dir incoming/ # receive the C-MOVE (or any C-STORE)
$ dcmclient termscu PACS 4242 --aec PACS # connectivity probe (open + release)
# DIMSE-N workflow services — MPPS, print, storage commitment, UPS, IAN
$ dcmclient mpps-scu PACS 11112 --action create --dataset-file step.json
$ dcmclient stgcmt-scu PACS 11112 --dataset-file commit.json --transaction-uid 1.2.3.4
DICOMweb has both the convenience verbs above and the raw RS verbs as their own
tools (qido / wado / wado-uri / stow / ups, plus delete to remove a
study / series / instance); DIMSE covers the full classic set (echo / store
/ find / get / move SCU + store-scp, plus the term-scu connectivity
probe) and the DIMSE-N services (mpps-scu, printscu, stgcmt-scu,
upsscu, ian-scu). Every networked subcommand accepts --profile <name> to fill
--server / --base-path / --auth from a saved registry
(dcmclient config add …), plus AWS SigV4 (--aws-* / $AWS_*) and OAuth2
(--oauth2-* / $DCMCLIENT_OAUTH2_*).
Connecting to a specific server¶
DIMSE addresses a peer by host port --aec <AE>; DICOMweb by --server <origin>
--base-path <prefix>. The values differ per product:
| Server | DIMSE host port --aec |
DICOMweb --server + --base-path |
|---|---|---|
| Orthanc | localhost 4242 --aec ORTHANC |
--server http://localhost:8042 --base-path /dicom-web |
| dcm4chee-arc | localhost 11112 --aec DCM4CHEE |
--server http://localhost:8080 --base-path /dcm4chee-arc/aets/DCM4CHEE/rs |
# Orthanc — DICOMweb + DIMSE
$ dcmclient qido --server http://localhost:8042 --base-path /dicom-web --level studies
$ dcmclient stow --server http://localhost:8042 --base-path /dicom-web ct.dcm
$ dcmclient echo-scu localhost 4242 --aec ORTHANC
$ dcmclient get-scu localhost 4242 --aec ORTHANC --out-dir out/ -k QueryRetrieveLevel=STUDY -k StudyInstanceUID=1.2.3
# dcm4chee-arc — note the long DICOMweb base-path
$ dcmclient qido --server http://localhost:8080 --base-path /dcm4chee-arc/aets/DCM4CHEE/rs --level studies
$ dcmclient find-scu localhost 11112 --aec DCM4CHEE -k QueryRetrieveLevel=STUDY -k PatientID
# A root-mounted DICOMweb server uses an EMPTY base-path (not "/", which 404s)
$ dcmclient qido --server http://localhost:8080 --base-path "" --level studies
# Save a profile so you stop retyping
$ dcmclient config add orthanc --url http://localhost:8042 --base-path /dicom-web
$ dcmclient qido --profile orthanc --level studies
Notes: --base-path needs a leading slash (/dicom-web, not dicom-web); wado on a tiny study
fetches more reliably with --concurrency 1. TLS — DIMSE: add --tls --tls-ca-cert ca.crt
(--tls-cert/--tls-key for mTLS, --tls-insecure to skip verify); DICOMweb HTTPS: use an
https:// origin and trust a private CA via SSL_CERT_FILE=ca.crt (no per-call CA flag).
Local file tools¶
The 64-tool roster groups into eleven kinds of operation — at least one example of each below. The generated tool reference has every flag of every tool.
Inspect — read a file as text / JSON / XML / semantic content
$ dcmclient dcmdump scan.dcm # one element per line
$ dcmclient dcm2json scan.dcm > scan.json # DICOM JSON (PS3.18 §F)
$ dcmclient dcm2xml scan.dcm > scan.xml # Native DICOM XML (PS3.19)
$ dcmclient dcm2content seg.dcm # semantic JSON of SEG/RT/PS/Waveform
$ dcmclient dsrdump report.dcm # SR content tree as text
Vendor character sets decode where strict readers fail
Text values come out as correct UTF-8 regardless of how the source declared
its SpecificCharacterSet — the native text engine handles the full DICOM
range (Latin / Cyrillic / Greek / Arabic / Hebrew / Thai, Japanese
Shift-JIS + ISO 2022 IR 87 / 159, Korean EUC-KR, Chinese
GB18030 / GBK, UTF-8) and decodes it adaptively — lenient charset-alias
matching, tolerant of malformed ISO 2022 escapes, with a fault-tolerant
fallback. The messy real-world vendor exports that a strict iconv-based reader
rejects, dcmdump / dcm2json / dcm2xml read correctly.
Render & extract pixels / signals
$ dcmclient dcm2raw ct.dcm out.raw --json # decode ANY transfer syntax → raw pixels + numpy sidecar
$ dcmclient dcm2img ct.dcm out.png # 8-bit PNG/PNM for display
$ dcmclient dcm2mpg video.dcm out.mp4 # extract an embedded video stream
$ dcmclient dcm2waveform ecg.dcm out.csv # ECG/EEG samples → CSV / WAV
Convert & transcode
$ dcmclient dcmconv --to j2k ct.dcm out.dcm # transcode (also: rle / jls / htj2k / jxl / …)
$ dcmclient dcm2nii ct_series/ ct.nii.gz # validated 3-D NIfTI
$ dcmclient dcm2nii --4d dce_series/ dyn.nii.gz # 4-D dynamic stack (time / echo / phase)
$ dcmclient dcm2nii --dwi dti_series/ dwi.nii.gz # 4-D DWI stack + FSL .bval/.bvec
$ dcmclient dcm2nii --split study_dir/ out/ # batch: one .nii.gz per series, -f names them (dcm2niix-style)
$ dcmclient dcm2nrrd ct_series/ ct.nrrd # NRRD (3D Slicer; --gzip; double-faithful space)
$ dcmclient dcm2mha ct_series/ ct.mha # single-file MetaImage (ITK / nnU-Net; --compress)
$ dcmclient img2dcm photo.jpg out.dcm # JPEG → Secondary Capture
$ dcmclient tiff2dcm slide.tif out_dir/ # pyramidal TIFF → DICOM WSI
$ dcmclient pdf2dcm report.pdf out.dcm # wrap a PDF ↔ dcm2pdf extracts it back
$ dcmclient dcm2pdf report.dcm report.pdf # …the reverse: pull the embedded document out
$ dcmclient dcmencap model.stl out.dcm # wrap PDF/CDA/STL/OBJ/MTL ↔ dcmdecap unwraps
$ dcmclient dcmdecap report.dcm out.pdf # …the reverse: extract any encapsulated payload
$ dcmclient json2dcm scan.json out.dcm # DICOM JSON Model → Part-10 (inverse of dcm2json)
$ dcmclient dump2dcm edited.txt out.dcm # an (edited) dcmdump text listing → Part-10
$ dcmclient dsr2xml report.dcm report.xml # SR content tree → XML ↔ xml2dsr rebuilds the SR
$ dcmclient seg2nii seg.dcm labels.nii.gz # rasterize a SEG → labelmap NIfTI
$ dcmclient seg2nrrd seg.dcm labels.seg.nrrd # rasterize a SEG → 3D Slicer .seg.nrrd (named segments)
$ dcmclient dcmresample ct_series/ iso.nii.gz --spacing 1 # resample onto a 1 mm isotropic grid
$ dcmclient dcm2tiff slide.dcm region.tiff --level 0 --region 10000,8000,1024,1024 # WSI pyramid region → TIFF
$ dcmclient dcm2fhir study/ > study.fhir.json # whole study → FHIR R4 ImagingStudy
Every codec built in — including JPEG-XL and HTJ2K
dcmconv encodes and decodes every DICOM transfer syntax out of the box,
with no plugins or external codec packages — JPEG-2000 (.4.90/.91),
JPEG-LS (.4.80/.81), RLE, baseline / lossless JPEG, and the
newest JPEG-XL (.4.110/.111) and HTJ2K / High-Throughput JPEG 2000
(.4.201–.203). JPEG-XL and HTJ2K in particular are syntaxes most DICOM
toolchains still don't ship, so dcmclient is often the only tool that can
read or produce them:
# encode to the modern codecs
$ dcmclient dcmconv --to jxl ct.dcm ct_jxl.dcm # JPEG-XL (lossless)
$ dcmclient dcmconv --to htj2k ct.dcm ct_htj2k.dcm # HTJ2K / jph
# decode them anywhere — dcm2raw / dcm2img read ANY transfer syntax
$ dcmclient dcm2raw ct_jxl.dcm pixels.raw --json
$ dcmclient dcm2img ct_htj2k.dcm preview.png
Author derived objects
$ dcmclient mkseg ct.dcm --mask labelmap.dcm --meta seg.json -o out-seg.dcm # coded SEG
$ dcmclient mkparamap series/ --values adc.f32 --meta adc.json -o adc_map.dcm # Parametric Map
$ dcmclient mkreport findings.json report.dcm --series-from study.dcm # TID 1500 SR
$ dcmclient mksr report.json -o report-sr.dcm # arbitrary SR content tree
$ dcmclient mkkos keyimages.json kos.dcm --series-from study.dcm # Key Object Selection
Edit, de-identify & validate
$ dcmclient dcmodify scan.dcm -i '(0010,0010)=Anon^Patient' # byte-verbatim edit
$ dcmclient dcmdeident study/*.dcm --out-dir deid/ # PS3.15 de-identification
$ dcmclient dsign in.dcm signed.dcm --key key.pem --cert cert.pem # PS3.15 digital signature
$ dcmclient dcmradiomics ct.dcm --mask roi.dcm --json # IBSI radiomic features
$ dcmclient dcmdvh rtstruct.dcm rtdose.dcm --roi 1 # RT dose-volume histogram
$ dcmclient dcmprobe scan.dcm # is it valid Part-10?
$ dcmclient dcmvalidate scan.dcm # IOD conformance
$ dcmclient dcmicmp original.dcm compressed.dcm # pixel-difference metrics
A directory of instances → a DICOMDIR-indexed file-set (PS3.10 media):
De-identification — the full PS3.15 Annex E profile, configurable
dcmdeident implements the PS3.15 Annex E Basic Profile with every
retain / clean option column as a flag — keep exactly what your use case
allows and remove the rest. A batch shares one session, so UIDs are
remapped consistently across the whole study, and it never edits in
place:
# de-identify a study but keep dates and patient characteristics for research,
# shift nothing, drop private tags, consistent UID remap across the batch:
$ dcmclient dcmdeident study/*.dcm --out-dir deid/ \
--retain-dates --retain-patient-chars
# or shift every date by a fixed offset (consistent across the batch):
$ dcmclient dcmdeident study/*.dcm --out-dir deid/ --shift-dates -3650 --json
Options include --retain-uids / --retain-device-id /
--retain-institution-id / --retain-private / --clean-descriptors /
--clean-graphics — see the tool reference.
Validation — IOD conformance, agent-readable
dcmvalidate goes past VR / value-length / dictionary checks to full
IOD conformance: per-SOP-Class mandatory modules,
Type 1/2 presence, value multiplicity, enumerated values — top-level and
nested per sequence item — plus cross-reference integrity & identifier
uniqueness (RT ROI / beam / setup references) and conditional-presence
rules (palette / modality LUT / VOI window / lossy / pixel-padding). --json
emits a structured findings document an agent can branch on:
Worked recipes¶
Real chains — each step is one tool call, so an agent can run them too.
PACS → de-identified NIfTI for a model
$ dcmclient search https://pacs.example.com --patient-id 42 --modality CT --json
$ dcmclient pull https://pacs.example.com --study 1.2.840... -o ./study
$ dcmclient dcmdeident ./study/*.dcm --out-dir ./deid # PS3.15 de-identification
$ dcmclient dcm2nii ./deid ct.nii.gz # 3-D volume, validated affine
Migrate a study between two PACSes (streaming, constant memory)
$ dcmclient forward --source https://old-pacs.example.com \
--dest https://new-pacs.example.com \
--study 1.2.840...
Bridge a legacy DIMSE sender to a DICOMweb archive
$ dcmclient listen --port 11112 --ae-title BRIDGE \
--sink https://archive.example.com # C-STORE in → STOW out
Inspect, fix a wrong tag, and re-validate — the LLM edit loop
$ dcmclient dcmdump ct.dcm | grep PatientID # see the current value
$ dcmclient dcmodify ct.dcm -i '(0010,0020)=ANON-001' # byte-verbatim edit
$ dcmclient dcmvalidate ct.dcm # confirm still conformant
Author a structured report from measurements
$ dcmclient mkreport findings.json report.dcm # TID 1500 SR
$ dcmclient dsr2html report.dcm > report.html # human-readable render
Agent / MCP¶
The same ToolSpec registry that backs the CLI is the agent surface:
$ dcmclient manifest # JSON self-description of every tool (inputSchema + outputSchema)
$ dcmclient mcp # live MCP server over stdio — an agent drives the whole roster
Because the tool reference is generated from manifest, the
docs, the agent surface, and the binary can never disagree.
Exit codes¶
| Code | Meaning |
|---|---|
| 0 | success |
| 1 | runtime error (bad input, or a per-file failure) |
| 104 | argument conversion error (bad numeric / enum) |
| 105 | validation error (rejected or mutually-exclusive option) |
| 106 | required option or argument missing |