agentleFS
aizechaizech/clinical-skills/.agents/skills/dicom-web-query/SKILL.md

dicom-web-query

Skill4 starsChanged 7 months agoMITView on GitHub

Query and retrieve DICOM objects via DICOMweb REST API. Also use when the user needs to work with DICOMweb servers, retrieve imaging studies via REST, or perform web-based DICOM operations. For traditional DICOM queries, see pacs-workflow.

The file

Files
SKILL.md

DICOMweb Query

You are a DICOMweb expert. Your role is to help users interact with DICOMweb-enabled servers for imaging data retrieval.

DICOMweb Overview

RESTful DICOM Services

ServiceMethodDescription
QIDO-RSGETQuery DICOM images (Query-based ID Retrieve)
WADO-RSGETRetrieve DICOM objects (Web Access to DICOM)
STOW-RSPOSTStore DICOM objects (Store Over the Web)
WADO-URIGETRetrieve via URI (legacy)

Base URL Structure

https://pacs.example.com/dicomweb

QIDO-RS (Query)

Study Search

import requests

BASE_URL = "https://pacs.example.com/dicomweb"

def qido_studies(filters=None, include_fields=None):
    """
    Query for studies using QIDO-RS.
    
    Args:
        filters: Dict of DICOM tags to filter
        include_fields: Specific tags to return
    """
    params = {}
    
    if filters:
        for key, value in filters.items():
            params[f"includefield={key}"] = value
    
    response = requests.get(
        f"{BASE_URL}/studies",
        params=params
    )
    
    return response.json()

Common Query Parameters

ParameterDICOM TagDescription
00100010PatientNamePatient name
00100020PatientIDPatient ID
00080020StudyDateStudy date (YYYYMMDD)
00080030StudyTimeStudy time
00080050AccessionNumberAccession number
00080060ModalityImaging modality
00081030StudyDescriptionStudy description
00200010StudyInstanceUIDStudy UID

Query by Patient

def find_studies_by_patient(patient_id):
    """Find all studies for a patient."""
    params = {
        "PatientID": patient_id,
        "includefield": "00080020,00080030,00080060"
    }
    
    response = requests.get(f"{BASE_URL}/studies", params=params)
    return response.json()

Query by Date Range

def find_studies_by_date(start_date, end_date, modality=None):
    """Find studies within date range."""
    params = {
        "StudyDate": f"{start_date}-{end_date}"
    }
    
    if modality:
        params["Modality"] = modality
    
    response = requests.get(f"{BASE_URL}/studies", params=params)
    return response.json()

Query by Modality

def find_ct_studies(limit=100):
    """Find CT studies."""
    params = {
        "Modality": "CT",
        "limit": limit
    }
    
    response = requests.get(f"{BASE_URL}/studies", params=params)
    return response.json()

WADO-RS (Retrieve)

Retrieve Study

def retrieve_study(study_uid, format="application/dicom+json"):
    """
    Retrieve study metadata.
    
    Args:
        study_uid: Study Instance UID
        format: Response format
    """
    headers = {"Accept": format}
    
    response = requests.get(
        f"{BASE_URL}/studies/{study_uid}",
        headers=headers
    )
    
    return response.json()

Retrieve as DICOM (ZIP)

def download_study_dicom(study_uid, output_path=None):
    """Download complete study as DICOM ZIP."""
    headers = {"Accept": "application/zip"}
    
    response = requests.get(
        f"{BASE_URL}/studies/{study_uid}/archive",
        headers=headers,
        stream=True
    )
    
    if output_path:
        with open(output_path, "wb") as f:
            for chunk in response.iter_content(chunk_size=8192):
                f.write(chunk)
    
    return response.content

Retrieve Series

def retrieve_series(study_uid, series_uid):
    """Retrieve specific series."""
    response = requests.get(
        f"{BASE_URL}/studies/{study_uid}/series/{series_uid}",
        headers={"Accept": "application/dicom+json"}
    )
    return response.json()

Retrieve Single Instance

def retrieve_instance(study_uid, series_uid, instance_uid):
    """Retrieve single DICOM instance metadata."""
    response = requests.get(
        f"{BASE_URL}/studies/{study_uid}/series/{series_uid}/instances/{instance_uid}",
        headers={"Accept": "application/dicom+json"}
    )
    return response.json()

Retrieve Pixel Data

def retrieve_image_pixels(study_uid, series_uid, instance_uid, frame=1):
    """
    Retrieve image pixel data.
    
    Args:
        study_uid: Study Instance UID
        series_uid: Series Instance UID
        instance_uid: SOP Instance UID
        frame: Frame number (1-indexed for multi-frame)
    """
    url = f"{BASE_URL}/studies/{study_uid}/series/{series_uid}/instances/{instance_uid}/frames/{frame}"
    
    response = requests.get(
        url,
        headers={"Accept": "image/jpeg"}
    )
    
    return response.content  # JPEG image data

WADO-RS Metadata

Study Metadata

def get_study_metadata(study_uid):
    """Get complete study metadata."""
    response = requests.get(
        f"{BASE_URL}/studies/{study_uid}/metadata",
        headers={"Accept": "application/dicom+json"}
    )
    return response.json()

Series Metadata

def get_series_metadata(study_uid, series_uid):
    """Get series metadata."""
    response = requests.get(
        f"{BASE_URL}/studies/{study_uid}/series/{series_uid}/metadata",
        headers={"Accept": "application/dicom+json"}
    )
    return response.json()

Instance Metadata

def get_instance_metadata(study_uid, series_uid, instance_uid):
    """Get single instance metadata."""
    response = requests.get(
        f"{BASE_URL}/studies/{study_uid}/series/{series_uid}/instances/{instance_uid}/metadata",
        headers={"Accept": "application/dicom+json"}
    )
    return response.json()

Thumbnail Retrieval

def get_thumbnail(study_uid, series_uid=None):
    """
    Retrieve study or series thumbnail.
    
    Args:
        study_uid: Study Instance UID
        series_uid: Optional series UID
    """
    if series_uid:
        url = f"{BASE_URL}/studies/{study_uid}/series/{series_uid}/thumbnail"
    else:
        # Get first series thumbnail
        url = f"{BASE_URL}/studies/{study_uid}/thumbnail"
    
    response = requests.get(
        url,
        headers={"Accept": "image/jpeg"}
    )
    
    return response.content

STOW-RS (Store)

Store DICOM File

def store_dicom(file_path, study_uid=None):
    """
    Store DICOM file to server.
    
    Args:
        file_path: Path to DICOM file
        study_uid: Optional existing study UID to add to
    """
    with open(file_path, "rb") as f:
        data = f.read()
    
    headers = {
        "Content-Type": "application/dicom",
        "Accept": "application/dicom+json"
    }
    
    response = requests.post(
        f"{BASE_URL}/studies{'/' + study_uid if study_uid else ''}/instances",
        headers=headers,
        data=data
    )
    
    return response.json()

Pagination

Limit Results

def qido_with_pagination(filters, limit=100, offset=0):
    """Query with pagination."""
    params = {
        "limit": limit,
        "offset": offset,
        **filters
    }
    
    response = requests.get(f"{BASE_URL}/studies", params=params)
    
    # Check for more results
    total = response.headers.get("X-Total-Count", "unknown")
    
    return {
        "results": response.json(),
        "total": total,
        "has_more": (offset + limit) < int(total) if total.isdigit() else True
    }

Common Patterns

Find and Download Study

def find_and_download(patient_id, output_dir):
    """Find patient's latest study and download."""
    # Find studies
    studies = find_studies_by_patient(patient_id)
    
    if not studies:
        return None
    
    # Get most recent
    latest = studies[0]
    study_uid = latest["0020000D"]["Value"][0]
    
    # Download
    download_path = f"{output_dir}/{study_uid}.zip"
    download_study_dicom(study_uid, download_path)
    
    return download_path

Bulk Retrieve by Date

def download_studies_by_date(start_date, end_date, modality, output_dir):
    """Download all studies for date range."""
    studies = find_studies_by_date(start_date, end_date, modality)
    
    downloaded = []
    for study in studies:
        study_uid = study["0020000D"]["Value"][0]
        try:
            path = f"{output_dir}/{study_uid}.zip"
            download_study_dicom(study_uid, path)
            downloaded.append(path)
        except Exception as e:
            print(f"Failed to download {study_uid}: {e}")
    
    return downloaded

Error Handling

ErrorCauseSolution
404 Not FoundStudy/series doesn't existVerify UID
401 UnauthorizedAuth requiredAdd credentials
403 ForbiddenInsufficient permissionsCheck user roles
500 Server ErrorServer issueRetry later

Authentication

Basic Auth

from requests.auth import HTTPBasicAuth

def authenticated_request(url, auth):
    """Make authenticated request."""
    response = requests.get(
        url,
        auth=HTTPBasicAuth(auth["username"], auth["password"])
    )
    return response

Bearer Token

def token_auth_request(url, token):
    """Make request with bearer token."""
    headers = {"Authorization": f"Bearer {token}"}
    response = requests.get(url, headers=headers)
    return response

Related Skills

  • pacs-workflow: For PACS-specific operations
  • filesystem-imaging: For local file handling
  • radiology-context: For configuration

Examples

Example 1: Query and Download CT Study

# Find CT studies from last week
studies = find_studies_by_date("20260325", "20260403", "CT")

# Download first result
if studies:
    study_uid = studies[0]["0020000D"]["Value"][0]
    download_study_dicom(study_uid, "ct_study.zip")

Example 2: Get Study Metadata

metadata = get_study_metadata("1.2.840.12345.67890")
for item in metadata:
    print(f"{item['0020000D']['vr']}: {item['00080018']['Value']}")

Example 3: Retrieve Image for Viewing

# Get thumbnail
thumb = get_thumbnail("1.2.840.12345.67890")

# Save as JPEG
with open("thumbnail.jpg", "wb") as f:
    f.write(thumb)

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.