Resources
+1
- + brapi-guide
io.github.cyanheads/brapi-mcp-server
9 changes recorded. MCP Harbor re-reads this listing from the official registry every six hours and asks the live server what it exposes, and keeps what changed between one look and the next.
https://raw.githubusercontent.com/cyanheads/brapi-mcp-server/HEAD/AGENTS.md
− **Version:** 0.8.1− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.11`− **MCP SDK:** `@modelcontextprotocol/server` ^2.2.0 (protocol revisions 2026-07-28 and 2025-*)− **Zod:** ^4.6.5− `brapi_connect` is the session handshake. It registers the BrAPI server under a named alias, forces a capability refresh, and inlines the full orientation envelope so one call orients the agent. `baseUrl` and `auth` are both `optional()` — when omitted, `resolveConnectInput` fills them from `BRAPI_<ALIAS>_*` then `BRAPI_DEFAULT_*` env vars, so credentials never enter the LLM context. Same envelope is available on-demand via `brapi_server_info`.− `find_*` tools share a pattern: pull one page capped at `loadLimit`, compute distributions across the returned rows, and if the upstream total exceeds `loadLimit` materialize the full union as a canvas dataframe and return a handle. Spilled rows live in DuckDB only — there is no parallel JSON store. Canvas is mandatory: startup fails closed when `core.canvas` is undefined.− 'Find germplasm by name, synonym, accession, PUI, crop, or free-text. Spills to a canvas dataframe when the upstream total exceeds loadLimit — query with brapi_dataframe_query (SQL).',− const connection = await getServerRegistry().get(ctx, input.alias ?? DEFAULT_ALIAS);− const filters = mergeFilters(/* named + extraFilters */, warnings);− const firstPage = await loadInitialPage(client, connection, '/germplasm', filters, loadLimit, ctx);− const { fullRows, dataframe } = await maybeSpill({− firstPage, client, connection, bridge,− return { /* results + distributions + refinementHint + dataframe? */ };− | `ctx.enrich` | Success-path agent context. `brapi_dataframe_query` and `brapi_build_phenotype_matrix` disclose capped results with `ctx.enrich.truncated({ shown, cap, guidance })`. |− `ctx.fail(reason, …)` is the typed thrower keyed off declared `errors[]` contracts — used by 15 tools and 1 resource today. The framework fills the matching contract entry's recovery hint into `data.recovery.hint` on the wire, for service throws carrying `data.reason` too. `ctx.content` is unused — no tool emits media blocks outside `brapi_get_image`, which returns image bytes through its own output schema.− **Default for new tools: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable?, severity?, thrownBy? }]` on `tool()` / `resource()` to receive a typed `ctx.fail(reason, …)` keyed by the declared reason union. TypeScript catches `ctx.fail('typo')` at compile time, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) — the single source of truth for the agen− brapi-walk-pedigree.tool.ts # BFS DAG walk (ancestors / descendants / both) with cycle detection− brapi-find-variants.tool.ts # find_* — variants, 1-based inclusive/exclusive genomic region− **Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the changelog entry plus a gates section); `release-pr-review` reviews and fixes on that branch (each fix an ordinary commit on top of the stack, pushed plainly — nothing already pushed is ever rewritten, so `main` keeps the record of w− - [ ] BrAPI tool: resolves connection via `ServerRegistry.get(ctx, alias ?? DEFAULT_ALIAS)` before touching the client− - [ ] BrAPI tool with dataframe spillover: rows beyond `loadLimit` materialize as a `df_<uuid>` canvas table via `CanvasBridge.registerDataframe`, handle surfaces in `result.dataframe`, `hasMore` set correctly+ **Version:** 0.10.1+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`+ **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1 (protocol revisions 2026-07-28 and 2025-*)+ **Zod:** ^4.6.7+ `brapi_connect` registers a BrAPI server under a named alias, forces a capability refresh, and inlines the full orientation envelope so one call orients the agent. `baseUrl` and `auth` are both `optional()` — when omitted, `resolveConnectInput` fills them from `BRAPI_<ALIAS>_*` then `BRAPI_DEFAULT_*` env vars, so credentials never enter the LLM context. Same envelope is available on-demand via `brapi_server_info`.+ Every other connection-scoped tool and resource resolves its connection through `requireRegisteredConnection` (`tools/shared/find-helpers.ts`). An alias with no registration in the caller's scope but a URL of its own — `BRAPI_<ALIAS>_BASE_URL` (`BRAPI_DEFAULT_BASE_URL` for `default`) or an enabled built-in — is registered on first use as an argument-free `brapi_connect` would register it: token exchange, capability profile (a cached one is reused), then save, with concurrent first calls in one s+ `find_*` tools share a pattern: pull one page (usually capped at `loadLimit`; germplasm text scans use configured `pageSize`), walk the remaining pages when it shows more exist (the upstream `totalCount` exceeds it, or, with no `totalCount`, it came back full), compute distributions across the full matched set, and return the leading rows within both `loadLimit` and the 24,000-byte inline budget (`INLINE_RESULTS_BUDGET_BYTES`, via `selectInlineRows`). Whenever `results` holds fewer rows than mat+ 'Find germplasm by name, synonym, accession, PUI, crop, or free-text. results[] holds the leading matches up to loadLimit and a 24,000-byte budget; when it holds fewer rows than matched, the matched set spills to a canvas dataframe — query with brapi_dataframe_query (SQL).',+ const connection = await requireRegisteredConnection(ctx, input.alias);+ const merged = mergeFilters(/* named + extraFilters */, warnings);+ const { filters } = applyDialectFiltersOrFail(ctx, dialect, 'germplasm', merged, warnings);+ const route = resolveFindRoute({ profile, dialect, endpoint: 'germplasm', filters, searchBody: merged, warnings });+ const firstPage = await loadInitialFindPage(client, connection, route, loadLimit, ctx,+ input.text ? { pageSize: getServerConfig().pageSize } : {});+ const spill = await maybeSpill({+ firstPage, client, connection, bridge, warnings, route,+ discloseInlineTrim(ctx, spill, loadLimit);+ return { results: spill.results, hasMore: spill.hasMore /* + distributions over spill.fullRows + dataframe? */ };+ ### Raw and SQL previews+ `brapi_raw_get`, `brapi_raw_search`, and `brapi_dataframe_query` use `selectPreviewRows` to bound both the UTF-8 JSON array and serialized rendered row section to 24,000 bytes, including escaping and markers. These previews return zero rows when the first row is oversized; finders retain their first-row exception through `selectInlineRows`. Raw tools stage every omitted fetched row, preserve envelope siblings, and never auto-walk explicit upstream pages. Query keeps full `registerAs` materializa+ | `ctx.enrich` | Success-path agent context. `brapi_dataframe_query`, `brapi_build_phenotype_matrix`, and the seven filter finders (through `discloseInlineTrim`) disclose capped results with `ctx.enrich.truncated({ shown, cap, guidance })`. |+ `ctx.fail(reason, …)` is the typed thrower keyed off declared `errors[]` contracts — declared by every tool except `brapi_dataframe_drop` (24) and all 6 resources today. The framework fills the matching contract entry's recovery hint into `data.recovery.hint` on the wire, for service throws carrying `data.reason` too. `brapi_get_image` emits bytes once through `ctx.content.image(data, mimeType)`; structured output and `format()` carry metadata only. Both byte sources normalize MIME case/paramete+ `brapi_walk_pedigree` keeps graph-terminal `leafCount` separate from `confirmedLeafCount` (successful empty lookups on all reached sides). Its bounded `frontier` identifies unresolved node/side outcomes; large frontiers stage separately. Nodes and edges each use `selectPreviewRows` with their actual renderer, retaining the discovered graph when trimmed. Safety-capped graph handles/provenance declare partial coverage; diagrams use only that discovered graph. Fetched omitted relatives stage separa+ **Default for new tools: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable?, severity?, thrownBy? }]` on `tool()` / `resource()` to receive a typed `ctx.fail(reason, …)` keyed by the declared reason union. TypeScript catches `ctx.fail('typo')` at compile time, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) — the single source of truth for the agen+ brapi-walk-pedigree.tool.ts # BFS DAG walk (ancestors / descendants / both), one edge per pair, loop-closing edges marked, optional diagram+ brapi-find-variants.tool.ts # find_* — variants, 0-based [start, end) genomic region+ pedigree-diagram.ts # Pure Mermaid / DOT renderer for walk_pedigree results (label escaping, Mermaid limits)+ **Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the release digest: theme line, `## Changes`, `## Gates`, changelog link last); `release-pr-review` reviews and fixes on that branch (each fix an ordinary commit on top of the stack, pushed plainly — nothing already pushed is ever rewri+ - [ ] BrAPI tool: resolves connection via `requireRegisteredConnection(ctx, input.alias)` before touching the client, and declares the reasons that resolution can raise: `unknown_alias`, `alias_base_url_invalid`, `private_address_blocked`, `auth_token_exchange_failed`, `auth_no_access_token`, `capability_discovery_unavailable`, `upstream_unauthorized`, `upstream_forbidden`+ - [ ] BrAPI tool with dataframe spillover: `results` is cut by `loadLimit` and the 24,000-byte inline budget (`selectInlineRows`); whenever it holds fewer rows than matched, the matched set materializes as a `df_<uuid>` canvas table via `CanvasBridge.registerDataframe`, the handle surfaces in `result.dataframe`, the cut is disclosed (`truncated` / `shown` / `cap` + notice), and `hasMore` is set correctly
https://raw.githubusercontent.com/cyanheads/brapi-mcp-server/HEAD/AGENTS.md
− **Version:** 0.8.0− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`− **MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revisions 2026-07-28 and 2025-*)− | `ctx.requestInput` / `ctx.inputs` | Multi-round-trip input. `brapi_submit_observations` gates apply-mode writes on a `confirm` elicitation: it reads `ctx.inputs.view('confirm')`, returns `ctx.requestInput({ inputRequests: … })` when the answer is missing, and treats a declined, cancelled, or unparseable answer as terminal (`user_declined`). `force: true` skips the round. |− `ctx.fail(reason, …)` is the typed thrower keyed off declared `errors[]` contracts — used by 15 tools and 1 resource today. `ctx.recoveryFor(reason)` resolves the matching contract entry's recovery hint into `data.recovery.hint` so it surfaces on the wire. `ctx.content` is unused — no tool emits media blocks outside `brapi_get_image`, which returns image bytes through its own output schema.− **Default for new tools: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` to receive a typed `ctx.fail(reason, …)` keyed by the declared reason union. TypeScript catches `ctx.fail('typo')` at compile time, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. The `recovery` field is required descriptive metadata (≥ 5 words, lint-validated); to surface it on the wire, spread `...ctx.reco− if (!conn) throw ctx.fail('unknown_alias', `No connection for ${input.alias}`,− { ...ctx.recoveryFor('unknown_alias') });− Skills are modular instructions in `framework-skills/` at the project root. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. Keep development skills out of root `skills/`: plugin hosts load that directory for installing agents.− | `devcheck` | Lint, format, typecheck, audit |− | `git-wrapup` | Land working-tree changes as a versioned commit stack; opens a release PR when the project declares release PR mode. |− | `release-pr-review` | Review an open release PR, land fixups, and keep its body current. Release PR mode only. |− | `release-and-publish` | Tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup`. |− | `api-linter` | Definition lint rule reference (`format-parity`, `schema-*`, `name-*`, `server-json-*`, …) |− | `api-mirror` | MirrorService: persistent SQLite-backed local mirror of a bulk upstream dataset with FTS5 — Tier 3 opt-in |− | `bun run audit:fix` | Upgrade vulnerable packages within existing ranges with `bun audit fix`; try before `bun update <name>` and `bun dedupe`. |− | `bun run audit:refresh` | Delete `bun.lock`, reinstall, re-audit. Last resort after in-place fixes; re-resolves ranged dependencies, including the framework. |− | `bun run format` | Auto-fix formatting via Biome |− | `bun run lint:mcp` | Validate MCP tool / resource / prompt definitions against the spec |− | `bun run lint:packaging` | Verify env var alignment between `manifest.json` and `server.json` |− | `bun run test` | Vitest suite |− - [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` = the unscoped repo name; `interface.shortDescription` from `package.json` description− - [ ] `.codex-plugin/mcp.json` updated — server name key is the unscoped repo name; user-supplied variables are listed in `env_vars`, never set to empty strings in `env`− - [ ] `.claude-plugin/plugin.json` populated — metadata from `package.json`; inline `mcpServers` entry keyed by the unscoped repo name; user-supplied variables declared under `userConfig` and referenced as `${user_config.<option>}`+ **Version:** 0.8.1+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.11`+ **MCP SDK:** `@modelcontextprotocol/server` ^2.2.0 (protocol revisions 2026-07-28 and 2025-*)+ **Zod:** ^4.6.5+ - **Cut noise.** Add only what earns its place: no speculative generality, no guards for states the framework already prevents (Zod-validated params, classified errors), no abstraction until a third caller proves it, no option nothing sets.+ | `ctx.requestInput` / `ctx.inputs` | Multi-round-trip input. `brapi_submit_observations` gates apply-mode writes on a `confirm` elicitation behind a consent record (`api-context` § Consent gates): asking stores `{ operation, clientId, subject, target, contentHash }` in `ctx.state` under `brapi/consent/<uuid>` (600 s TTL) and returns the id as `requestState`; each call first redeems (reads and deletes) the record its `requestState` names. An answer counts only when that record matches this calle+ `ctx.fail(reason, …)` is the typed thrower keyed off declared `errors[]` contracts — used by 15 tools and 1 resource today. The framework fills the matching contract entry's recovery hint into `data.recovery.hint` on the wire, for service throws carrying `data.reason` too. `ctx.content` is unused — no tool emits media blocks outside `brapi_get_image`, which returns image bytes through its own output schema.+ **Default for new tools: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable?, severity?, thrownBy? }]` on `tool()` / `resource()` to receive a typed `ctx.fail(reason, …)` keyed by the declared reason union. TypeScript catches `ctx.fail('typo')` at compile time, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) — the single source of truth for the agen+ if (!conn) throw ctx.fail('unknown_alias', `No connection for ${input.alias}`);+ Skills are modular instructions in `framework-skills/` at the project root. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry. The directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, so a server that ships `.claude-plugin/` or `.codex-plugin/` would hand these development skills to every agent that installs it. Keep `skills/` free for skills meant f+ | `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern, release commit on top. No tag, no push to main; opens the release PR when the project declares release PR mode |+ | `release-pr-review` | Review pass on an open release PR — simplifier + correctness review, fixes as ordinary commits on top of the stack, PR body kept in sync. Release PR mode only |+ | `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |+ | `api-linter` | Definition linter rule catalog — invoked by `bun run lint:mcp` and `devcheck` |+ | `api-mirror` | MirrorService: persistent self-refreshing local mirror (embedded SQLite + FTS5) of a bulk upstream dataset — Tier 3 opt-in |+ | `bun run audit:fix` | `bun audit fix` — upgrade vulnerable packages to the lowest safe version within existing ranges (`--dry-run` previews, `--latest` rewrites ranges). First response when `devcheck` flags a transitive advisory; then `bun update <name>`, then `bun dedupe` |+ | `bun run audit:refresh` | Delete `bun.lock` and reinstall. Last resort after `audit:fix`, `bun update <name>`, and `bun dedupe` — re-resolves every ranged dep (the framework pin included) and rewrites the lockfile as `lockfileVersion: 2` |+ | `bun run format` | Auto-fix formatting (safe fixes only) |+ | `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |+ | `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |+ | `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity, Dockerfile build platform (run by devcheck) |+ | `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |+ **CI is one file.** `.github/workflows/codeql.yml` (scaffolded) is the only GitHub Actions workflow: CodeQL is GitHub-owned end to end, and the file runs only while the repo's CodeQL *default setup* is turned off. Verification — `devcheck`, tests, the release gates — runs locally; don't add a workflow that re-runs it.+ - [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` = the unscoped repo name (never the npm scope — `lint:packaging` enforces this); `interface.shortDescription` from `package.json` description+ - [ ] `.codex-plugin/mcp.json` updated — server name key is the unscoped repo name; every user-supplied variable (API key, contact email, instance URL) is listed in `env_vars` so Codex forwards it from the user's environment. Never write `"KEY": ""` into `env` — an empty value replaces the user's exported key and is read as unset+ - [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `author`, `repository`, `license`, `keywords` from `package.json`; inline `mcpServers` entry keyed by the unscoped repo name. Every user-supplied variable is declared under `userConfig` (`type`, `title`, `description`; `sensitive: true` for keys and tokens; `required: true` or `default: ""`) and referenced from `env` as `"KEY": "${user_config.<option>}"` — mirror the `user_config` block in `manifest.json`. Never wri
We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect