---
summary: "Per-alias env-var fallback for brapi_connect — credentials live in BRAPI_<ALIAS>_* and never enter the LLM context. Plus a Breedbase /token form-encoding fix that unblocks SGN auth against Cassavabase et al."
breaking: false
---

# 0.3.1 — 2026-04-29

Connect-time credentials no longer have to ride through agent input. `brapi_connect` now fills `baseUrl` and `auth` from `BRAPI_<ALIAS>_*` env vars (then `BRAPI_DEFAULT_*`) when the agent omits them, so the LLM can call `brapi_connect({ alias: 'cassava' })` with nothing else and the server resolves the rest from `.env`. This is meaningful for security: passwords stop being prompt-engineered into every session.

Also fixes a real bug — the SGN/Breedbase token-exchange request was sending JSON, but Breedbase's `/token` follows the OAuth2 spec and requires `application/x-www-form-urlencoded`. SGN auth now works against Cassavabase, Sweetpotatobase, Yambase, Musabase, Solgenomics, etc.

## Added

- **Env-var fallback for `brapi_connect`** — `baseUrl` and `auth` are now optional inputs. When omitted, the handler resolves them via the new `resolveConnectInput` helper with three layers of precedence:
  1. Explicit agent input (always wins).
  2. Per-alias env vars: `BRAPI_<ALIAS>_*` where the alias name is uppercased and hyphens become underscores (`my-server` → `BRAPI_MY_SERVER_*`).
  3. Default env vars: `BRAPI_DEFAULT_*` — only consulted when alias differs from `default`.
- **Per-alias credential families.** Each alias's env vars can carry one credential family — the auth mode is derived from which fields are set:
  - `_USERNAME` + `_PASSWORD` → `mode: 'sgn'` (Breedbase token exchange)
  - `_BEARER_TOKEN` → `mode: 'bearer'`
  - `_API_KEY` (+ optional `_API_KEY_HEADER`) → `mode: 'api_key'`
  - `_OAUTH_CLIENT_ID` + `_OAUTH_CLIENT_SECRET` (+ optional `_OAUTH_TOKEN_URL`) → `mode: 'oauth2'`
  - None → `mode: 'none'`

  Setting multiple families within a single alias raises a `ValidationError` naming the conflict.
- **`src/config/alias-credentials.ts`** module exposes `aliasEnvPrefix`, `readAliasCredentials`, `deriveAuthFromCredentials`, and `resolveConnectInput` — all unit-tested with 28 cases covering the resolution matrix.
- **`.env.example`** documents the per-alias pattern with examples for `default`, `cassava`, `sweetpotato`, `t3wheat`, `gigwa`, plus an API-key example (`prod`) and a bearer-token example (`cgiar`).

## Changed

- **`brapi_connect.input` schema** — `baseUrl` and `auth` are now `optional()` rather than required / defaulted. Tool description and field descriptions updated to point at the env-var fallback. Existing callers that pass both fields explicitly continue to work unchanged.
- **`brapi_connect.handler`** logs the auth source (`'agent'` vs `'env'`) alongside the existing alias / baseUrl / authMode fields.

## Fixed

- **SGN token exchange against Breedbase servers.** `defaultTokenFetcher` now POSTs `application/x-www-form-urlencoded` with `URLSearchParams`-encoded credentials instead of JSON. Breedbase's `/token` endpoint follows the OAuth2 password-grant spec and rejects JSON bodies (`HTTP 400 "JSON array body required"`); the JSON form would silently fail against every SGN-family server. Verified end-to-end against Cassavabase (`access_token` issued, `/serverinfo` returns 118 supported calls).

## Removed

- **`dev:stdio` and `dev:http` package.json scripts.** Both wrapped `tsx --watch` — a long-running foreground process that's incompatible with agent-driven validation (an agent invoking it either blocks indefinitely or backgrounds the process and mistakes silence for success). Finite verification paths remain: `bun run test`, `bun run devcheck`, and JSON-RPC against a one-shot built binary via the `field-test` skill. Humans who want hot-reload can run `tsx --watch src/index.ts` directly.
