Skip to content

io.github.cyanheads/brapi-mcp-server

by cyanheads · remote · local (npm) · no sign-in to list tools · v0.10.1 · published by GitHub user cyanheads

Collaborative BrAPI v2.1 MCP workspace: studies, germplasm, genotypes across Breedbase, T3, more.

Changes · 7

22 tools: 7 change things and 15 only read.

Scanned Oct 21, 2026

7 checks matched something in the tool definitions. No finding is not a safety guarantee. How the checks work.

claude mcp add --transport http brapi https://brapi.caseyjhand.com/mcp

Findings

Matches of fixed checks, with the text they matched.

3 warn4 info
  • The description contains "other server", wording about other tools or servers.

    Matched text

    description: "...on first use; call this to connect any other server by `baseUrl`, to pass `auth`, or to re-..."

    What this check looks for
  • The description contains "Other servers", wording about other tools or servers.

    Matched text

    description: "...embedded measurements, including zeros. Other servers discover up to 200 studies before pulli..."

    What this check looks for
  • The server marks "brapi_dataframe_describe" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

    Matched text

    annotations: {"readOnlyHint":true,"idempotentHint":true,"openWorldHint":false}; The name "brapi_dataframe_describe" says "describe": a word for reading.

    What this check looks for
  • The description mentions "Credentials".

    Matched text

    description: "...ncurrent connections via named aliases. Credentials can be configured server-side and omitt..."

    What this check looks for
  • The input schema at properties.auth.description mentions "credentials".

    Matched text

    inputSchema.properties.auth.description: "Auth payload. Omit to use credentials configured server-side for this alias (..."

    What this check looks for
  • The input schema at properties.auth.oneOf[4].properties.mode.description mentions "credentials".

    Matched text

    inputSchema.properties.auth.oneOf[4].properties.mode.description: "OAuth2 client-credentials flow; exchanged for an access token at..."

    What this check looks for
Show 1 more finding
  • The input schema at properties.auth.oneOf[4].description mentions "credentials".

    Matched text

    inputSchema.properties.auth.oneOf[4].description: "OAuth2 client-credentials variant."

    What this check looks for

Tools

Open a tool for its reasons and full description. Descriptions are the publisher's own words, shown as they are.

22 tools

Changes things 7Claude Code: ask
  • brapi_build_phenotype_matrixPull observations across one or more studies and pivot them into a germplasm × trait matrix materialized as a canvas dataframe.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Pull observations across one or more studies and pivot them into a germplasm × trait matrix materialized as a canvas dataframe. Returns a dataframe handle (read it with brapi_dataframe_describe and brapi_dataframe_query) plus a summary of dimensions and aggregate method. Long-form output is suitable for downstream GROUP BY analysis by study, germplasm, or variable. A study whose pull fails is named in warnings and the rest still build the matrix; the call fails only when every study failed.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • brapi_connectOpen a connection to a BrAPI v2 server, authenticate, and return the full orientation envelope (server identity, capability profile, content summary, suggested next tools).5 findings

    Why it is in this group

    • The description's first verb is "open": a word for a change.

    Findings

    • Info

      The description mentions "Credentials".

      Matched text

      description: "...ncurrent connections via named aliases. Credentials can be configured server-side and omitt..."

      What this check looks for
    • Info

      The input schema at properties.auth.description mentions "credentials".

      Matched text

      inputSchema.properties.auth.description: "Auth payload. Omit to use credentials configured server-side for this alias (..."

      What this check looks for
    • Info

      The input schema at properties.auth.oneOf[4].properties.mode.description mentions "credentials".

      Matched text

      inputSchema.properties.auth.oneOf[4].properties.mode.description: "OAuth2 client-credentials flow; exchanged for an access token at..."

      What this check looks for
    • Info

      The input schema at properties.auth.oneOf[4].description mentions "credentials".

      Matched text

      inputSchema.properties.auth.oneOf[4].description: "OAuth2 client-credentials variant."

      What this check looks for
    • Warn

      The description contains "other server", wording about other tools or servers.

      Matched text

      description: "...on first use; call this to connect any other server by `baseUrl`, to pass `auth`, or to re-..."

      What this check looks for
    Description, as the server wrote it
    Open a connection to a BrAPI v2 server, authenticate, and return the full orientation envelope (server identity, capability profile, content summary, suggested next tools). Other BrAPI tools connect an alias the server has a URL configured for (a built-in or operator-configured alias, or `default` on a deployment with a default server) on first use; call this to connect any other server by `baseUrl`, to pass `auth`, or to re-point an alias. Supports multiple concurrent connections via named aliases. Credentials can be configured server-side and omitted from this call. When a request carries no MCP session on a deployment without per-user auth, aliases live in one namespace shared by every such caller: re-registering an alias re-points their later calls to it. Built-in known servers (callable with no `baseUrl` or `auth` — public BrAPI v2 endpoints): `bti-breedbase-demo`, `bti-cassava`, `bti-sweetpotato`. Operator-configured aliases on this deployment (credentials and/or baseUrl read from server env vars): `default`, `cassava`. Aliases are shortcuts only; any other BrAPI v2 server is reachable by passing `baseUrl` directly.

    Annotations the server declares

    • openWorldHint: true
    • readOnlyHint: false
    • idempotentHint: true
  • brapi_dataframe_describeStart here after a spillover.1 finding

    Why it is in this group

    • The name "brapi_dataframe_describe" says "describe": a word for reading.
    • The description's first verb is "start": a word for a change.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "brapi_dataframe_describe" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

      Matched text

      annotations: {"readOnlyHint":true,"idempotentHint":true,"openWorldHint":false}; The name "brapi_dataframe_describe" says "describe": a word for reading.

      What this check looks for
    Description, as the server wrote it
    Start here after a spillover. Lists dataframes (or describes one) with columns, row counts, and originating-source provenance. The dataframe name appears inline on every find_* response that spilled (`result.dataframe.tableName`) — pass it as `dataframe` to inspect schema and provenance before writing the first brapi_dataframe_query. On a shared HTTP endpoint without per-caller auth, listing without a name is available only where each MCP session has its own workspace, and never to a request without a session; pass a known name instead.

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
    • openWorldHint: false
  • brapi_dataframe_queryRun SQL across in-memory dataframes.No findings

    Why it is in this group

    • The name "brapi_dataframe_query" says "query": a word for reading.
    • The description's first verb is "run": a word for a change.
    • The input takes a statement or command to run (input.sql), which can change anything it can reach.
    Description, as the server wrote it
    Run SQL across in-memory dataframes. Dataframes auto-populate when find_* tools spill (named `df_<uuid>`) — the dataframe name appears inline on every find_* response that spilled (`result.dataframe.tableName`), so the typical flow is find_* → read the name → query here. Use brapi_dataframe_describe to inspect schema and provenance for a known name. SELECT only — writes/DDL/COPY/PRAGMA/ATTACH/file-reads are rejected. Use SQL as the paging idiom: `LIMIT/OFFSET` to walk results, projection to trim columns, aggregation to summarize. Use `registerAs` to chain — the result lands as a new dataframe.

    Annotations the server declares

    • readOnlyHint: false
    • idempotentHint: false
    • openWorldHint: false
  • brapi_export_genotype_matrixPull genotype calls for a germplasm × variant set and pivot them into a matrix.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Pull genotype calls for a germplasm × variant set and pivot them into a matrix. `matrix-json` registers a wide canvas dataframe; `vcf-lite` adds VCF-subset text in `vcf`; `plink` adds .ped/.map text in `ped`/`map`. Text formats pull /variants metadata and convert zero-based BrAPI start to one-based positions; missing/invalid positions use `.`/`0`. Read the dataframe with brapi_dataframe_describe and brapi_dataframe_query (SQL). `variantColumnLegend` maps SQL-safe columns to original variant IDs.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • brapi_germplasm_performanceAggregate a single germplasm's observations across studies, returning per-variable statistics (n, mean, median, sd, min, max), contributing studies, seasons, and collection coverag…1 finding

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The description contains "Other servers", wording about other tools or servers.

      Matched text

      description: "...embedded measurements, including zeros. Other servers discover up to 200 studies before pulli..."

      What this check looks for
    Description, as the server wrote it
    Aggregate a single germplasm's observations across studies, returning per-variable statistics (n, mean, median, sd, min, max), contributing studies, seasons, and collection coverage. SGN/Breedbase uses advertised germplasm-scoped observation units with embedded measurements, including zeros. Other servers discover up to 200 studies before pulling per study. Pass studyDbIds to skip discovery and restrict collection to known studies. Per-study observation limits, page limits, failures, and incomplete scans appear in coverage and warnings. For the underlying matrix, use brapi_build_phenotype_matrix.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • brapi_walk_pedigreeWalk germplasm ancestry or descendancy as a deduplicated DAG with cycle detection and depth limits.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Walk germplasm ancestry or descendancy as a deduplicated DAG with cycle detection and depth limits. leafCount counts graph terminals, confirmedLeafCount counts successfully verified leaves, and frontier records unresolved directions. Nodes and edges each fit loadLimit and a 24,000-byte preview budget; trimmed discovered graphs and already-fetched omitted relatives are staged for brapi_dataframe_describe and brapi_dataframe_query (SQL). Diagrams describe only the discovered graph, never unvisited relatives.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
Only reads 15Claude Code: allow
  • brapi_describe_filtersList the valid filter names for a BrAPI endpoint (studies, germplasm, observations, variables, images, variants, locations) — companion lookup for the `extraFilters` passthrough on…No findings

    Why it is in this group

    • The name "brapi_describe_filters" says "describe": a word for reading.
    Description, as the server wrote it
    List the valid filter names for a BrAPI endpoint (studies, germplasm, observations, variables, images, variants, locations) — companion lookup for the `extraFilters` passthrough on any `find_*` tool. Entries reflect the BrAPI v2.1 spec; individual servers may implement subsets.

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
  • brapi_find_genotype_callsPull genotype calls for a germplasm × variant set.No findings

    Why it is in this group

    • The name "brapi_find_genotype_calls" says "find": a word for reading.
    Description, as the server wrote it
    Pull genotype calls for a germplasm × variant set. Filter to bound cost — at minimum, set `variantSetDbId` or `germplasmDbIds`. The upstream pull is capped by deployment policy; when the pull is truncated, narrow the filters. results[] holds the leading calls up to `loadLimit` and a 24,000-byte budget; when it holds fewer than were collected, every collected call is materialized as a dataframe — read it with brapi_dataframe_describe and brapi_dataframe_query (SQL) instead of paging row-by-row.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • brapi_find_germplasmFind germplasm by name, synonym, accession number, PUI, crop, or free-text query.No findings

    Why it is in this group

    • The name "brapi_find_germplasm" says "find": a word for reading.
    Description, as the server wrote it
    Find germplasm by name, synonym, accession number, PUI, crop, or free-text query. Matches across registered synonyms. results[] holds the leading matches up to loadLimit and a 24,000-byte budget; when it holds fewer rows than matched, the matched set is materialized as a dataframe — read it with brapi_dataframe_describe and brapi_dataframe_query (SQL) instead of paging row-by-row.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • brapi_find_imagesFilter images by observation unit, observation, study, descriptive ontology term, file name, or MIME type.No findings

    Why it is in this group

    • The name "brapi_find_images" says "find": a word for reading.
    Description, as the server wrote it
    Filter images by observation unit, observation, study, descriptive ontology term, file name, or MIME type. Returns metadata only — use brapi_get_image to fetch bytes inline. results[] holds the leading matches up to loadLimit and a 24,000-byte budget; when it holds fewer rows than matched, the matched set is materialized as a dataframe — read it with brapi_dataframe_describe and brapi_dataframe_query (SQL).

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • brapi_find_locationsFind research stations / field sites by country, abbreviation, type, location ID, or free-text.No findings

    Why it is in this group

    • The name "brapi_find_locations" says "find": a word for reading.
    Description, as the server wrote it
    Find research stations / field sites by country, abbreviation, type, location ID, or free-text. Countries filter by ISO 3166-1 alpha-3 code via countryCodes, or by free-form English country name via countryNames (resolved client-side to alpha-3 — "Uganda" → "UGA"). Optional bbox parameter restricts rows to a latitude/longitude window. When no first-page row falls inside it under the spec GeoJSON [lon, lat, alt] reading and a row carries a Point geometry, bbox is retried with axes swapped; if that matches, the swapped reading is used (non-conformant servers store [lat, lon, alt]) with a warning and `coordinateAxisOrder: "swapped"`. results[] holds the leading matches up to loadLimit and a 24,000-byte budget; when it holds fewer rows than matched, the matched set is materialized as a dataframe — read it with brapi_dataframe_describe and brapi_dataframe_query (SQL).

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • brapi_find_observationsPull observation records filtered by study, germplasm, variable, season, or observation unit.No findings

    Why it is in this group

    • The name "brapi_find_observations" says "find": a word for reading.
    Description, as the server wrote it
    Pull observation records filtered by study, germplasm, variable, season, or observation unit. Without a studies, trials, observationUnits, or observations anchor, a GET query first runs a one-row count probe and skips the bulk pull (warnings says so) when the probe fails or reports more than 5,000 matches. results[] holds the leading matches up to loadLimit and a 24,000-byte budget; when those bounds cut it short, the matched set is materialized as a dataframe — read it with brapi_dataframe_describe and brapi_dataframe_query (SQL).

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • brapi_find_studiesLocate studies matching crop, trial type, season, location, or program.No findings

    Why it is in this group

    • The name "brapi_find_studies" says "find": a word for reading.
    Description, as the server wrote it
    Locate studies matching crop, trial type, season, location, or program. Enriches results with program/trial/location context in one call. results[] holds the leading matches up to loadLimit and a 24,000-byte budget; when it holds fewer rows than matched, the matched set is materialized as a dataframe — read it with brapi_dataframe_describe and brapi_dataframe_query (SQL).

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • brapi_find_variablesFind observation variables (traits) by name, trait class, ontology term, or free-text query.No findings

    Why it is in this group

    • The name "brapi_find_variables" says "find": a word for reading.
    Description, as the server wrote it
    Find observation variables (traits) by name, trait class, ontology term, or free-text query. Free-text queries are ranked against every fetched row by PUI, name, synonym, and trait text; a candidate carries the ontology PUI of its row as termId when the row has one. results[] holds the leading rows up to loadLimit and a 24,000-byte budget; when it holds fewer rows than matched, the matched set is materialized as a dataframe — read it with brapi_dataframe_describe and brapi_dataframe_query (SQL).

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • brapi_find_variantsFind variant records by variant set, reference sequence, or genomic region (start/end, 0-based inclusive / exclusive per the BrAPI spec; servers may differ).No findings

    Why it is in this group

    • The name "brapi_find_variants" says "find": a word for reading.
    Description, as the server wrote it
    Find variant records by variant set, reference sequence, or genomic region (start/end, 0-based inclusive / exclusive per the BrAPI spec; servers may differ). results[] holds the leading matches up to loadLimit and a 24,000-byte budget; when it holds fewer rows than matched, the matched set is materialized as a dataframe — read it with brapi_dataframe_describe and brapi_dataframe_query (SQL).

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • brapi_get_germplasmFetch a single germplasm by DbId with attributes and direct parents.No findings

    Why it is in this group

    • The name "brapi_get_germplasm" says "get": a word for reading.
    Description, as the server wrote it
    Fetch a single germplasm by DbId with attributes and direct parents. Response companions report study count, direct parent count, and direct descendant count — signals for pedigree depth and observation coverage.

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
  • brapi_get_imageFetch image bytes for up to 5 imageDbIds and return them inline as `type: image` content blocks.No findings

    Why it is in this group

    • The name "brapi_get_image" says "get": a word for reading.
    Description, as the server wrote it
    Fetch image bytes for up to 5 imageDbIds and return them inline as `type: image` content blocks. Falls back to the metadata `imageURL` when the server lacks dedicated image-content delivery or that request fails. No filesystem side-effects.

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
    • openWorldHint: true
  • brapi_get_studyFetch a single study by DbId with its program, trial, and location records resolved where those lookups succeed.No findings

    Why it is in this group

    • The name "brapi_get_study" says "get": a word for reading.
    Description, as the server wrote it
    Fetch a single study by DbId with its program, trial, and location records resolved where those lookups succeed. Response includes cheap observation/observation-unit/variable counts as drill-down signals.

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
  • brapi_raw_getPassthrough to BrAPI GET /{path} without foreign-key resolution.No findings

    Why it is in this group

    • The name "brapi_raw_get" says "get": a word for reading.
    Description, as the server wrote it
    Passthrough to BrAPI GET /{path} without foreign-key resolution. List results (result array or result.data) return a leading preview within loadLimit and a 24,000-byte budget on each serialized row representation; an oversized first row returns no inline rows. Omitted fetched rows remain in a dataframe readable with brapi_dataframe_describe and brapi_dataframe_query. Explicit params.page/pageSize fetches only that upstream page, staging it if trimmed. Non-list results and envelope siblings are preserved. Emits a suggestion when a curated tool covers the endpoint.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • brapi_server_infoReturn the full orientation envelope for a BrAPI connection — server identity, capabilities, content counts, suggested finders, and notes.No findings

    Why it is in this group

    • The description's first verb is "return": a word for reading.
    Description, as the server wrote it
    Return the full orientation envelope for a BrAPI connection — server identity, capabilities, content counts, suggested finders, and notes. Pass forceRefresh to refetch the cached capability scan; pass an alias to read a non-default connection.

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true

This is not a safety rating, and no finding is not a safety guarantee. The groups and findings come from fixed checks of the tool definitions the server returned on the date shown, not from its code or what it does when called. How the checks work.

Install

Pinned to version 0.10.1. Values in angle brackets are yours to fill in.

claude mcp add --transport http brapi https://brapi.caseyjhand.com/mcp

Run in a terminal.

Claude Code permissions

Allow the reads, ask before a change, deny the deletes. Paste into .claude/settings.json.

.claude/settings.json
{
  "permissions": {
    "allow": [
      "mcp__brapi__brapi_server_info",
      "mcp__brapi__brapi_describe_filters",
      "mcp__brapi__brapi_find_studies",
      "mcp__brapi__brapi_get_study",
      "mcp__brapi__brapi_find_germplasm",
      "mcp__brapi__brapi_get_germplasm",
      "mcp__brapi__brapi_find_variables",
      "mcp__brapi__brapi_find_observations",
      "mcp__brapi__brapi_find_images",
      "mcp__brapi__brapi_get_image",
      "mcp__brapi__brapi_find_locations",
      "mcp__brapi__brapi_find_variants",
      "mcp__brapi__brapi_find_genotype_calls",
      "mcp__brapi__brapi_raw_get",
      "mcp__brapi__brapi_raw_search"
    ],
    "ask": [
      "mcp__brapi__brapi_connect",
      "mcp__brapi__brapi_build_phenotype_matrix",
      "mcp__brapi__brapi_walk_pedigree",
      "mcp__brapi__brapi_germplasm_performance",
      "mcp__brapi__brapi_export_genotype_matrix",
      "mcp__brapi__brapi_dataframe_describe",
      "mcp__brapi__brapi_dataframe_query"
    ],
    "deny": []
  }
}

Tools that only read are allowed, tools that change things ask first, tools that delete or cancel are denied. It assumes the server is installed as brapi. Review it before you use it.