Skip to content

Improve versioning discoverability #40

Description

@fedorov

IDC MCP Server: Making Release-Versioning Discoverable

Subject: Why an existing capability went undiscovered, and what to change
Server: IDC MCP (prod), api_version 3.0.0b2, build 968e137
Data release observed: IDC v24 (idc-index data 24.2.2), released 2026-04-16
Date: 2026-08-01


Summary

The IDC MCP server can already answer "what changed in release N" precisely. Every row in the
index table carries series_init_idc_version (the release in which a series first appeared) and
series_revised_idc_version (the release in which it was last revised). Combined with
version_metadata_index, which timestamps all 24 releases, this supports a true diff computed
entirely from the current release — no external release notes required.

That capability was not discovered until a user explicitly and repeatedly suggested it existed.
Before then, an incorrect claim was stated with confidence: "the MCP serves a single release, so it
can't diff v24 against v23."
The data to contradict that was present the whole time.

The failure is a discoverability failure, not a capability gap. It had three distinct causes, and
only one of them is "the feature wasn't documented."


Diagnosis

1. get_idc_version pointed away from the capability

The tool description frames version information as pure provenance — confirm which release your
answers are based on. Read alongside get_stats, which returns a single set of headline totals, the
natural inference is that the server exposes one immutable snapshot.

This is worse than silence. An absent description prompts investigation; a description that implies
a boundary ends it. The wrong conclusion was reached because of the documentation, not despite it.

2. Tool search could not surface it

A literal search for "IDC version release" returned get_idc_version, get_citations,
get_stats, list_collections, and list_analysis_results — none of which expose versioning.

The capability lives in a column, and column names are not in the search index. No tool
description on the server contains the words new, changed, added, diff, since, or
history. The vocabulary a user actually brings to this question matches nothing that is indexed.

3. Correct guesses suppressed the schema call

Working SQL was written against index using guessed column names — collection_id,
series_size_MB, Modality, PatientID — all of which happened to be right. Because the queries
succeeded, get_table_schema('index') was never called. That is the single location where
series_init_idc_version is documented.

This is the most generalizable lesson: a design that only works if the client reads the schema
first will fail often, because clients that guess correctly never read it.
Partial success is an
active suppressant of the discovery path.

The structural pattern

The capability sat three hops from any tool description:

list_tables  →  get_table_schema('index')  →  series_init_idc_version

Every hop was optional. Anything more than one hop from a tool description is found only by clients
that go looking — and clients go looking only when they already suspect the feature exists.


Recommendations

Ordered by leverage.

1. Add a first-class get_release_changes(version) tool — highest impact

The data fully supports this; it simply is not an affordance. Suggested return shape:

  • collections newly introduced in that release
  • series added vs. series revised, per collection
  • size added, patient counts touched
  • new analysis-result collections

Write the description in the vocabulary users actually bring:

What's new in a given IDC release. What changed since the previous version. Which collections
were added, updated, or revised, and how much data each contributed.

This makes the capability reachable on the first search, from the first natural phrasing of the
question. It is the single change that would have prevented the entire failure.

2. Rewrite get_idc_version to advertise rather than foreclose

One added sentence converts an anti-affordance into a signpost:

Every series in index also records the release in which it first appeared
(series_init_idc_version) and was last revised (series_revised_idc_version), so the
composition of any past release is queryable from the current one.

Low cost, and it directly prevents the specific false claim that was made.

3. Populate the MCP server instructions field

This is the general fix for the whole class of problem. A short capability inventory is delivered at
initialization, before any tool call, and does not depend on the client running the right search
with the right words. Roughly five lines:

  • release composition and change history across versions
  • cohort building with structured filters
  • license and citation breakdown for any cohort
  • per-collection clinical data joins
  • specialized indices for SEG, RTSTRUCT, SM, CT, MR, PT geometry

Complementarily, expose an MCP prompt named whats_new to give the human-facing entry point,
so the user does not have to know the capability exists in order to ask for it.

4. Fix prior_versions_index

Its description is empty, and its min_idc_version / max_idc_version columns are undocumented.
The table whose name most strongly signals version history is the least explained object on the
server — an unforced loss.

5. Have list_tables return notable column names, not just column_count

Currently list_tables reports index has 31 columns without naming any. Surfacing a handful of
notable ones would have exposed series_init_idc_version two turns earlier, and it closes the
"client guessed columns correctly and skipped the schema" gap without requiring an extra call.

6. Add release-note metadata to version_metadata_index

The table currently has two columns (idc_version, version_timestamp). Adding
announcement_url and/or release_notes_doi per version closes the one gap the diff genuinely
cannot fill: the SQL tells you what changed, never why. With that column, narrative context —
which collections came from GDC with matching genomic data, who contributed a given dataset —
becomes fetchable rather than requiring an external web search.


Appendix: the diff the server can already produce

Derived purely from run_sql, with no reference to published release notes.

v24 totals (series_init_idc_version = 24): 39,872 new series, 5.66 TB, 6,039 patients,
across 16 collections. One new analysis result: eay131_tumor_annotations, 15,799 RTSTRUCT/SEG
series.

Fifteen brand-new collections, confirmed by MIN(series_init_idc_version) = 24 across all their
series:

Collection New series Size (TB)
EAY131 30,293 0.80
CGCI-BLGSP 1,933 1.61
PSMA-PET-CT-Lesions 1,791 0.12
PDXNet 919 0.13
HCMI-CMDC 810 0.37
Spinal-Multiple-Myeloma-SEG 720 0.30
LDCT-and-Projection-data 698 0.86
CGCI-HTMCP-CC 525 0.15
CGCI-HTMCP-DLBCL 496 0.55
CATCH 350 0.57
CPTAC-STAD 99 0.02
CGCI-HTMCP-LC 84 0.09
UW-CIRP-Mouse-PET-CT-NSCLC 75 0.04
CDDP-EAGLE-1 49 0.02
HTAN-TNP-SARDANA 3 0.04

Plus BoneMarrowWSI-PediatricLeukemia (first appeared v22), which gained 1,027 new ANN series.

Notably, the diff surfaces changes the published release announcement omits: seven series were
revised rather than added in v24 — four in acrin_nsclc_fdg_pet, three in anti_pd_1_lung
(series_init_idc_version < 24 AND series_revised_idc_version = 24). Small, but real changes to
existing collections that a reader of the release notes alone would miss. This is a concrete
argument for recommendation #1: the server can already tell users something the prose cannot.

Reference query

WITH per_coll AS (
  SELECT collection_id,
         MIN(series_init_idc_version) AS coll_first_version,
         COUNT(DISTINCT SeriesInstanceUID)
           FILTER (WHERE series_init_idc_version = 24) AS added_v24,
         COUNT(DISTINCT SeriesInstanceUID)
           FILTER (WHERE series_init_idc_version < 24
                     AND series_revised_idc_version = 24) AS revised_v24,
         COUNT(DISTINCT SeriesInstanceUID) AS total_series
  FROM index
  GROUP BY collection_id
)
SELECT collection_id, coll_first_version, added_v24, revised_v24, total_series,
       CASE WHEN coll_first_version = 24
            THEN 'brand new collection'
            ELSE 'existing collection' END AS status
FROM per_coll
WHERE added_v24 > 0 OR revised_v24 > 0
ORDER BY coll_first_version, added_v24 DESC;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions