Development¶
Run this after any change to cosmotron_mcp/; it must stay green. The
cosmotron conda env holds every dependency, including the optional
covariance stack (sacc/pyccl/tjpcov) and the inference stack
(firecrown/cosmosis/lsstdesc-crow — conda-forge only, see
Install).
Full developer guidance — agent-prompt editing rules, the tool-priority
invariant in detail — lives in
CLAUDE.md
at the repo root; the tool + agent contract the runtime agents themselves
read is AGENTS.md.
This page is a shorter, docs-site-native summary of the parts most relevant
to someone extending the pipeline.
Repo layout¶
cosmotron_mcp/
├── server.py FastMCP server — thin @mcp.tool wrappers; no logic here.
├── tools/ Deterministic pipeline functions (catalogue, maps, spectra,
│ covariance, ingest, readers, io, scripting, jobs, inference).
├── schemas.py Pydantic models (PipelineConfig, Manifest, SessionContext,
│ CatalogueProfile, DatasetRecord) + SCHEMA_VERSION.
├── profiles.py Catalogue-type profile loader (catalogue_profiles.yaml).
├── registry.py Persistent SQLite dataset registry (L1).
├── pipeline_resolve.py Deterministic pipeline_config resolution + provenance.
├── base.py Session/manifest/artefact I/O + validation.
├── gates.py Human gates (results/gates/<gate_id>.json), closed only by
│ authorise_gate (permission: ask). See Human gates.
├── reviewer_tools.py Deterministic review evidence block.
├── output_validation.py Post-run invariant checks (Nyquist, Knox, SACC shape).
└── plotting.py Deterministic figures (no fabricated data).
.opencode/
├── agents/ Agent prompts.
└── skills/ Static reference skills.
tests/ pytest suite (synthetic data, no real surveys).
docs-site/ This documentation site (MkDocs + mkdocs-material).
Tool-priority invariant¶
"A capability was shipped as a tool, but the model wrote its own script instead" is the single most common regression in this repo's history. It's structural, not a one-off, so any new pipeline/plot/inference capability must do all of the following or it will regress:
- Ship it as a first-class MCP tool. Deterministic Python, not model-authored code.
- Make the tool cover the actual request, name the capability in the signature. If users keep escaping to hand-written code to get X, X belongs as a named parameter on the existing tool, not a new one.
- Close the escape hatch mechanically.
run_analysis_scriptrejects matplotlib/pyplot/ savefig by default;@cosmotron's tool-deny list is enforced bytests/test_cosmotron_toolmap.py, not a prose "never". - Keep every doc surface in sync — the agent prompt's tool table,
AGENTS.md, the matching skill, and (now) this docs site's API reference. A capability the agent can't see, it won't use. - Add a mechanical test that the escape is blocked or the tool covers
the request (e.g.
tests/test_scripting_plot_guard.py,tests/test_plotting_show_noise.py,tests/test_docs_tool_coverage.py).
The same logic applies to inference config: the model picks a family/choice, the tool owns the syntax — never let the model author raw CosmoSIS INI or Firecrown YAML (see Writing TASK.md).
Extending this documentation¶
- New
@mcp.toolinserver.py→ add its name toserver.py's__all__list. That's it —docs-site/gen_reference_pages.pyruns on every build, parsesserver.py's imports to find which real module the tool delegates to (viatests/_tool_inventory.tool_module_map), and writes it onto that module's API reference page automatically. No page to hand-edit, no category to pick.tests/test_docs_tool_coverage.pyfails the suite until the tool is in__all__. - New agent in
.opencode/agents/→ one newagents/<name>.mdpage + onenav:line indocs-site/mkdocs.yml. - New skill in
.opencode/skills/→ one new##section inskills.md. - New survey family →
cosmotron_mcp/catalogue_profiles.yamlneeds no doc change (the field reference in Catalogue-type profiles already covers every field); only add an example if the new family exercises a field the existing KiDS-1000 example doesn't. - New human-confirmation point → prefer a human gate
(
cosmotron_mcp/gates.py::open_gate) over a mandatory string argument on the tool that would otherwise resolve it — a string is something the model writes, a gate file closed byauthorise_gatebehindpermission: askis not. Add the new gatekindtogates.py's kind table and to Human gates's table; if it has no safe default and resolves by re-calling with corrected input rather than byauthorise_gate(likenside/probe/cores), do NOT persist it as a gate file — see that page for why. - New
COSMOTRON_*env var → add a row to Configuration & environment's table — that page is the one place a user-facing env var should be documented; several existed with no doc entry anywhere before this rule.
Build and preview locally: