---
summary: "Resolver field-name fix for BrAPI v2.1 trait subobjects + bbox swap-on-zero detection for non-conformant GeoJSON deployments."
breaking: false
---

# 0.3.9 — 2026-04-29

Two patch-level field fixes from running the read surface against the BrAPI Community Test Server. `find_variables` free-text queries now surface candidates when the match lives on `trait.traitName` (the v2.1 spec key) rather than only the variable name. `find_locations` bbox filtering survives servers that store GeoJSON coordinates as `[lat, lon, alt]` instead of the spec-required `[lon, lat, alt]`.

## Added

- **`CoordinateAxisOrder` type + `axisOrder` parameter on `extractCoordinates` (`src/mcp-server/tools/shared/find-helpers.ts`)** — `'spec'` (default) follows the GeoJSON RFC 7946 `[lon, lat, alt?]` convention; `'swapped'` reads the array as `[lat, lon, alt?]` for non-conformant deployments. Legacy top-level `latitude`/`longitude` fields are unambiguous by name and not affected.
- **`hasPointGeometry` helper** — true when a record carries a GeoJSON `Point` with a 2- or 3-element numeric coordinate array. Used by the bbox swap-on-zero retry to skip Polygon-only result sets where reinterpretation can't help.
- **`coordinateAxisOrder` field on `brapi_find_locations` output** — surfaces which axis interpretation the bbox filter and renderer used, so callers can correlate displayed coordinates with the warning when a swap fired.
- **Tests** — new `tests/services/ontology-resolver.test.ts` (8 cases) covers PUI / name / synonym paths, the v2.1 trait-name path, legacy alias fallback, ranking order, and `ontologyDbId` scope. New cases in `brapi-find-variables.tool.test.ts` (trait-only text match) and `brapi-find-locations.tool.test.ts` (Cornell-shape swap recovery, spec-correct preserved, both readings empty, polygon-only skip).

## Changed

- **`brapi_find_locations` bbox handler** — when the spec-correct reading produces zero matches and at least one row carries a Point geometry, retries the filter once in-memory with axes swapped. On recovery, sets `coordinateAxisOrder: "swapped"` and emits a dedicated warning naming the deployment as non-conformant. Falls back to the existing "verify the latitude/longitude window" warning when both readings empty or no Point rows present.
- **`brapi_find_locations` renderer** — threads `result.coordinateAxisOrder` into `extractCoordinates` so printed coordinates match the warning rather than contradicting it.

## Fixed

- **`brapi_find_variables` free-text query returned empty `ontologyCandidates` when the match lived on `trait.traitName`** — `OntologyResolver` (`src/services/ontology-resolver/ontology-resolver.ts`) read `trait.name` and `trait.description`, but BrAPI v2.1 servers populate `trait.traitName` and `trait.traitDescription`. Both lookups returned undefined, so candidates were never built and the v0.3.8 dbId-promotion path stayed silently dormant. `scoreOne` and `toCandidate` now read both v2.1 keys with `?? ` fallback to the legacy aliases. Closes [#3](https://github.com/cyanheads/brapi-mcp-server/issues/3).
- **`brapi_find_locations` bbox excluded all rows on servers that store GeoJSON Points as `[lat, lon, alt]`** — the BrAPI Community Test Server is a known offender (Cornell at 42.44°N, -76.46°W stored as `[42.44423, -76.46313, 123]`). The retry-once-with-swap path described above recovers matches without operator pre-knowledge while keeping the spec-conformant default. Closes [#4](https://github.com/cyanheads/brapi-mcp-server/issues/4).
