9.2 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
A Model Context Protocol (MCP) server that provides DICOM medical imaging QA tools to Claude. Built with FastMCP and pydicom, it exposes 17 read-only async tools for analyzing DICOM files, with a focus on body composition analysis, Dixon sequence validation, pixel analysis, Philips private tag resolution, UID comparison, segmentation verification, and T1 mapping (MOLLI/NOLLI) analysis.
Commands
# Install dependencies
poetry install --with dev
# Run all tests
poetry run pytest -v --tb=short
# Run a single test class
poetry run pytest test_dicom_mcp.py::TestToolRegistration -v
# Run a single test
poetry run pytest test_dicom_mcp.py::TestToolRegistration::test_all_tools_registered -v
# Run the MCP server directly
poetry run python -m dicom_mcp
# Run with PII filtering enabled
DICOM_MCP_PII_FILTER=true poetry run python -m dicom_mcp
Architecture
The project is structured as a Python package (dicom_mcp/) with a backward-compatible shim at the root (dicom_mcp.py). All tests live in test_dicom_mcp.py.
Package Structure
dicom_mcp/
__init__.py # Re-exports all public symbols for backward compat
__main__.py # Entry point: python -m dicom_mcp
server.py # mcp = FastMCP("dicom_mcp") + run()
config.py # Env-var-driven config (PII_FILTER_ENABLED, MAX_FILES)
constants.py # Enums (ResponseFormat, SequenceType), COMMON_TAGS, VALID_TAG_GROUPS
pii.py # PII tag set + redaction functions
helpers/
__init__.py # Re-exports all helper functions
tags.py # _safe_get_tag, _format_tag_value, _resolve_tag, _validate_tag_groups, _format_markdown_table
sequence.py # _identify_sequence_type, _is_dixon_sequence, _get_dixon_image_types
files.py # _is_dicom_file, _find_dicom_files
philips.py # _resolve_philips_private_tag, _list_philips_private_creators
pixels.py # _get_pixel_array, _extract_roi, _compute_stats, _apply_windowing
filters.py # _parse_filter, _apply_filter
tree.py # _build_tree_text, _build_tree_json, _format_tree_value
tools/
__init__.py # Imports all tool modules to trigger @mcp.tool() registration
discovery.py # dicom_list_files, dicom_find_dixon_series
metadata.py # dicom_get_metadata, dicom_compare_headers
query.py # dicom_query, dicom_summarize_directory
validation.py # dicom_validate_sequence, dicom_analyze_series
search.py # dicom_search
philips.py # dicom_query_philips_private
pixels.py # dicom_read_pixels, dicom_compute_snr, dicom_render_image
tree.py # dicom_dump_tree
uid_comparison.py # dicom_compare_uids
segmentation.py # dicom_verify_segmentations
ti_analysis.py # dicom_analyze_ti
dicom_mcp.py # Thin shim so `python dicom_mcp.py` still works
Import Chain (No Circular Dependencies)
config.pyandconstants.pyare leaf modules (no internal imports)server.pyonly importsFastMCPexternally — owns themcpinstancehelpers/*.pyimport fromconstants.py,config.py, pydicom/numpy (neverserver.py)tools/*.pyimportmcpfromserver.py, plus helpers and constantstools/__init__.pyimports all tool modules (triggers@mcp.tool()registration)server.run()does a deferred import ofdicom_mcp.toolsbefore starting__init__.pyre-exports everything soimport dicom_mcpstill works
Tool structure: Each tool is an async function that takes a directory/file path, performs DICOM analysis using pydicom, and returns formatted results (markdown or JSON). All tools are read-only and annotated with ToolAnnotations(readOnlyHint=True). All blocking I/O is wrapped in asyncio.to_thread() to prevent event loop blocking.
The 17 tools:
dicom_list_files— Recursively find DICOM files, optionally filtered by sequence type; supports count_only modedicom_get_metadata— Extract DICOM header info from a single file using tag groups; supports Philips private tags viaphilips_private_tagsparameterdicom_compare_headers— Compare headers across 2-10 files side-by-sidedicom_find_dixon_series— Identify Dixon sequences and detect image types (water/fat/in-phase/out-phase)dicom_validate_sequence— Validate sequence parameters (TR, TE, flip angle) against expected valuesdicom_analyze_series— Comprehensive series analysis checking parameter consistency and completenessdicom_summarize_directory— High-level overview of directory contents with field-level summariesdicom_query— Query arbitrary DICOM tags across a directory with optional groupingdicom_search— Search DICOM files using filter syntax (text, numeric, presence operators)dicom_query_philips_private— Query Philips private DICOM tags by DD number and element offset; can list all Private Creator tags or resolve specific private elementsdicom_read_pixels— Extract pixel statistics with optional ROI and histogramdicom_compute_snr— Compute signal-to-noise ratio from two ROIsdicom_render_image— Render DICOM to PNG with windowing and ROI overlaysdicom_dump_tree— Full hierarchical dump of DICOM structure including nested sequences; configurable depth and private tag visibilitydicom_compare_uids— Compare UID sets (e.g. SeriesInstanceUID) between two DICOM directories; supports any tag keyword or hex codedicom_verify_segmentations— Validate that segmentation DICOM files reference valid source images via SourceImageSequencedicom_analyze_ti— Extract and validate inversion times from MOLLI/T1 mapping sequences across vendors; handles Philips private TI tags automatically
Key constants:
MAX_FILES— Safety limit for directory scans (default 1000, configurable viaDICOM_MCP_MAX_FILESenv var)COMMON_TAGS— Dictionary of 9 tag groups (patient_info, study_info, series_info, image_info, acquisition, manufacturer, equipment, geometry, pixel_data) mapping to DICOM tag tuplesVALID_TAG_GROUPS— Sorted list of valid tag group names
PII Filtering
Patient-identifying tags can be redacted from tool output via an environment variable:
- Enable:
DICOM_MCP_PII_FILTER=true(acceptstrue,1,yes, case-insensitive) - Disable: unset or any other value (default)
Redacted tags (patient tags only): PatientName (0010,0010), PatientID (0010,0020), PatientBirthDate (0010,0030), PatientSex (0010,0040).
Affected tools: dicom_get_metadata, dicom_compare_headers, dicom_summarize_directory, dicom_query. All other tools do not expose patient data and are unaffected.
Redaction is applied at the output formatting level via redact_if_pii() in pii.py. Internal logic (sequence identification, grouping) uses raw values.
Behavioural Constraints
This MCP is a data inspection tool, not a clinical decision support system. When using these tools, keep responses strictly factual and descriptive:
- Report what is observed in the DICOM data (tag values, pixel statistics, parameters, counts)
- Describe technical characteristics (acquisition settings, sequence types, vendor differences)
- Do not suggest clinical utility, diagnostic applications, or workflow suitability
- Do not interpret findings in a clinical or diagnostic context
- Do not assess data quality relative to specific clinical use cases
- Do not recommend clinical actions based on the data
Present data as-is. Qualified professionals draw the conclusions.
These constraints are enforced at protocol level via FastMCP(instructions=...) in server.py, which sends them to any MCP client at connection time. See docs/GUIDELINES.md for the full policy and regulatory context.
Key Patterns
- All tools use
ResponseFormatenum (MARKDOWN/JSON) for output formatting SequenceTypeenum covers: DIXON, T1_MAPPING, MULTI_ECHO_GRE, SPIN_ECHO_IR, T1, T2, FLAIR, DWI, LOCALIZER, UNKNOWN- DICOM files are validated by checking for the 128-byte preamble + "DICM" magic bytes in
_is_dicom_file() - Custom DICOM tags can be specified in hex format
(GGGG,EEEE) - Philips private tags are resolved dynamically per-file via
_resolve_philips_private_tag()— block assignments vary across scanners - All synchronous I/O (pydicom.dcmread, file globbing) is wrapped in
asyncio.to_thread()to keep the event loop responsive _find_dicom_files()returnstuple[list[tuple[Path, Dataset]], bool]— pre-read datasets + truncation flag — to avoid double-reading files- The server imports
FastMCPfrommcp.server.fastmcp(not directly fromfastmcp)
Documentation
Additional documentation lives in the docs/ directory:
- docs/USAGE.md — Detailed tool reference, parameter guide, and QA workflow examples
- docs/TODO.md — Planned improvements and known issues
- docs/CAPABILITIES.md — Plain-English summary of all 17 tool capabilities
- docs/GUIDELINES.md — Behavioural constraints and regulatory context