Skip to main content
Glama

medimgq: labeled CT images for exam questions

An MCP server that lets an AI build image questions for medical-school practice exams. The AI asks for structures ("label the pancreas, the left adrenal gland and both kidneys"); medimgq finds a real CT slice where they are all clearly visible, draws numbered arrows, and returns the image together with an answer key. It can also export click-the-structure (hotspot) masks that line up exactly with the image.

Images come from the TotalSegmentator dataset: 1,228 real CT scans, each with expert-reviewed outlines of 117 structures. CT comes first; X-ray and MRI are planned.

If you are an AI agent, read ai_read_this.md instead. It is the same information in a form built for tool use.


Contents


Related MCP server: CRC-LNM Medical Agent

How it works

Every medimgq option, grouped into Ask, Find a slice, Render, Get back

  1. Ask. The AI names the structures to label. Names are forgiving: "IVC", "inferior vena cava" and "postcava" all work; "kidney" means both kidneys; "lumbar spine" means L1–L5.

  2. Find a slice. medimgq searches a precomputed index of every structure on every slice of every scan. By default it uses the textbook view and vertebral level (the pancreas at L1, the thyroid at C6–T1) and skips noisy scans, cut-off anatomy and arms in the field of view.

  3. Render. It draws numbered arrows, placed so they don't cover other labeled structures or each other, picks a sensible window (lung window for lung lobes) and crops to the right region (neck, chest or abdomen).

  4. Get back. A PNG, an answer key such as {"1": "pancreas", "2": "left kidney, right kidney"}, the exact pixel geometry of every arrow and label, and optional hotspot masks.

The AI writes the question itself. medimgq only supplies the images and the facts about them.


Setup

You need Python 3.12+, uv, about 30 GB of disk for the dataset, and a few minutes to build the index.

git clone https://github.com/agadin/medical-image-question-creator.git
cd medical-image-question-creator
uv sync

1. Download the data (not included in this repo). Put it in the project folder, or point MEDIMGQ_TS_ROOT at it.

2. Build the indexes (one time):

uv run python -m medimgq.indexer --workers 12   # ~4 min: every structure on every slice
uv run python -m medimgq.quality --workers 12   # ~4 min: noise, contrast phase, cut-off body, arms
uv run python -m medimgq.findings --workers 12  # ~1 min: aneurysms, goiter, cysts, organ enlargement

They are written to data/index/ (about 160 MB).

3. Check it works:

uv run pytest            # unit tests on synthetic scans + an end-to-end test that calls every tool

Environment variable

Default

Purpose

MEDIMGQ_TS_ROOT

./Totalsegmentator_dataset_v201

dataset location

MEDIMGQ_INDEX_DIR

./data/index

index location

MEDIMGQ_OUTPUT_DIR

./outputs

where images go when no output_dir is given

MEDIMGQ_BLOCKLIST

./qc/blocklist.csv

cases a reviewer has rejected


Connect it to an AI

Claude Code

claude mcp add medimgq -- uv run --directory /path/to/medical-image-question-creator medimgq-mcp

Claude Desktop and other MCP clients: add this to the client's MCP config.

{
  "mcpServers": {
    "medimgq": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/medical-image-question-creator", "medimgq-mcp"]
    }
  }
}

Then ask your AI something like: "Make a CT question where the student has to recognize the left brachiocephalic vein. Use numbers on the image and add four distractors."


What you can ask for

  • Any of 117 structures: abdominal and pelvic organs, lung lobes, heart and great vessels, neck vessels, thyroid, trachea, esophagus, every vertebra C1–S1, every rib, major muscles and bones. Ask the list_structures tool for the full list with synonyms.

  • Several structures per label. {"label": "2", "structures": ["kidneys"]} puts one number on both kidneys.

  • Unlabeled arrows. A label of "" draws an arrow with no text ("What is the structure at the arrow?").

  • Shuffled numbers. shuffle_labels: true reassigns the numbers randomly, so the answer isn't always the first label you listed.

  • Real pathology: aortic aneurysms, goiters, renal cysts, cardiomegaly, enlarged spleen, liver or prostate, found by measuring every scan (see Real pathology).

  • Bare images with hotspot masks for click-the-structure questions.

  • Patients by filter: pathology category (tumor, vascular, trauma, inflammation, …), body region, scan type, sex, age, estimated contrast phase, or a specific case.


Options, with pictures

All images below are real output. They can be regenerated with uv run python scripts/make_docs_images.py.

Label style: render.preset

Ten label presets on the same slice

Preset

Use it for

arrow_short (default)

Most questions. Short arrows placed to avoid covering other structures and labels.

arrow_margin

Atlas-style figures: labels line up in the margins.

on_structure

Compact images where the number sits on the structure.

outline_labeled, outline_arrows

Easier questions; the outline shows the structure's shape.

outline_only

"Name the outlined structure" questions.

boxed_margin, yellow_short

Style variants.

edge_arrows

Arrow tips stop just inside the structure's edge.

bare

No markers. Use with hotspot masks.

Every part of a preset can be overridden on its own: marker, tip, outline, shared_mode, text_style (halo / boxed / boxed_dark), marker_color, text_color, font_size, size (longest side in pixels, default 768) and format (png / webp / jpeg).

View: view

Axial, coronal and sagittal examples

auto picks each structure's textbook view: axial for most organs, coronal for lung lobes and long bones, sagittal for vertebrae and the spinal cord. You can always force one.

Window: render.window

Soft tissue, lung, bone and mediastinum windows on one slice

auto uses the lung window when any lung lobe is labeled, the bone window when most targets are bones, and soft tissue otherwise. Presets: soft_tissue, abdomen, liver, mediastinum, lung, bone, brain, angio, or any [width, level] pair.

Crop: render.crop

Auto, body, targets and full crops of a neck slice

auto picks a region from the slice: neck (shoulders cut away), chest (arms cut away) or abdomen (whole trunk). You can force neck, chest or abdomen, or use body, targets (tight zoom) or full. A labeled structure is never cropped out.

Arrow tips and shared labels: render.tip, render.shared_mode

Center versus edge tips, and multi-arrow versus repeated shared labels

Which piece to point at: labels[].component

The aorta label on the aortic root versus the descending aorta

Some structures appear as two pieces on one slice. On an axial slice at T8, for example, you can see both the aortic root and the descending aorta. By default the arrow goes to the largest piece. Set component to a patient direction (anterior, posterior, left, right, superior, inferior) to pick the piece lying furthest that way. Directions that a view can't show (e.g. superior on an axial slice) are rejected.

Hotspot masks: hotspot_zones

A bare image, its zones overlaid, and one zone's mask

Each zone becomes a transparent PNG the same size as the image, with the structure's pixels painted. Give one zone as the answer and the others as known wrong answers, so a wrong click can explain what the student actually clicked.

Choosing the slice: strategy, level, filters

Strategy

What it picks

classical (default)

Textbook view and vertebral level, then the slice where every target is most visible

max_visibility

The slice where the least visible target is as visible as possible

max_area

The slice with the most total area of the targets

random

Any slice that passes the filters

level

A vertebral level you give, e.g. "level": "T8"

index

An exact slice number in a given case

labeled_image picks randomly among the best top_k (default 10) cases, so repeated calls vary. Pass a seed to make a result reproducible.

Quality filters are on by default and can each be relaxed:

Filter

Default

Meaning

max_noise_hu

45

Skip grainy scans (noise measured in muscle).

min_visibility

0.4

Each target must show at least 40% of its typical cross-section.

allow_edge_truncated

false

Skip structures cut off by the scan's edge.

allow_body_truncated

false

Skip slices where the body runs off the image.

allow_arms_in_fov

false

Skip abdominal slices with the arms down beside the body.

exclude_cases

[]

Skip these cases (e.g. ones already used).

blocklist

qc/blocklist.csv

Cases a reviewer rejected.

Patient filters: pathology (no_pathology, tumor, vascular, trauma, inflammation, bleeding, other, unclear), pathology_location and study_type (substring match), gender, age_min, age_max, and phase (non_contrast, arterial, venous; off by default).


What comes back

{
  "image_path": "outputs/mcp/q001.png",
  "image_size": [768, 695],
  "answer_key": { "1": "pancreas", "2": "left kidney, right kidney" },
  "key": [
    {
      "label": "1",
      "structures": ["pancreas"],
      "display_names": ["pancreas"],
      "anchors_px": [[480.0, 318.5]],
      "label_box_px": [536.4, 302.0, 564.4, 335.0],
      "arrows_px": [[[536.4, 318.5], [481.5, 318.5]]],
      "outlines_px": [],
      "area_mm2": { "pancreas": 2250.0 }
    }
  ],
  "hotspot_masks": [{ "id": "pancreas", "structures": ["pancreas"], "path": "outputs/mcp/q001_mask_pancreas.png" }],
  "source": {
    "dataset": "TotalSegmentator v2.0.1",
    "case_id": "s1426", "view": "axial", "slice": 71, "vertebral_level": "L1",
    "window": "soft_tissue", "crop_region": "abdomen",
    "case": { "age": 68, "sex": "m", "study_type": "unknown", "pathology": "unknown", "contrast_phase_estimate": "non_contrast" },
    "attribution": "TotalSegmentator dataset v2.0.1 (Wasserthal et al., Radiology: AI 2023), CC BY 4.0"
  },
  "selection": { "strategy": "classical", "visibility": { "pancreas": 1.0 }, "areas_mm2": { "pancreas": 2250.0 } },
  "image_base64": "iVBORw0KG...",
  "mime_type": "image/png"
}

Arrows are [tail, tip] pairs in output-image pixels. outlines_px is filled when outlines are drawn. image_base64 is included unless you pass include_base64: false.


The tools

Tool

What it does

list_structures(query?, region?, system?)

Look up names, synonyms and groups.

dataset_info()

Filter values, window and preset names.

find_slices(structures, view?, strategy?, level?, filters?, limit?, seed?)

Rank (case, view, slice) candidates.

slice_contents(case_id, view, slice_index)

Everything visible on a slice, to pick distractors.

render_image(case_id, view, slice_index, labels, render?, hotspot_zones?)

Render a slice you chose.

labeled_image(labels, view?, strategy?, level?, filters?, render?, hotspot_zones?, …)

Find a slice and render it in one call.

batch_labeled_images(requests, output_dir?, avoid_repeat_cases?)

Many images into one folder with a manifest.jsonl.

list_findings()

Pathology the dataset contains, with definitions and case counts.

find_findings(finding, min_value?, max_value?, filters?, limit?)

Cases with a finding, most severe first, with the slice to render.

render_image, labeled_image and batch requests also take shuffle_labels (and render_image a seed), and every label takes component.


Examples

One call, five labels:

{
  "tool": "labeled_image",
  "arguments": {
    "labels": [
      { "label": "1", "structures": ["left brachiocephalic vein"] },
      { "label": "2", "structures": ["right brachiocephalic vein"] },
      { "label": "3", "structures": ["brachiocephalic trunk"] },
      { "label": "4", "structures": ["trachea"] },
      { "label": "5", "structures": ["esophagus"] }
    ]
  }
}

A specific vertebral level: "strategy": "level", "level": "T8"

An unlabeled arrow on the aortic arch: "labels": [{ "label": "", "structures": ["aorta"] }], "view": "axial", "strategy": "max_area"

A click-the-structure question:

{
  "tool": "labeled_image",
  "arguments": {
    "labels": [],
    "render": { "preset": "bare" },
    "hotspot_zones": [
      { "id": "answer", "structures": ["esophagus"] },
      { "id": "aorta", "structures": ["aorta"] },
      { "id": "heart", "structures": ["heart"] }
    ],
    "strategy": "level", "level": "T8"
  }
}

A disease question: call find_findings with finding: "thoracic_aortic_aneurysm", then render_image with the returned case_id and slice, labeling aorta (with component: "posterior"), esophagus and heart.

Choosing distractors yourself: call find_slices, then slice_contents on the chosen slice to see what else is visible, then render_image with the labels you want.

A full worked example is in examples/examsim_ct_bank.py. It is the script that made the images for a 14-question practice bank, talking to the server exactly as an AI would.


Real pathology

Most scans show normal anatomy, and the dataset's own pathology labels are coarse (for example "vascular, thorax"). So medimgq measures every scan and flags real findings:

A thoracic aortic aneurysm and a retrosternal goiter

Finding

Measured as

Flagged at

Cases

Thoracic aortic aneurysm

widest aortic diameter, chest levels

≥ 50 mm

18

Abdominal aortic aneurysm

widest aortic diameter, lumbar levels

≥ 30 mm

74

Goiter

thyroid volume

≥ 35 mL

7

Renal cyst

largest cyst's diameter

≥ 10 mm

41

Cardiomegaly

heart width ÷ chest width on an axial slice (T5–T11)

≥ 0.60

33

Splenomegaly

spleen volume

≥ 450 mL

48

Hepatomegaly

liver volume

≥ 2,400 mL

36

Enlarged prostate

prostate volume (men)

≥ 40 mL

43

list_findings returns this table, and find_findings returns the cases, most severe first, with the axial slice that shows each finding best. The thresholds are screening values for finding teaching cases, not diagnoses, so look at every image. Each threshold was checked by viewing flagged cases near the cutoff and at the extreme. A hiatal hernia detector was tried and dropped because it mostly flagged normal stomachs.

Limits and caveats

  • Only segmented structures can be labeled. Nerves, the azygos vein, the internal jugular veins, laryngeal cartilages and bowel segments aren't available.

  • A structure that appears twice on a slice (e.g. the aortic root and the descending aorta) gets its arrow on the larger piece unless you set component.

  • Resolution is 1.5 mm, so images are slightly softer than clinical CT.

  • Pathology metadata describes the whole scan, not the labeled structure.

  • Contrast phase is an estimate from the brightness of the aorta and portal vein.

  • Check every image before you publish it. Masks come from a model and were reviewed by the dataset authors, but mistakes are possible. Add bad cases to qc/blocklist.csv.


Project layout and development

src/medimgq/
  server.py      MCP server (9 tools)
  catalog.py     structure names, synonyms, groups, name resolution
  selection.py   slice search: strategies, textbook levels, filters
  render.py      labels, arrows, outlines, crop, window, hotspot masks
  volume.py      CT loading, radiological orientation, windows
  indexer.py     builds the structure/slice index
  quality.py     builds the quality index (noise, phase, truncation, arms)
  findings.py    builds the findings index (aneurysm, goiter, cysts, organ enlargement)
scripts/
  compare_strategies.py   contact sheet comparing slice strategies
  showcase_labels.py      contact sheet comparing label presets
  make_docs_images.py     regenerates the images in this README
examples/
  examsim_ct_bank.py      worked example: an MCP client building a question bank
qc/blocklist.csv          cases rejected after review
tests/test_unit.py        unit tests on synthetic scans (no dataset needed)
tests/test_server.py      end-to-end test over stdio (needs the dataset and indexes)
uv run pytest                                  # tests
uv run python scripts/compare_strategies.py    # compare slice strategies
uv run python scripts/showcase_labels.py       # compare label presets

Data and attribution

Images are derived from the TotalSegmentator dataset v2.0.1 (Wasserthal J. et al., TotalSegmentator: Robust Segmentation of 104 Anatomic Structures in CT Images, Radiology: Artificial Intelligence, 2023), licensed CC BY 4.0. Every generated image carries this attribution in its footer.

The code is released under the MIT License.

For educational use only. Not for clinical decision-making.

Available Tools

9 tools
batch_labeled_imagesA

Run many labeled_image requests into one folder with a manifest.jsonl (no base64 in the reply). With avoid_repeat_cases each image comes from a different case when possible.

NameRequiredDescriptionDefault
requestsYes
output_dirNo
avoid_repeat_casesNo

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are supplied, so the description must carry behavior. It usefully discloses that output goes to a folder with manifest.jsonl, that replies contain no base64, and how avoid_repeat_cases affects case selection. It does not disclose overwrite behavior, directory creation, permissions, error handling, or return-path details for a file-writing batch operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the core batch action and output location; no redundant restatement. It is appropriately sized for the description's role.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a batch tool with nested request objects and no output schema, the description is too thin: manifest contents, output_dir behavior, file naming, and failure modes are unspecified. An agent can identify the tool but lacks enough context to invoke it safely and predictably.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With only 0% top-level schema description coverage, the description must explain parameters. It explains avoid_repeat_cases and loosely maps output_dir to "one folder", and points requests to labeled_image, but does not clarify output_dir defaults or the structure and fields of each BatchRequest.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific action and batch scope: run many labeled_image requests into one folder, which distinguishes it from the single labeled_image sibling. It also names manifest.jsonl and the no-base64 reply behavior, so the operation is immediately identifiable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Use is implied for many labeled_image requests rather than a single call, and avoid_repeat_cases is described. However, there is no explicit when/when-not guidance, no statement of alternatives such as labeled_image for single images, and no prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

dataset_infoC

Filter values (pathology, study type, contrast phase), windows, presets, and option names.

NameRequiredDescriptionDefault

No parameters

TDQS

C2.4/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are supplied, so the description carries the full burden, and it discloses little: it names categories of data (pathology, study type, contrast phase, windows, presets, option names) but says nothing about read-only safety, return structure, or how that content is organized. For a lookup/metadata tool with no output schema, that leaves the response shape entirely undisclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It is a single short clause with no padding, so length is not a problem. However, it is a grammatically incomplete fragment that is not front-loaded with a purpose statement, so brevity comes at the cost of clarity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no annotations, no output schema, and no parameters, the description is the only source of information about this tool, and it does not say what calling it produces, in what form, or when it should be preferred over the list_*/find_* siblings. The gap is significant relative to how little structured metadata is available.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is nothing for the description to disambiguate; the baseline for a parameterless tool applies. The listed content categories describe what the call covers rather than argument meaning.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose2/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description is a bare noun phrase ('Filter values..., windows, presets, and option names') with no verb and no statement of what the tool actually does or returns. It hints at dataset metadata enumeration, but an agent cannot tell whether this returns available filter values, a dataset summary, or configuration options, nor how it differs from siblings like list_structures or list_findings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no when-to-use or when-not-to-use guidance and no mention of any alternative tool. At best the agent can infer 'call this to discover filter values before searching,' but that inference is never stated and no sibling is referenced.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

find_findingsB

Cases showing a measured finding (see list_findings), with the axial slice that best shows it. min_value defaults to the finding's threshold. Render a result with render_image(case_id, 'axial', slice, labels=[...structures_to_label]).

NameRequiredDescriptionDefault
limitNo
filtersNo
findingYes
max_valueNo
min_valueNo
most_severe_firstNo

TDQS

B3.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It does disclose one real behavior beyond structured data -- min_value defaults to the finding's threshold -- and clarifies the return shape (cases plus a chosen slice). It says nothing about sorting (most_severe_first defaults true), the default limit, or what filters do to results, so meaningful behavioral gaps remain.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, purpose front-loaded, no padding. Minor cost: the render_image call snippet consumes a third of the text for something that is arguably another tool's job.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with a 15-field nested filter object, no output schema, and no annotations, the description is far too thin -- an agent cannot know which filters are safe to omit or what the returned slice field is named. The one useful note (min_value default) does not cover the filter surface.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% across 6 parameters (plus a 15-field nested CaseFilters object), so the description must compensate. It explains only min_value; finding, max_value, limit, most_severe_first, and the entire filters object are left with bare titles like 'Max Value' and 'Filter'.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource ('cases showing a measured finding') and what comes back ('the axial slice that best shows it'), which is concrete enough to distinguish it from list_findings, which it explicitly points to. It does not distinguish itself from find_slices, the closest-sounding sibling, so a 4 rather than a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The parenthetical '(see list_findings)' implies you must look up valid finding names first, and the last sentence chains results into render_image, which is useful workflow guidance. However, there is no statement of when to use this versus find_slices or slice_contents, and no exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

find_slicesC

Rank (case, view, slice) candidates where every structure is clearly visible. classical = textbook view and vertebral level; level = a vertebral level like 'L1'.

NameRequiredDescriptionDefault
seedNo
viewNoauto
levelNo
limitNo
filtersNo
strategyNoclassical
structuresYes
slice_indexNo

TDQS

C2.6/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are supplied, so the description carries the full burden, yet it never says whether the tool is read-only, what the returned candidates look like (scores? IDs? pagination?), or that filtering defaults such as min_visibility=0.4 and allow_arms_in_fov=false silently exclude slices. For an 8-parameter search tool with zero annotation coverage this is a significant gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It is short and front-loaded, with the core ranking behavior first and the enum gloss second, so no sentence is wasted. However, the second sentence covers only a fraction of the enum surface and the backtick-marked tokens read as fragments rather than structured guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given eight parameters, a nested-but-undescribed filters object with many behavioral toggles, no annotations, and no output schema, the description is far too thin to let an agent call the tool correctly. An agent would have to guess at strategy semantics, filter defaults, and the shape of the ranked result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% at the top level: seed, view, level, limit, strategy, structures, and slice_index have no inline documentation. The description clarifies only two of six strategy enum values and says nothing about 'max_visibility', 'max_area', 'random', or 'index', nor how 'level' interacts with the strategy or with the 'auto' view.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Rank (case, view, slice) candidates where every structure is clearly visible', which tells an agent this is a ranked slice search rather than a listing or rendering call. It does not explicitly differentiate itself from siblings such as find_findings or slice_contents, so it stops short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no when-to-use guidance versus alternatives and no indication of when a caller should prefer this tool over find_findings, slice_contents, or render_image. The only usage hint is the gloss on two strategy values ('classical', 'level'), leaving four other strategies, the 'view' and 'limit' choices, and the required 'structures' argument unaddressed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

labeled_imageC

Find a slice where every labeled structure (plus context_structures) is visible, then render it. Picks randomly among the top_k best cases so repeated calls vary (fix seed to reproduce). shuffle_labels randomly reassigns the label texts among the groups so the answer's number varies.

NameRequiredDescriptionDefault
nameNo
seedNo
viewNoauto
levelNo
top_kNo
labelsYes
renderNo
filtersNo
strategyNoclassical
output_dirNo
hotspot_zonesNo
include_base64No
shuffle_labelsNo
context_structuresNo

TDQS

C2.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden of behavioral disclosure. It usefully discloses that the tool selects randomly among the top_k best cases, that seed controls reproducibility, and that shuffle_labels randomizes label-to-group assignment. It does not describe side effects such as writing to output_dir, what is returned, how failures are handled, or how visibility is evaluated.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three tightly written sentences with the core action front-loaded. Each sentence adds distinct information: the find-and-render behavior, the top_k randomness and seed reproducibility, and the shuffle_labels effect. The phrase "answer's number varies" is slightly ambiguous but does not undermine the overall efficiency.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

This is a complex tool with 14 parameters, extensive nested objects, no annotations, and no output schema. The description covers only the core selection and randomization semantics. It leaves the agent without enough context about filters, rendering settings, output behavior, include_base64, output_dir, hotspot_zones, strategy, and how labeled structures are specified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is reported as 0%, so the description should compensate heavily for 14 top-level parameters. It adds meaning only for top_k, seed, shuffle_labels, and context_structures. Critical parameters such as labels, filters, render, strategy, view, hotspot_zones, include_base64, and output_dir receive no explanation in the description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific composite action: find a slice where all labeled structures are visible and then render it. This clearly identifies the tool as a labeled-image generator rather than a pure slice finder or renderer. It stops short of naming or distinguishing sibling tools such as find_slices, render_image, or batch_labeled_images.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains how to make repeated calls vary or be reproducible via top_k and seed, and how shuffle_labels changes the answer's number. However, it gives no explicit guidance on when to use this tool versus find_slices, render_image, slice_contents, or batch_labeled_images. The usage context is implied by the find-then-render behavior, but alternatives are not addressed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_findingsA

Pathology this dataset actually contains, found by measuring the segmentations (aneurysms, goiter, renal cysts, cardiomegaly, organ enlargement), with case counts. Also lists the coarse per-scan pathology categories from the dataset's metadata.

NameRequiredDescriptionDefault

No parameters

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are supplied, so the description carries the full burden. It does add genuinely useful provenance — findings are measured from segmentations rather than asserted in metadata, and counts are returned — but says nothing about whether the listing is exhaustive, how counts are scoped (per case vs per scan), or what the response shape looks like.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the core purpose and followed by the secondary metadata source. The parenthetical pathology examples are informative rather than filler, and nothing is redundant.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, read-only listing with no output schema, the description is largely sufficient: it names the two data sources and the presence of case counts. A brief note on return shape (e.g. how findings are keyed) would close the remaining gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4; there is no argument semantics to explain and the description correctly implies no filtering options exist.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource and what it returns: pathology present in the dataset, derived by measuring segmentations, with case counts, plus coarse per-scan metadata categories. That distinguishes it from list_structures (structures) and find_findings (search), though it never names those siblings explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by 'pathology this dataset actually contains' versus metadata categories, but there is no explicit when-to-use guidance, no statement of when to prefer find_findings or dataset_info, and no prerequisites or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_structuresA

List labelable structures. query resolves a name/synonym or substring-searches; filter by region (abdomen, thorax, neck, pelvis, head, spine, upper_limb, lower_limb) or system.

NameRequiredDescriptionDefault
queryNo
regionNo
systemNo

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so the description carries the full behavioral burden. It usefully discloses that `query` has dual behavior (exact name/synonym resolution versus substring search), which is non-obvious, but says nothing about result volume, pagination, or what fields a returned structure carries — significant gaps for a zero-annotation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences with no filler, and the query behavior is front-loaded before the filters. The trailing 'or system' is slightly elliptical — it reads as an afterthought and leaves the parameter under-described rather than saving space.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and no annotations, the description should at minimum sketch what a structure record looks like (identifier, canonical name, region, system) so an agent can chain into labeling tools. It covers inputs adequately but leaves the output side entirely unspecified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate and largely does: it explains `query`'s two resolution modes and enumerates all eight valid `region` values that the schema does not constrain via enum. The `system` parameter is only named, with no values or semantics given, which is the one real gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: list labelable structures. The word 'labelable' scopes the result set and implicitly separates it from siblings like list_findings, but no sibling is named, so the differentiation is inferred rather than stated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied — an agent can guess this is the lookup step before labeling (labeled_image, batch_labeled_images), but the description never says when to call this versus find_findings or list_findings, nor whether filters are combinable or exclusive.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

render_imageB

Render a specific slice with labels. Returns image path (+ base64), answer key with arrow/label geometry, and optional hotspot masks (transparent PNGs, pixel-aligned with the image). shuffle_labels randomly reassigns the label texts among the groups (seed to reproduce).

NameRequiredDescriptionDefault
nameNo
seedNo
viewYes
labelsYes
renderNo
case_idYes
output_dirNo
slice_indexYes
hotspot_zonesNo
include_base64No
shuffle_labelsNo

TDQS

B3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It does add genuinely useful behavior: it discloses the return payload (image path plus base64, answer key with arrow/label geometry, optional pixel-aligned hotspot mask PNGs) and explains that shuffle_labels randomly reassigns label text with seed for reproducibility. It does not mention whether files are written to disk via output_dir, what permissions are needed, cost or performance, or what happens on an invalid slice index.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences, front-loaded with the core action and the return contract, followed by the one non-obvious parameter behavior. Nothing is padded or redundant, though the parenthetical detail about transparent PNGs could be trimmed.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For an 11-parameter, no-annotation, no-output-schema tool, the description covers the return shape well, which is the most important missing piece. It still leaves the labels and hotspot_zones structures, view and slice selection, and output_dir side effects to the schema, so an agent could invoke it but not confidently.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% at the top level across 11 parameters, so the description must compensate, but it only explains shuffle_labels and seed. Core parameters like case_id, view, slice_index, labels, hotspot_zones, include_base64, and output_dir get no elaboration. Nested $defs do carry some descriptions, such as Label.label and RenderSettings.size and window, which softens the gap slightly, but most top-level semantics are left implicit.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: render a slice with labels, returning an annotated image. It is clear what the tool does, but it never distinguishes itself from the sibling tools labeled_image and batch_labeled_images, which sound like near-identical operations, so an agent cannot tell which to pick from the description alone.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no when-to-use guidance and no exclusions. Given that labeled_image and batch_labeled_images exist as siblings, the description should say when this single-slice renderer is the right choice instead of those, but it says nothing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

slice_contentsC

Structures visible on a slice (area, visibility), largest first. Use to choose distractors.

NameRequiredDescriptionDefault
viewYes
case_idYes
slice_indexYes
min_area_mm2No

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. It does disclose ordering ("largest first") and the returned attributes (area, visibility), which is useful, but it never says the tool is read-only, does not mention the area filtering behavior implied by min_area_mm2, and does not address empty results or what happens when a slice has no structures.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences with the resource description and the ordering fact front-loaded, and no filler. The only inefficiency is the jargon-laden second sentence, which spends words on an unexplained term rather than on information the agent needs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 4-parameter tool with no annotations and no output schema, the description is thin: it omits the default filter behavior, index conventions, and the form of the returned structure entries beyond naming two fields. An agent could call it, but not predict what it receives or how the threshold affects the result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% across 4 parameters, so the description must supply meaning. It only gestures at "slice" and "area"; it never explains what slice_index indexes (base, range), what view values are valid (only in the enum), or that min_area_mm2 is a threshold defaulting to 50 mm² that suppresses small structures.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

"Structures visible on a slice (area, visibility), largest first" states a specific verb-and-resource plus the returned fields and ordering, so an agent knows it retrieves per-slice structure listings. It does not distinguish itself from the sibling list_structures, which plausibly returns structures at the dataset level, leaving some ambiguity about scope.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"Use to choose distractors" gives one concrete use case, which is more than a bare purpose statement. However, no alternative is named (list_structures, find_slices), no when-not-to-use is stated, and the domain term "distractors" is undefined, so the agent must infer the selection conditions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 9 tool updatesv0.1.0
    • First observedbatch_labeled_images
    • First observeddataset_info
    • First observedfind_findings
    • First observedfind_slices
    • First observedlabeled_image
    • First observedlist_findings
    • First observedlist_structures
    • First observedrender_image
    • First observedslice_contents

TDQS

B3.2/5.0

Scored across 9 tools

Disambiguation4/5

Each tool has a distinct primary role: metadata, structure listing, finding listing/search, slice search, slice contents, rendering, and batch rendering. The main overlap is that labeled_image is essentially a composite of find_slices + render_image, and batch_labeled_images is a batch variant, but the descriptions make their intended uses reasonably clear.

Naming Consistency3/5

The names mix verb_noun patterns (list_structures, find_findings, render_image) with noun or adjective forms (dataset_info, slice_contents, labeled_image, batch_labeled_images). The convention is still readable, but it is not fully predictable across the set.

Tool Count5/5

Nine tools fit the medical-imaging query/render workflow well, with each tool earning a place and no excessive surface area. The set is large enough to cover search, inspection, rendering, and batching without feeling bloated.

Completeness4/5

The surface covers metadata, structure/finding discovery, slice search, slice inspection, single rendering, and batch rendering, which are the core operations for this domain. Minor gaps exist, such as no explicit case-listing/get_case tool, but agents can work around them through find_* operations.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI coding agents to perform end-to-end medical image analysis locally, from inspecting CT/MRI/X-ray studies and running containerized segmentation models like MedSAM2, TotalSegmentator, and HD-BET to refining masks, measuring volumes, and writing reports. It pairs preset or custom guideline protocols with a code-customizable viewer that renders images back to agents as annotated PNGs and to humans as interactive HTML.
    2
    Apache 2.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables a connected LLM to inspect brain MRI slices as crops and montages with coordinates, compare a region against its mirrored hemisphere, and review the pipeline's candidate hotspots. It lets the model write prioritised "area to review" annotations that appear live in the browser viewer, and move or read the clinician's crosshair position.
    MIT