---
summary: "Field-test pass against real BrAPI servers — capability-gated companion lookups, top-level study FK probes, structured-season + multi-set variant tolerance, and a multi-tenant HTTP deployment notice."
breaking: false
---

# 0.3.4 — 2026-04-29

A robustness pass shaped by exercising every tool against live BrAPI servers (Breedbase, T3, Sweetpotatobase, and the test server). The headline change is that companion lookups in `brapi_get_germplasm` and `brapi_get_study` now consult the cached capability profile before firing — saving a 404 round-trip per unsupported endpoint. Several `find_*` tools learned to surface upstream sparsity (free-text filters that aren't honored, bbox windows that exclude all rows, named filters that return zero calls) instead of silently returning empty results.

## Added

- **`brapi_connect` multi-tenant deployment notice** — when a connection authenticates over HTTP without per-client auth (`MCP_TRANSPORT_TYPE=http` and `MCP_AUTH_MODE=none|unset`), the tool now emits a `notice`-level log warning that the resolved bearer token persists under the shared `default` tenant and is reachable to any other connected client. README gains a "Multi-user HTTP deployments" section documenting tenant resolution and the cross-user state caveat.
- **Typed `study_not_found` / `germplasm_not_found` errors** — `brapi_get_study`, `brapi_get_germplasm`, and `brapi_submit_observations` now translate upstream 404s into a typed contract entry with a routing recovery hint, replacing the generic `serviceUnavailable` bubble-up.
- **`brapi_walk_pedigree` root-existence pre-flight** — when `germplasm/{germplasmDbId}/pedigree` is supported, each requested root is probed before the BFS so missing roots surface as warnings up front. Handles servers (e.g. `test-server.brapi.org`) that reply 200 + `result: null` for unknown germplasm rather than 404.
- **Dataset handle truncation flags** — `DatasetHandle` now carries `truncated` and `maxRows` fields when the producing tool capped rows before exhausting upstream. `brapi_find_genotype_calls` and the shared `spillToDataset` helper plumb these through so consumers can detect partial datasets.

## Changed

- **`brapi_get_study` / `brapi_get_germplasm` companion lookups gate on capability profile** — pedigree, attribute, progeny, observation, observation-unit, and variable count probes now skip when the cached `/serverinfo` profile doesn't advertise the corresponding service. This avoids the per-request 404 storm against servers that only expose a subset of the spec.
- **Study FK count probes use top-level endpoints** — `brapi_get_study` and `brapi_submit_observations` now call `/observations?studyDbIds=…`, `/observationunits?studyDbIds=…`, and `/variables?studyDbId=…` (BrAPI v2.1 canonical routes) instead of `/studies/{id}/observations`, `/studies/{id}/observationunits`, `/studies/{id}/observationvariables`. `pageSize=0` was replaced with `pageSize=1` so servers that reject zero-page sizes still return a `totalCount`.
- **`brapi_find_observations` accepts structured `season` payloads** — output schema and distribution rendering now handle the BrAPI v2.1 `{seasonDbId, year, season, seasonName}` form alongside the legacy flat-string variant.
- **`brapi_find_variants` tolerates multi-set membership** — `variantSetDbId` accepts string OR string[] OR a separate `variantSetDbIds` plural form per spec drift across servers; `variantType`, `referenceBases`, `alternateBases`, `referenceName`, `start`, `end`, and the filter fields are now `nullish()` to accept upstream nulls without dropping rows.
- **`brapi_get_image` requires GET on `images/{imageDbId}/imagecontent`** — many servers expose the endpoint for PUT-only upload; the tool now only routes to `/imagecontent` when the capability profile lists GET, otherwise falls back to the metadata `imageURL`.
- **`brapi_submit_observations` study existence check** — `fetchStudyName` is now `fetchStudyExistence`, throwing `study_not_found` for upstream 404s and propagating transient errors (timeouts, 5xx) rather than swallowing them into a missing study name.
- **Tool descriptions tightened** — `brapi_connect`, `brapi_find_genotype_calls`, `brapi_find_locations`, `brapi_find_variables`, `brapi_get_image`, `brapi_raw_get`, and `brapi_submit_observations` rewrote their headline lines to drop redundant or prescriptive copy.

## Fixed

- **`brapi_find_germplasm` free-text heuristic warning** — when the requested `text` filter would have narrowed the result and the server returned every row that matches the other filters, the tool now warns that the free-text filter may not be honored. Catches servers that silently ignore unrecognized query parameters.
- **`brapi_find_locations` bbox warnings** — surfaces a warning when a bbox window excludes every upstream row (likely a coordinate-convention mismatch) or some-but-not-all rows, instead of returning a quietly-empty list.
- **`brapi_find_genotype_calls` empty-result warning** — when a named filter (variant set, germplasm, callset, variant) was provided and upstream returned zero calls, surfaces a warning that the filter combination may not match data on this server.
- **`renderDatasetHandle` consolidation** — six `find_*` tools that duplicated the dataset-handle render block now share one helper, so `truncated` / `maxRows` lines surface consistently.
- **Test fixtures aligned with new routing** — `brapi_get_study`, `brapi_get_germplasm`, and `brapi_submit_observations` test mocks now advertise the capabilities the production code consults and respond on the top-level routes the tools actually call.
