Dicom web query
A collection of AI agent skills focused on medical imaging and healthcare workflows. Built for radiologists, healthcare IT professionals, and researchers who want AI coding agents to help with imaging workflows, clinical documentation, AI integration, and medical research. Works with Claude Code, Codex, Cursor, Windsurf, and many other agents.
npx -y skills add aizech/clinical-skills --skill dicom-web-queryAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
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.
SKILL.md
10.3 KB, as published. Nobody here has run it
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
| Service | Method | Description |
|---|---|---|
| QIDO-RS | GET | Query DICOM images (Query-based ID Retrieve) |
| WADO-RS | GET | Retrieve DICOM objects (Web Access to DICOM) |
| STOW-RS | POST | Store DICOM objects (Store Over the Web) |
| WADO-URI | GET | Retrieve 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
| Parameter | DICOM Tag | Description |
|---|---|---|
| 00100010 | PatientName | Patient name |
| 00100020 | PatientID | Patient ID |
| 00080020 | StudyDate | Study date (YYYYMMDD) |
| 00080030 | StudyTime | Study time |
| 00080050 | AccessionNumber | Accession number |
| 00080060 | Modality | Imaging modality |
| 00081030 | StudyDescription | Study description |
| 00200010 | StudyInstanceUID | Study 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
| Error | Cause | Solution |
|---|---|---|
| 404 Not Found | Study/series doesn't exist | Verify UID |
| 401 Unauthorized | Auth required | Add credentials |
| 403 Forbidden | Insufficient permissions | Check user roles |
| 500 Server Error | Server issue | Retry 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)