Brapi Mcp Server

io.github.cyanheadsv0.8.1更新於 Oct 8, 2026

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

已驗證Streamable HTTP可網頁執行DatabasesData & Analytics

概覽

AI 產生的概覽

讓助理查詢 BrAPI v2.1 植物育種資料(研究、種質、系譜、觀測、基因型),並以 SQL 進行分析。

功能
連接 Breedbase、T3 等 BrAPI v2.1 伺服器,提供研究、種質、變數、觀測、影像、地點與變異的查找工具,以及詳情工具和系譜走訪。結果可溢出到 DuckDB 資料框架,助理可用唯讀 SQL 查詢、建立表型矩陣,或匯出為 CSV、Parquet、JSON。原始 GET 與 search 透傳可涵蓋沒有專用工具的端點,提示詞可引導研究探索性分析與跨研究統合分析。
適用情境
當助理需要探索或分析 BrAPI 相容伺服器上的育種試驗資料時使用:尋找研究與種質、追溯系譜、拉取觀測或基因型呼叫,以及組裝表型或基因型矩陣用於分析。
執行需求
可作為遠端 Streamable HTTP 端點執行,也可透過 npx、Bun 或 Docker 在本機執行;本機使用需要 Bun v1.4.0+ 或 Node.js v24+。內建別名無需憑證,但其他 BrAPI 伺服器需要基礎 URL 與透過環境變數提供的按別名憑證。選用設定包括 MCP_TRANSPORT_TYPE、MCP_HTTP_PORT、BRAPI_DEFAULT_BASE_URL 與 BRAPI_PAGE_SIZE。
安裝前請注意
除非設定 BRAPI_ENABLE_WRITES,否則寫入功能關閉;啟用後 brapi_submit_observations 可在預覽確認後建立或更新觀測記錄。資料框架匯出與刪除也需明確開啟。其他伺服器的憑證透過環境變數傳入,以免進入模型上下文。資料框架名稱相當於能力權杖,同一工作階段或租用戶桶中持有名稱的任何人都能讀取其資料列。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Brapi Mcp Server,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Web executable,透過 Streamable HTTP。 遠端服務在工作區中設定後即可從網頁執行環境執行。

其他 MCP 客戶端

把它新增到你客戶端的 mcpServers 設定中。

{
  "mcpServers": {
    "brapi-mcp-server": {
      "type": "http",
      "url": "https://brapi.caseyjhand.com/mcp"
    }
  }
}

README

@cyanheads/brapi-mcp-server

A collaborative BrAPI v2.1 workspace for multi-agent research via MCP. Search studies, germplasm, genotypes, & more - across Breedbase, T3, Sweetpotatobase, & any BrAPI v2-compliant server.

25 Tools • 6 Resources • 2 Prompts

Public Hosted Server: https://brapi.caseyjhand.com/mcp


Overview

Plant-breeding data from any BrAPI (Breeding API) v2 server, including Breedbase instances such as Cassavabase and Sweetpotatobase and the Triticeae Toolbox (T3), with several servers connected at once under named aliases. Search studies, germplasm, observations, genotype calls, images, locations, and variants, walk pedigrees, and build phenotype and genotype matrices; results past the per-call cap spill into a DuckDB dataframe workspace that agents in the same session query with SQL. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.

Tools

ToolDescription
brapi_connectAuthenticate to a BrAPI v2 server, register it under an alias, and return the orientation envelope
brapi_server_infoRe-fetch the orientation envelope for a registered alias, optionally refreshing capabilities
brapi_describe_filtersList valid filter names for an endpoint, for use in any finder's extraFilters
brapi_find_studiesFind studies by crop, trial type, season, location, or program
brapi_get_studyFetch a study with program, trial, and location resolved, plus companion counts
brapi_find_germplasmFind germplasm by name, synonym, accession, PUI, crop, or free text
brapi_get_germplasmFetch a germplasm with attributes, direct parents, and companion counts
brapi_walk_pedigreeWalk ancestry or descendancy as a deduplicated DAG with cycle detection
brapi_find_variablesFind observation variables by name, trait class, or ontology term, with free-text ranking
brapi_find_observationsPull observation records by study, germplasm, variable, season, or unit
brapi_find_imagesFind image metadata by unit, observation, study, ontology term, or MIME type
brapi_get_imageFetch up to 5 images inline as image content blocks
brapi_find_locationsFind research stations by country, type, abbreviation, or bounding box
brapi_find_variantsFind variants by variant set, reference, or genomic region
brapi_find_genotype_callsPull genotype calls through async search, bounded by a deployment pull ceiling
brapi_dataframe_describeList dataframes, or describe one with columns, row count, and provenance
brapi_dataframe_queryRun read-only SQL across dataframes
brapi_dataframe_dropOpt-in. Drop a dataframe by name
brapi_dataframe_exportOpt-in, stdio-only. Export a dataframe to disk as CSV, Parquet, or JSON
brapi_build_phenotype_matrixBuild a germplasm × trait matrix from one or more studies as a dataframe
brapi_germplasm_performancePer-variable aggregates (n, mean, median, sd, min, max) for one germplasm across its studies
brapi_export_genotype_matrixPivot a variant set's calls into a germplasm × variant matrix, with VCF-lite or PLINK text
brapi_submit_observationsOpt-in. Two-phase observation write: preview validates, apply confirms and writes
brapi_raw_getPassthrough to any GET /{path} endpoint no curated tool covers
brapi_raw_searchPassthrough to any POST /search/{noun} endpoint, with async polling handled

Resources

ResourceDescription
brapi://server/infoOrientation envelope for the default connection
brapi://callsRaw capability profile (/serverinfo + /calls) for the default connection
brapi://study/{studyDbId}One study with program, trial, and location resolved
brapi://germplasm/{germplasmDbId}One germplasm with attributes and parents
brapi://filters/{endpoint}Filter catalog for one endpoint
brapi://variable/{observationVariableDbId}One observation variable (trait, scale, method, ontology)

Every resource reads the default connection and mirrors a tool; tool-only clients and multi-server workflows use the tools.

Prompts

PromptDescription
brapi_eda_studyExploratory-data-analysis playbook for one study, ending in a structured report
brapi_meta_analysisCross-study meta-analysis playbook for a germplasm × trait combination

Capability reference

brapi_connect tool

  • baseUrl and auth are optional: omitted values come from BRAPI_<ALIAS>_* env vars, the built-in aliases, then BRAPI_DEFAULT_* (see Per-alias credentials); alias (default default, pattern ^[a-zA-Z0-9_-]+$) keeps several servers registered at once; auth.mode is none, bearer, api_key, sgn (Breedbase /token exchange), or oauth2 (client credentials)
  • Returns the orientation envelope: server identity, auth summary, capabilities (supported, notableGaps), active dialect, content counts, attribution for built-in servers, and nextToolSuggestions naming the entry-point finders this server can serve
  • Typed errors: auth_session_required, auth_base_url_mismatch, alias_base_url_unset, auth_token_exchange_failed, auth_no_access_token, upstream_unauthorized, upstream_forbidden; a failed connect leaves any earlier registration under the alias intact

brapi_server_info tool

  • alias optional; forceRefresh (default false) refetches the capability profile instead of reading the cache
  • Returns the same orientation envelope as brapi_connect

brapi_describe_filters tool

  • endpoint is one of studies, germplasm, observations, variables, images, variants, locations; unknown_endpoint carries availableEndpoints
  • Each filter has name, type, description, and example; the catalog follows the v2.1 spec, and individual servers may honor a subset

brapi_find_studies tool

  • Filters: crop, trialTypes, seasons, locations, programs, trials, studyNames, active
  • distributions over programName, studyType, seasons, locationName, commonCropName

brapi_get_study tool

  • studyDbId required; resolves program, trial, and location inline; study_not_found when the upstream has no such study
  • Companion counts observationCount, observationUnitCount, variableCount; a count the server can't scope to the study is omitted with a warning, never reported as the server-wide total

brapi_find_germplasm tool

  • Filters: names, germplasmDbIds, germplasmPUIs, accessionNumbers, crops, synonyms, collections, genus, species; text is a client-side substring match on name, accession, display name, and synonyms that drops non-matching rows, so pair it with a server-side filter
  • distributions over commonCropName, genus, species, collection, countryOfOriginCode

brapi_get_germplasm tool

  • germplasmDbId required; returns attributes and direct parents; germplasm_not_found when the upstream has no such germplasm
  • Companion counts studyCount, directParentCount, directDescendantCount

brapi_walk_pedigree tool

  • 1–20 root germplasmDbIds; direction is ancestors (default), descendants, or both; maxDepth 1–10 (default 3); the walk stops at 1,000 nodes and sets truncated
  • Deduplicated nodes and edges with depthReached, rootCount, leafCount, cycleCount, deadEndCount; past loadLimit, both sets spill to nodesDataframe and edgesDataframe

brapi_find_variables tool

  • Filters: variables, variableNames, variablePUIs, traitClasses, ontologies, studies, methods, scales, crop
  • text ranks the full result set and moves matches to the top without dropping the rest; ontologyCandidates lists the ranked matches, each with source (puiMatch, nameMatch, synonymMatch, traitClassMatch)
  • distributions over ontologyDbId, traitClass, scaleName

brapi_find_observations tool

  • Filters: studies, germplasm, variables, observationUnits, observations, seasons, programs, trials, observationLevels, timestampFrom / timestampTo
  • distributions over observationVariableName, studyName, germplasmName, observationLevel, season

brapi_find_images tool

  • Filters: images, observationUnits, observations, studies, imageFileNames, mimeTypes, descriptiveOntologyTerms; returns metadata only, with bytes via brapi_get_image
  • distributions over mimeType, studyName, observationUnitName, descriptiveOntologyTerms

brapi_get_image tool

  • 1–5 imageDbIds per call, up to 20 MB each; images_unsupported when the server doesn't advertise /images
  • Each image's source is imagecontent or the imageURL fallback; failed fetches land in per-image errors[] and suspect payloads (a non-image MIME type) in warnings[], so a partial batch still returns

brapi_find_locations tool

  • Filters: locations, locationNames, countryCodes (ISO 3166-1 alpha-3), countryNames (English names resolved to alpha-3), locationTypes, abbreviations; optional bbox needs all four of minLat, maxLat, minLon, maxLon and applies after the fetch
  • distributions over countryCode, locationType; coordinateAxisOrder: "swapped" reports a server that stores coordinates as [lat, lon]

brapi_find_variants tool

  • Filters: variantSets, variants, references, and a genomic region of referenceName + start (inclusive) / end (exclusive), 1-based
  • distributions over variantType, referenceName, variantSetDbId

brapi_find_genotype_calls tool

  • Needs at least one of variantSetDbId, variantSetDbIds, germplasmDbIds, callSetDbIds, variantDbIds (no_filters otherwise); optional callFormat (VCF, FLAPJACK, DARTSEQ, JSON)
  • distributions over callSetName, variantName, variantSetDbId, plus the server's callFormatting; search_endpoint_disabled when the active dialect marks POST /search/calls as dead
  • The upstream pull stops at BRAPI_GENOTYPE_CALLS_MAX_PULL (default 100,000) and sets truncated; loadLimit bounds only the inline preview

brapi_dataframe_describe tool

  • dataframe optional: omit to list every dataframe, or name one for columns, row count, and provenance (originating tool, baseUrl, query, expiry), which only auto-registered df_* tables carry
  • Listing without a name fails with list_all_disabled_on_shared_http on an HTTP deployment where every caller shares the default tenant

brapi_dataframe_query tool

  • sql is a single SELECT; writes, DDL, COPY, PRAGMA, ATTACH, file reads, and system-catalog reads fail as sql_rejected, with the specific reason in data.gateReason
  • Returns rowCount, typed columns, and rows bounded by preview (≤1,000), rowLimit, and BRAPI_CANVAS_MAX_ROWS; truncated, shown, cap, and notice disclose the cut
  • registerAs (identifier, ≤63 characters) saves the full result as a new dataframe for chaining

brapi_dataframe_drop tool

  • dataframe required; returns dropped: false, not an error, for an unknown name
  • Registered only when BRAPI_CANVAS_DROP_ENABLED=true

brapi_dataframe_export tool

  • format is csv, parquet, or json; optional columns or sql (mutually exclusive) and filename (no path separators or ..; omit for a timestamp-suffixed default); returns the absolute path, sizeBytes, and rowCount
  • Typed errors: export_dir_unset, dataframe_not_found, invalid_filename, mutually_exclusive_projection
  • Registered only over stdio with BRAPI_EXPORT_DIR set

brapi_build_phenotype_matrix tool

  • studies required (≥1), optional variables / germplasm subsets; shape wide (default) or long; aggregate mean (default), median, first, or all (always long form); loadLimit caps observations per study
  • Returns the matrix as a dataframe plus observationCount, germplasmCount, variableCount, and variableLegend mapping SQL-safe column names to variable names; truncated / cap flag a study that hit loadLimit; no_observation_path when neither /observations nor /observationunits returns data

brapi_germplasm_performance tool

  • germplasmDbId required; discovers its studies (up to 200) unless studyDbIds is supplied; optional variables subset; germplasm_not_found when the upstream has no such germplasm
  • perVariable rows carry n, mean, median, sd (omitted when n < 2 or non-numeric), min, max, studyCount, studyDbIds, and seasons

brapi_export_genotype_matrix tool

  • variantSetDbId required (no_filters otherwise), optional germplasmDbIds; format is matrix-json, vcf-lite (adds vcf text), or plink (adds ped / map text), and every format registers the germplasm × variant dataframe
  • variantColumnLegend maps SQL-safe column names back to variant IDs; search_endpoint_disabled when the active dialect marks POST /search/calls as dead
  • maxCalls / maxColumns can lower BRAPI_GENOTYPE_CALLS_MAX_PULL / BRAPI_GENOTYPE_MATRIX_MAX_COLUMNS but never raise them; truncated means a ceiling fired, and warnings names which

brapi_submit_observations tool

  • studyDbId plus 1–5,000 observations; a row with observationDbId updates via PUT, one without creates via POST, and nothing is deleted
  • mode: "preview" (default) returns valid, invalid, routing counts, and perRowWarnings without writing; mode: "apply" asks the caller to confirm (force: true skips it) and writes only on the round that redeems the server's record of that prompt, for the same caller, server, study, and rows — a pre-supplied or replayed answer is asked again — and returns posted, updated, and studyObservationCount; failures are observations_unsupported, study_not_found, post_unsupported, put_unsupported, and user_declined
  • Registered only when BRAPI_ENABLE_WRITES=true; requires the brapi:write:observations scope

brapi_raw_get tool

  • path is a relative route such as /samples (a full URL fails as cross_origin_path); optional params and loadLimit
  • Returns the raw envelope (url, metadata, result) plus a suggestion when a curated tool covers the endpoint; list results past loadLimit spill to a dataframe unless params.page / params.pageSize drive paging

brapi_raw_search tool

  • noun (e.g. observations, calls, germplasm) and a body posted verbatim to POST /search/{noun}; async searches are polled to completion
  • Returns kind (sync or async), searchResultsDbId, result, and a suggestion; spills like brapi_raw_get unless body.page / body.pageSize is set; search_endpoint_disabled when the active dialect marks the route as dead

brapi://server/info resource

  • Orientation envelope for the default connection as application/json
  • Same payload as brapi_server_info called with no arguments

brapi://calls resource

  • Capability profile for the default connection: supported services with their HTTP methods and versions, plus crops
  • Reflects what /serverinfo + /calls returned at the last load

brapi://study/{studyDbId} resource

  • Same payload as brapi_get_study on the default connection
  • study_not_found when the upstream has no such study

brapi://germplasm/{germplasmDbId} resource

  • Same payload as brapi_get_germplasm on the default connection
  • germplasm_not_found when the upstream has no such germplasm

brapi://filters/{endpoint} resource

  • Same payload as brapi_describe_filters; unknown_endpoint for an endpoint outside the catalog
  • Listing brapi://filters returns one resource per endpoint

brapi://variable/{observationVariableDbId} resource

  • The /variables/{id} record (trait, scale, method, ontology) on the default connection
  • variable_not_found when the upstream has no such variable

brapi_eda_study prompt

  • Arguments: studyDbId required; alias optional
  • Returns one user message: a six-step playbook (orient, variables, coverage, missing data, IQR outliers, optional pedigree walk) ending in a markdown report with recommended next steps

brapi_meta_analysis prompt

  • Arguments: germplasmDbIds (comma-separated) and traitName required; alias optional, run once per alias for multi-server analyses
  • Returns one user message: a seven-step playbook (resolve the trait, discover studies, build the observation table, harmonize scales, summarize per study and across studies, optional pedigree walk) ending in a report that cites every dataframe handle or filter map used

Features

Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.

BrAPI-specific:

  • Shared finder contract: the seven filter finders (studies, germplasm, variables, observations, images, locations, variants) take alias, loadLimit, and extraFilters (keys from brapi_describe_filters), and return results, hasMore, distributions, and an optional dataframe handle
  • Dataframe spillover: past loadLimit, finders page the rest of the result (up to 50 pages and 50,000 rows) into a DuckDB df_<uuid> table and return its handle
  • Dialect adapters (spec, brapi-test, breedbase, cassavabase, bms), detected per connection, translate v2.1 plural filters into the form each server family honors, drop filters it ignores, and switch to POST /search/{noun} when a GET would narrow a multi-value filter; BRAPI_<ALIAS>_DIALECT pins one
  • Several connections at once under named aliases, with three public Breedbase servers built in and credentials resolved per alias from env vars, so they stay out of the LLM context
  • Capability-aware calls: each connection's /serverinfo + /calls profile is cached and checked before a tool calls an endpoint; a per-connection concurrency cap and exponential-backoff retries cover 429/5xx

Agent-friendly output:

  • Typed failures: every connection-scoped tool and resource fails with unknown_alias until brapi_connect registers the alias, and a filter finder or brapi_build_phenotype_matrix fails with all_filters_dropped instead of widening to an unfiltered pull when the dialect drops every filter supplied
  • Query echo on every finder: totalCount, returnedCount, the exact appliedFilters sent upstream, a refinementHint on broad results, an empty-result notice, and warnings
  • Graceful partial failure: brapi_get_image returns per-image errors[] and warnings[] rows instead of failing the batch
  • Discriminated outputs: brapi_submit_observations returns a mode-discriminated result (preview / apply), brapi_raw_search reports kind, and brapi_get_image reports each image's source

Working with dataframes

When a finder's upstream total exceeds loadLimit, the response carries a dataframe handle: tableName, rowCount, columns, createdAt, expiresAt, plus truncated, maxRows, and totalCount when a cap fired. Columns renamed to pass the SQL identifier check map back to their upstream keys in columnLegend.

text
1. brapi_find_observations { studies: ["s-422"] }   → first-page rows inline + dataframe.tableName = "df_<uuid>" (when totalCount > loadLimit)2. brapi_dataframe_describe { dataframe: "df_<uuid>" }   → schema + provenance (originating tool, baseUrl, query, expiry)3. brapi_dataframe_query { sql: "SELECT germplasmName, value FROM df_<uuid> WHERE observationVariableDbId = 'V1' LIMIT 100" }   → typed columns + bounded rows

Dataframe names are capability tokens, not row-level ACLs: anyone holding a name in the same session or tenant bucket (see Deployment shapes) can read its rows. Provenance lasts BRAPI_DATASET_TTL_SECONDS (default 24h); set BRAPI_CANVAS_DROP_ENABLED=true to expose brapi_dataframe_drop for explicit cleanup.

Getting started

Public Hosted Instance

A public instance is available at https://brapi.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:

json
{  "mcpServers": {    "brapi-mcp-server": {      "type": "streamable-http",      "url": "https://brapi.caseyjhand.com/mcp"    }  }}

Self-Hosted / Local

Add the following to your MCP client configuration file.

json
{  "mcpServers": {    "brapi-mcp-server": {      "type": "stdio",      "command": "bunx",      "args": ["@cyanheads/brapi-mcp-server@latest"],      "env": {        "MCP_TRANSPORT_TYPE": "stdio",        "MCP_LOG_LEVEL": "info"      }    }  }}

Or with npx (no Bun required):

json
{  "mcpServers": {    "brapi-mcp-server": {      "type": "stdio",      "command": "npx",      "args": ["-y", "@cyanheads/brapi-mcp-server@latest"],      "env": {        "MCP_TRANSPORT_TYPE": "stdio",        "MCP_LOG_LEVEL": "info"      }    }  }}

Or with Docker:

json
{  "mcpServers": {    "brapi-mcp-server": {      "type": "stdio",      "command": "docker",      "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/brapi-mcp-server:latest"]    }  }}

For Streamable HTTP, set the transport and start the server:

sh
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http# Server listens at http://localhost:3010/mcp

No env vars are required: the built-in aliases (bti-cassava, bti-sweetpotato, bti-breedbase-demo) connect as-is, and brapi_connect accepts any other BrAPI v2 URL at runtime. For servers that need a login, set credentials as env vars so passwords, tokens, and keys stay out of the LLM context (see Per-alias credentials).

Prerequisites

  • Bun v1.4.0 or higher (or Node.js v24+).
  • @duckdb/node-api, installed as a regular dependency, with prebuilt native bindings for macOS, Linux (glibc and musl), and Windows on x64 and arm64. Cloudflare Workers is not supported.

Installation

  1. Clone the repository:
sh
git clone https://github.com/cyanheads/brapi-mcp-server.git
  1. Navigate into the directory:
sh
cd brapi-mcp-server
  1. Install dependencies:
sh
bun install
  1. Configure environment:
sh
cp .env.example .env# edit .env if you need credentials or non-default settings

Configuration

Every variable is optional.

VariableDescriptionDefault
BRAPI_DEFAULT_BASE_URLBase URL for the default alias (e.g. https://test-server.brapi.org/brapi/v2).—
BRAPI_DEFAULT_* credentialsOne credential family for the default alias; see Per-alias credentials.—
BRAPI_DEFAULT_API_KEY_HEADERAPI-key header for the default alias, and the fallback header for any api_key auth that names none.Authorization
BRAPI_BUILTIN_ALIASES_DISABLEDComma-separated built-in aliases to remove (case-insensitive).—
BRAPI_LOAD_LIMITDefault inline row cap for finders before spilling to a dataframe.1000
BRAPI_PAGE_SIZEUpstream pageSize for spillover page walks. The dataframe ceiling is pageSize × 50, capped at 50,000 rows.1000
BRAPI_MAX_CONCURRENT_REQUESTSPer-connection concurrency cap.4
BRAPI_RETRY_MAX_ATTEMPTS / BRAPI_RETRY_BASE_DELAY_MSRetries on 429/5xx and the exponential-backoff base delay.3 / 500
BRAPI_REQUEST_TIMEOUT_MSPer-request HTTP timeout.30000
BRAPI_COMPANION_TIMEOUT_MSTimeout for non-critical enrichment calls (FK lookups, count probes), which also skip retries.8000
BRAPI_SEARCH_POLL_TIMEOUT_MS / BRAPI_SEARCH_POLL_INTERVAL_MSAsync /search polling budget and interval.60000 / 1000
BRAPI_DATASET_TTL_SECONDSLifetime of dataframe provenance (the handle's expiresAt).86400
BRAPI_REFERENCE_CACHE_TTL_SECONDSTTL for cached capability profiles and reference data (programs, trials, locations, crops).3600
BRAPI_ALLOW_PRIVATE_IPSAllow RFC 1918 / loopback targets. Dev only.false
BRAPI_SESSION_ISOLATIONScope connections and the default canvas to the MCP session when one exists; false shares them across the tenant. See Deployment shapes.true
BRAPI_ENABLE_WRITESFeature flag. Registers brapi_submit_observations.false
BRAPI_CANVAS_DROP_ENABLEDFeature flag. Registers brapi_dataframe_drop.false
BRAPI_EXPORT_DIRFeature flag. Output directory for brapi_dataframe_export; setting it registers the tool, over stdio only.—
BRAPI_CANVAS_MAX_ROWS / BRAPI_CANVAS_QUERY_TIMEOUT_MSResponse row cap and per-query timeout for brapi_dataframe_query.10000 / 30000
BRAPI_GENOTYPE_CALLS_MAX_PULLUpstream call ceiling per brapi_find_genotype_calls or brapi_export_genotype_matrix call. Max 500000.100000
BRAPI_GENOTYPE_MATRIX_MAX_COLUMNSVariant-column ceiling per brapi_export_genotype_matrix matrix. Max 500000.10000
MCP_TRANSPORT_TYPETransport: stdio or http.stdio
MCP_HTTP_PORTHTTP server port.3010
MCP_SESSION_MODEHTTP session mode. This server requires stateful (apply-mode writes confirm over the session, and isolation keys off it); an HTTP start fails if it resolves to stateless.auto (resolves stateful)
MCP_AUTH_MODEAuthentication: none, jwt, or oauth.none
MCP_REQUEST_STATE_KEYSecret of at least 32 bytes, the same on every instance, that seals the requestState a confirmation round returns; any other state is refused before the handler runs. Recommended with BRAPI_ENABLE_WRITES.—
MCP_LOG_LEVELLog level (debug, info, warning, error, etc.).info
LOG_TOOL_FAILURE_PAYLOADSLog each failed tool call's arguments and result (key-name redaction only).false
STORAGE_PROVIDER_TYPEStorage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1.in-memory
OTEL_ENABLEDEnable OpenTelemetry.false
OTEL_EXPORTER_OTLP_LOGS_ENDPOINTOpt-in OTLP log export endpoint; the base OTEL_EXPORTER_OTLP_ENDPOINT never enables it.—

See .env.example for the full list of optional overrides.

Per-alias credentials

brapi_connect fills baseUrl and auth from env vars when the agent omits them, in this order:

  1. Agent input, within the pairing rules below.
  2. Per-alias env vars: BRAPI_<ALIAS>_*, uppercased with hyphens as underscores (my-server → BRAPI_MY_SERVER_*).
  3. Built-in aliases: see Built-in aliases.
  4. BRAPI_DEFAULT_BASE_URL, for an alias with no URL or credentials of its own.

Env credentials go only to the server configured with them:

  • An alias's credentials pair with its BRAPI_<ALIAS>_BASE_URL, else its enabled built-in URL. A caller baseUrl that points elsewhere fails with auth_base_url_mismatch. Credentials with no URL of their own, including those left behind by a built-in disabled via BRAPI_BUILTIN_ALIASES_DISABLED, fail with alias_base_url_unset and are never sent to BRAPI_DEFAULT_BASE_URL.
  • BRAPI_DEFAULT_* credentials attach only when the resolved URL is BRAPI_DEFAULT_BASE_URL; an alias pointed anywhere else connects without auth unless it has credentials of its own. With no BRAPI_DEFAULT_BASE_URL set, default credentials fail with alias_base_url_unset.

URLs compare after normalizing host case, default ports, and trailing slashes. Caller-supplied auth is never mixed with env credentials.

Each alias carries one credential family, and the auth mode follows from which fields are set. Mixing families within an alias raises a ValidationError.

Vars setResolved mode
_USERNAME + _PASSWORDsgn (Breedbase /token exchange)
_BEARER_TOKENbearer
_API_KEY (+ optional _API_KEY_HEADER)api_key
_OAUTH_CLIENT_ID + _OAUTH_CLIENT_SECRET (+ optional _OAUTH_TOKEN_URL)oauth2
(none set)none

BRAPI_<ALIAS>_DIALECT pins the dialect adapter (spec, brapi-test, breedbase, cassavabase, bms) when detection picks the wrong one; auto or unset detects it.

sh
# .env — attach write credentials to the built-in 'bti-cassava' aliasBRAPI_BTI_CASSAVA_USERNAME=aliceBRAPI_BTI_CASSAVA_PASSWORD=...# (BASE_URL omitted — the built-in registry covers it)
# Static API key as alias 'prod'BRAPI_PROD_BASE_URL=https://my-brapi.example.com/brapi/v2BRAPI_PROD_API_KEY=...BRAPI_PROD_API_KEY_HEADER=X-API-Key

The agent then calls brapi_connect({ alias: 'bti-cassava' }) with no baseUrl, no auth, and no secrets in the prompt.

Built-in aliases

These public BrAPI v2 endpoints connect with no configuration. Their orientation envelope carries license, citation, and homepage in an attribution block (Creative Commons Attribution).

AliasUpstreamHosted byCropNotes
bti-cassavacassavabase.orgBoyce Thompson InstituteCassavaNextGen Cassava
bti-sweetpotatosweetpotatobase.orgBoyce Thompson InstituteSweet potato
bti-breedbase-demobreedbase.orgBoyce Thompson InstituteDemoSample data only, for onboarding and tests

The registry holds only servers verified for anonymous reads. Servers that require login, including the Triticeae Toolbox (T3) wheat, oat, and barley hosts, connect through BRAPI_<ALIAS>_BASE_URL plus credentials (see .env.example).

BRAPI_<ALIAS>_BASE_URL overrides a built-in URL, e.g. to point bti-sweetpotato at a staging mirror via BRAPI_BTI_SWEETPOTATO_BASE_URL. BRAPI_<ALIAS>_USERNAME and friends attach credentials on top of the built-in URL; each Breedbase instance has its own user table, so write access needs a separate account on each. BRAPI_BUILTIN_ALIASES_DISABLED=bti-cassava,bti-breedbase-demo removes entries.

Citation: all three built-ins reference Morales et al. 2022, "Breedbase: a digital ecosystem for modern plant breeding." G3 12(7): jkac078. doi:10.1093/g3journal/jkac078.

Running the server

Local development

  • Build and run the production version:

    sh
    # One-time buildbun run rebuild
    # Run the built serverbun run start            # transport from MCP_TRANSPORT_TYPE (stdio default)bun run start:stdiobun run start:http
    # Or run from source with hot reloadbun --watch src/index.ts
  • Run checks and tests:

    sh
    bun run devcheck         # Lint, format, typecheck, security, changelog syncbun run test             # Vitest suitebun run lint:mcp         # Validate MCP definitions

Docker

sh
docker build -t brapi-mcp-server .docker run --rm -p 3010:3010 brapi-mcp-server

The image defaults to HTTP transport, stateful session mode, and logs to /var/log/brapi-mcp-server. OpenTelemetry peer dependencies are installed by default; build with --build-arg OTEL_ENABLED=false to omit them.

Deployment shapes

Two kinds of state scope by tenant and, by default, by MCP session: connection state (registered aliases and exchanged upstream tokens) and dataframes (df_<uuid> tables, usable by anyone who holds the name within its bucket). Three configurations set where those buckets end:

ShapeSettingsIsolationBest for
Per-session (default)MCP_AUTH_MODE=none + HTTP stateful + BRAPI_SESSION_ISOLATION=trueEach MCP session gets its own connections and canvas. Requests without a session share one tenant-wide namespace, so brapi_connect refuses their caller-supplied auth with auth_session_required; keyless connections and operator env credentials still work.Multi-user hosting without SSO
Per-user credentialsMCP_AUTH_MODE=jwt or oauth (+ HTTP stateful)Each user's JWT tid claim is its own tenant; sessions sub-scope inside it when isolation is on.Multi-user hosting with institutional SSO; the strongest separation
Shared workspaceMCP_AUTH_MODE=none + BRAPI_SESSION_ISOLATION=falseAll callers share one tenant's connections and canvas.One researcher running parallel agents on shared upstream credentials

Stdio is always a single session. Clients on MCP protocol revision 2026-07-28 carry no session on any transport, so outside the per-user-credentials shape they land in the shared tenant workspace, where re-registering an alias re-points every such caller's later calls to it.

On HTTP without per-user auth, brapi_dataframe_describe won't list dataframes without a name, and brapi_dataframe_query rejects system-catalog reads in every shape, so a caller without a known df_<uuid> name can't enumerate other callers' tables.

Project structure

DirectoryPurpose
src/index.tscreateApp() entry point: registers tools (behind their feature flags), resources, and prompts, and inits services.
src/configServer env parsing (Zod), per-alias credential resolution, and the built-in alias registry.
src/mcp-server/toolsTool definitions (*.tool.ts) and shared helpers. Twenty-five tools across connection, retrieval, analysis, write, and raw passthrough.
src/mcp-server/resourcesResource definitions (*.resource.ts).
src/mcp-server/promptsPrompt definitions (*.prompt.ts).
src/servicesBrAPI client, dialect adapters, filter catalog, canvas bridge, capability registry, ISO country resolver, ontology resolver, reference-data cache, server registry.
tests/Unit and integration tests mirroring src/.

Development guide

See CLAUDE.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for logging, ctx.state for tenant-scoped storage — no console, no direct persistence access
  • Add new tools to the matching group in src/mcp-server/tools/definitions/index.ts; src/index.ts composes the groups behind their feature flags
  • Wrap upstream calls: validate raw → normalize → return output schema; never fabricate missing fields

Contributing

Issues are welcome. Run checks and tests before submitting:

sh
bun run devcheckbun run test

License

Apache-2.0 — see LICENSE for details.

來源:README.md,提交 707f21b

工具

0
工具後設資料尚未被收錄。

版本歷史

4
  1. v0.8.1最新Oct 4, 2026
  2. v0.8.0Sep 24, 2026
  3. v0.7.13Sep 19, 2026
  4. v0.7.12Sep 16, 2026