# AGENTS.md Shared instructions for AI coding agents working in this repository. ## What this project is `genefoundry-router` is a **thin FastMCP 3.x aggregator** (a router/gateway), NOT a data server. It federates the GeneFoundry `*-link` MCP fleet behind one Streamable-HTTP endpoint (`genefoundry`) with collision-free namespacing, BM25 tool-search, pluggable auth, and a config-driven registry. It is a *client* to each backend and a *server* to hosts. - Primary code area: `genefoundry_router/` - Source of truth for backends: `servers.yaml` (structure) + `.env` (URLs/secrets) + `uv.lock` - Design spec: `docs/specs/2026-06-13-genefoundry-router-design.md` - Implementation plan: `docs/plans/2026-06-13-genefoundry-router-implementation.md` ## Required check before handoff ```bash make ci-local # format-check, lint, lint-loc (600-LOC budget), mypy, unit + integration tests ``` Other useful targets: `make test`, `make test-integration`, `make test-cov` (coverage ≥70), `make lint`, `make typecheck`, `make run`, `make validate`, `make doctor`, `make list-tools`, `make docker-build`, `make docker-prod-config`, `make docker-npm-config`. ## Coding standards - Python **3.14**; dependency + venv management via **uv** (`uv sync --group dev`, `uv run`). - Modern typing (`X | None`, builtin generics); `ruff` (lint + format) and `mypy` must pass. - **600-LOC per module** budget, enforced by `scripts/check_file_size.py` (`make lint-loc`). - TDD: write a failing test, see it fail, implement minimally, see it pass; one atomic commit per change. - FastMCP 3.x symbols are post-training-cutoff and fast-moving — **verify imports against the installed package** before relying on them (see the import smoke in the implementation plan, Task 2). Adapt to the installed API; the integration test is the contract. ## Project-specific guidance - **Namespacing is the gateway's job.** Keep per-backend `transform` blocks minimal and delete them from `servers.yaml` as source repos adopt Tool-Naming Standard v1. - **No token passthrough**: never forward the caller's `Authorization` header to backends. - Streamable HTTP only (`transport="http"`); SSE is not offered. - **Container & deployment hardening is a fleet standard.** The router and every `-link` server follow `docs/CONTAINER-HARDENING-STANDARD-v1.md` (non-root, read-only rootfs, `cap_drop: ALL`, `no-new-privileges`, resource limits, expose-only behind the proxy, digest-pinned base, CI image scan + SBOM). The router's `docker/` is the reference implementation; copy it, don't reinvent. - **Backends are unauthenticated by design** (the router owns edge auth at the trust boundary) — they MUST be reachable only through the router/reverse proxy, never published directly. ## Fleet deploy contract - `docker/docker-compose.npm.yml` is the file the fleet controller (`strato_v6_docker_npm`, `scripts/utils/deployment_preflight.py`) deploys and validates. Every service there declares `user: ":"` numerically — this image's own value from `docker/Dockerfile` (`10001:10001`), never copied from a sibling `-link` repo. - `user` must NOT appear in the Compose files listed in `container-release.json` (`docker/docker-compose.yml`, `docker/docker-compose.prod.yml`) — the shared release gate forbids it there. - Guard test: `tests/unit/docker/test_compose.py` (`test_npm_overlay_declares_numeric_deploy_user`, `test_release_compose_files_omit_user`). - The deployed overlay is gated centrally: `container_release.py validate-deployed-overlay` runs in the shared release workflow's `prepare` job, before any image is built, for every repository that calls it. The rules, the `container-release.json` fields (`service.deployed_compose_files`, `deployed_seed_binds`, `deployed_sidecars`) and the waiver path are documented in [`docs/deployment.md`](docs/deployment.md#fleet-compose-contract). - Release checklist this repo enforces: bump `pyproject.toml`, `uv lock`, `CHANGELOG.md` heading `## [x.y.z] - YYYY-MM-DD`, `CITATION.cff` `version:` (generated file — this repo's `date-released` is release-driven, set by `scripts/gen_citation_cff.py --write` from the repo's actual latest GitHub release date, not from the CHANGELOG; leave it alone in a PR and let the fleet-level `make citation-write` refresh it after the tag lands), tag `vx.y.z`, then approve the `release` environment gate via `gh api repos/berntpopp/genefoundry-router/actions/runs//pending_deployments` (it can gate twice; `status: waiting` is the gate, not a slow build). ## Fleet standards (apply repo-wide; each `-link` repo has a tracking issue) - Tool-Naming & Normalization — `docs/TOOL-NAMING-STANDARD-v1.md` - Response-Envelope — `docs/RESPONSE-ENVELOPE-STANDARD-v1.md` - Tool-Surface Budget — `docs/TOOL-SURFACE-BUDGET-STANDARD-v1.md` (`make lint-surface`) - Tool-Schema Documentation — `docs/TOOL-SCHEMA-DOCUMENTATION-STANDARD-v1.md` (`make lint-surface`) - Container & Deployment Hardening — `docs/CONTAINER-HARDENING-STANDARD-v1.md` - Versioning (single-source) — `docs/VERSIONING-STANDARD-v1.md` - Logging & CLI Standard v1 (sibling; canonical text tracked per-repo) Conformance is gated, not just documented. `docs/conformance/` is the canonical source vendored byte-identical into every `-link` repo: `conformance.py` gates *transport*, `behaviour.py` gates the *contract* an agent relies on (no silently-empty filter, honest `total`/`truncated`, an error that names the parameter, `isError` on every error envelope). Behaviour probes are derived from each server's own advertised schema — there is no per-repo probe list to write, and none to forget. ## Boundary Research use only. Not clinical decision support. Mirror the backends' disclaimers.