get-structured-report-text
Retrieve a DICOM Structured Report and convert it to human-readable text. Requires study, series, and SOP instance UIDs.
Instructions
Retrieves and converts a Structured Report (SR) instance to human-readable text. Requires Study, Series, and SOP Instance UIDs from find-structured-reports. Does not retrieve image data.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| studyInstanceUid | Yes | DICOM Study Instance UID (e.g., 1.2.840.113619.2.55.3). Obtain from find-studies or find-structured-reports. | |
| seriesInstanceUid | Yes | DICOM Series Instance UID (e.g., 1.2.840.113619.2.55.3.604688123). Obtain from find-series or find-structured-reports. | |
| sopInstanceUid | Yes | DICOM SOP Instance UID (e.g., 1.2.840.113619.2.55.3.604688123.123.1591781234.469). Obtain from find-instances or find-structured-reports. |
Implementation Reference
- src/index.js:289-334 (registration)Registration of the 'get-structured-report-text' tool on the MCP server with its Zod schema for studyInstanceUid, seriesInstanceUid, and sopInstanceUid parameters, and handler that calls getStructuredReportText.
server.tool( 'get-structured-report-text', 'Retrieves and converts a Structured Report (SR) instance to human-readable text. Requires Study, Series, and SOP Instance UIDs from find-structured-reports. Does not retrieve image data.', { studyInstanceUid: studyUidSchema.describe( 'DICOM Study Instance UID (e.g., 1.2.840.113619.2.55.3). Obtain from find-studies or find-structured-reports.' ), seriesInstanceUid: seriesUidSchema.describe( 'DICOM Series Instance UID (e.g., 1.2.840.113619.2.55.3.604688123). Obtain from find-series or find-structured-reports.' ), sopInstanceUid: sopUidSchema.describe( 'DICOM SOP Instance UID (e.g., 1.2.840.113619.2.55.3.604688123.123.1591781234.469). Obtain from find-instances or find-structured-reports.' ), }, async ({ studyInstanceUid, seriesInstanceUid, sopInstanceUid }) => { let textResult; try { // Log the retrieval criteria server.sendLoggingMessage({ level: 'info', data: `Retrieving structured report text for studyInstanceUid: ${studyInstanceUid}, seriesInstanceUid: ${seriesInstanceUid}, sopInstanceUid: ${sopInstanceUid}`, }); // Perform the retrieval using the provided parameters textResult = await getStructuredReportText( studyInstanceUid, seriesInstanceUid, sopInstanceUid, process.env ); // Log the successful retrieval server.sendLoggingMessage({ level: 'info', data: `Successfully retrieved structured report text for SOP Instance UID: ${sopInstanceUid}`, }); } catch (error) { const err = `Error retrieving structured report text: ${error.message}`; server.sendLoggingMessage({ level: 'error', data: err }); return errorContent(err); } return textContent(textResult); } ); - Core handler function getStructuredReportText that fetches DICOM instance metadata from the DICOMweb server and converts it to human-readable text using srToText.
export async function getStructuredReportText( studyInstanceUid, seriesInstanceUid, sopInstanceUid, env = process.env ) { // Fetch the instance metadata const headers = buildAuthHeaders(env); const res = await makeQuery( urlJoin( env.DICOMWEB_HOST, `/studies/${encodeURIComponent(studyInstanceUid)}/series/${encodeURIComponent(seriesInstanceUid)}/instances/${encodeURIComponent(sopInstanceUid)}/metadata` ), { headers, signal: buildSignal(env), } ); if (!res.ok) { throw new Error( `Get instance metadata request failed with HTTP status ${res.status} [uri: ${scrubUrl(res.url)}]` ); } const items = await res.json(); if (!items || !Array.isArray(items) || items.length === 0) { throw new Error( `Instance not found [Study Instance UID: ${studyInstanceUid}, Series Instance UID: ${seriesInstanceUid}, SOP Instance UID: ${sopInstanceUid}]` ); } // Find the first item that matches a known SR SOP Class UID and convert it to text const srItem = items.find((item) => SR_REPORTS_SOP_CLASS_UIDS.includes(item['00080016']?.Value?.[0]) ); if (srItem) { return srToText(srItem); } throw new Error( `Structured report not found [Study Instance UID: ${studyInstanceUid}, Series Instance UID: ${seriesInstanceUid}, SOP Instance UID: ${sopInstanceUid}]` ); } - src/utils/srToText.js:186-238 (helper)srToText function that converts a DICOM Structured Report instance (JSON format) into a human-readable plain-text string, including patient info header and recursive content sequence processing.
export function srToText(srInstance) { const lines = []; // Header block const name = dicomPersonName(srInstance, PatientName); const patientId = dicomVal(srInstance, PatientID); const sex = dicomVal(srInstance, PatientSex); const dob = dicomVal(srInstance, PatientBirthDate); const referringPhysician = dicomPersonName(srInstance, ReferringPhysicianName); const studyDescription = dicomVal(srInstance, StudyDescription); const seriesDescription = dicomVal(srInstance, SeriesDescription); const completionFlag = dicomVal(srInstance, CompletionFlag); const verificationFlag = dicomVal(srInstance, VerificationFlag); const contentDate = dicomVal(srInstance, ContentDate); const contentTime = dicomVal(srInstance, ContentTime); if (name || patientId) { lines.push(`Patient: ${name} (${sex}, ${dob}, ${patientId})`); } if (referringPhysician) { lines.push(`Referring Physician: ${referringPhysician}`); } if (studyDescription) { lines.push(`Study: ${studyDescription}`); } if (seriesDescription) { lines.push(`Series: ${seriesDescription}`); } if (completionFlag) { lines.push(`Completion Flag: ${completionFlag}`); } if (verificationFlag) { lines.push(`Verification Flag: ${verificationFlag}`); } if (contentDate || contentTime) { lines.push(`Content Date/Time: ${contentDate} ${contentTime}`.trim()); } // Report title from root ConceptNameCodeSequence const title = codeMeaning(vals(srInstance, ConceptNameCodeSequence)); if (title) { lines.push('', title, ''); } // Content body const contentItems = vals(srInstance, ContentSequence); if (contentItems.length > 0) { const continuous = dicomVal(srInstance, ContinuityOfContent) === 'CONTINUOUS'; processContentSequence(contentItems, lines, continuous); } return lines.join('\n'); } - src/utils/srToText.js:77-120 (helper)contentItemBody helper that dispatches on ValueType (TEXT, PNAME, DATETIME, CODE, NUM, etc.) to convert individual DICOM SR content items to plain text.
function contentItemBody(item) { switch (dicomVal(item, ValueType)) { case 'TEXT': return dicomVal(item, TextValue); case 'PNAME': return dicomPersonName(item, PersonName); case 'DATETIME': return dicomVal(item, DateTime); case 'DATE': return dicomVal(item, DicomDate); case 'TIME': return dicomVal(item, Time); case 'UIDREF': return dicomVal(item, UID); case 'CODE': return codeMeaning(vals(item, ConceptCodeSequence)); case 'NUM': { const mvItems = vals(item, MeasuredValueSequence); if (mvItems.length === 0) { return ''; } const mv = mvItems[0]; const numeric = dicomVal(mv, NumericValue); const unitItems = vals(mv, MeasurementUnitsCodeSequence); const unit = unitItems.length > 0 ? dicomVal(unitItems[0], CodeValue) : ''; return unit ? `${numeric} ${unit}` : numeric; } case 'CONTAINER': return ''; default: { const vt = dicomVal(item, ValueType); return vt ? `[${vt} not supported]` : ''; } } } - SR_REPORTS_SOP_CLASS_UIDS constant defining the known SOP Class UIDs for Structured Reports used to validate the retrieved instance.
export const SR_REPORTS_SOP_CLASS_UIDS = [ // Basic Text SR Storage '1.2.840.10008.5.1.4.1.1.88.11', // Enhanced SR Storage '1.2.840.10008.5.1.4.1.1.88.22', // Comprehensive SR Storage '1.2.840.10008.5.1.4.1.1.88.33', // Mammography CAD SR Storage '1.2.840.10008.5.1.4.1.1.88.50', // Chest CAD SR Storage '1.2.840.10008.5.1.4.1.1.88.65', // X-Ray Radiation Dose SR Storage '1.2.840.10008.5.1.4.1.1.88.67', ];