Guidance for working in this repository.
An LLM-first REST API + MCP server for the NCI Imaging Data Commons (src/idc_api/,
tests in tests/), backed by the idc-index Parquet index queried locally with DuckDB. One
backend-agnostic core library, two thin adapters (rest/, mcp/).
uv venv --python 3.12
uv pip install -e ".[dev]"
uv run --directory . pytest tests -q # run the test suite
uv run idc-api # REST API → http://127.0.0.1:8000 (/v3/docs)
uv run idc-mcp # MCP server over stdioThese are the rules the design depends on. Full detail and walkthroughs are in dev/developer_guide.md; the design rationale is in dev/architecture.md.
core/never imports an adapter. Nofastapi/mcpimports undercore/. Adapters importcore/, never the reverse.- Adapters are thin. A REST route or MCP tool validates input and calls a service. No SQL
or domain logic in
rest/ormcp/. - Services return Pydantic models from
core/models.py— never raw dicts or DataFrames. Both adapters serialize the same models; parity tests enforce REST output == MCP output. - SQL we author is parameterized. Use
backend.query(sql, params)with?placeholders; never f-string user values into SQL. Identifiers (table/column names) that can't be bound must be validated againstschemaallow-lists and double-quoted (seecore/filters.py). - Raw caller/LLM SQL only via
backend.run_user_sql. Never route untrusted SQL throughbackend.query. - MCP tool descriptions are prescriptive about when to call the tool (e.g. "call this before filtering"), not just what it does — this measurably improves tool selection.
- Errors: raise an
IDCAPIErrorsubclass from services. REST maps it to{status, code, message}; the MCPguarddecorator converts it to a cleanToolError. Never leak tracebacks.
Adding a capability touches five places (model → service → REST route → MCP tool → parity test). See the walkthrough in dev/developer_guide.md.
Docs are split by audience — keep them in their lanes:
- docs/user-guide.md — the human-facing user guide: concepts, the query surfaces (Discovery → Cohort → Retrieval, with SQL as the escape hatch) and how they relate, the recommended workflow, worked REST/MCP examples, and the config reference. Usage documentation belongs here.
idc://guideMCP resource (the_GUIDEstring insrc/idc_api/mcp/server.py) — the agent-facing guide and the canonical place for the conceptual model on the agent side. It mirrors the same conceptual model as the user guide (tool families, how they relate, the workflow). Keep it in sync when the conceptual model changes. Note:tests/test_mcp.pyasserts this resource contains "Data model".INSTRUCTIONS(insrc/idc_api/mcp/server.py) — the MCP serverinstructions, injected into the agent's prompt on every session (always-on, most token-sensitive). Keep it lean: orientation + the behavioral rules (ground-first, check size before download, cite/license)- a pointer to
idc://guide. Do not restate the full data model / tool reference here — put detail inidc://guideso this third copy can't silently drift.
- a pointer to
- README.md — kept lean: intro, status, install, run one-liners, deploy, and pointers. Do not add endpoint/tool reference or usage detail here — that goes in the user guide.
- CONTRIBUTING.md — the process: branching, Conventional Commits, the
changelog rule, and the versioning policy (SemVer with MAJOR pinned to the URL prefix,
/v3↔3.y.z). Do not restate setup/test commands or the architecture invariants here — it points atdev/developer_guide.mdfor those. The release runbook lives indev/deployment.md, not here: it is a maintainer task, not a contributor one. - CHANGELOG.md — hand-curated, Keep a Changelog format. Describes user-visible change only (endpoints, MCP tools, response shapes, config vars, defaults) — never refactors, CI, or dependency bumps. Not generated from commits.
dev/— design & contributor docs: architecture.md, api_v3_plan.md (design rationale + SQL threat model), deployment.md (Cloud Run tiers and the release runbook), developer_guide.md (setup, test, CI, the invariants, walkthroughs).
When you add or change a capability: update docs/user-guide.md; mirror any conceptual
change into the idc://guide resource; check whether INSTRUCTIONS needs a one-line touch (it
usually shouldn't, if it stays lean); keep README.md a pointer. If the change is user-visible,
add a CHANGELOG.md entry under ## [Unreleased] in the same PR.