CAT
/MCP
SkillsMCPMarketplacesDigestToolsAdvertise

This week in Claude

Every Monday: Claude Code, Agent SDK, MCP, and the Anthropic platform moves worth your time.

Skills by Category
Frontend DevelopmentBackend & APIsTesting & QASecurityDevOps & CI/CDGit & Pull RequestsDocumentationCode Review & QualityAI & Agent BuildingSkill Development
MCP Servers by Category
Sales & MarketingWeb & Browser AutomationDatabasesAI & LLM ToolsCloud & InfrastructureCommunication & MessagingDeveloper ToolsDesign & CreativeDocuments & KnowledgeSearch & Web Crawling
Marketplaces by Category
AI Agents & OrchestrationLLM IntegrationDevelopment ToolsFrontend & UIBackend & APIsDatabasesTesting & Code QualityDevOps & CloudSecurity & ComplianceGit & Version Control

Cross AI Tools

Discover Claude Code plugins, extensions, and tools. Automatically updated directory of Anthropic Claude AI marketplaces with development tools, productivity plugins, and integrations.

Resources

  • Browse Skills
  • Browse MCP Servers
  • Browse Marketplaces
  • Plugins Reference

Community

  • About
  • Tools
  • Feedback
  • Privacy Policy
  • Advertise

Built for the Claude Code community with Claude Code by @mertduzgun

Independent project, not affiliated with Anthropic

Brapi Mcp Server

cyanheads/brapi-mcp-server
3STDIO, HTTPregistry active
Summary

A BrAPI v2.1 client that turns Claude into a breeding database workbench. Point it at Breedbase, T3, or any compliant server and you get 25 tools spanning germplasm search, study retrieval, pedigree walking, observation queries, and genotype calls. The find tools spill paginated results into DuckDB-backed dataframes you can SQL over, build phenotype matrices from, or export as CSV/Parquet. It handles async search polling, caches server capabilities on connect, and gives you both curated endpoints and raw passthrough for anything outside the standard set. Writes are opt-in and additive only. Useful if you're doing cross-trial analysis, pedigree tracing, or need to pull breeding data into an agent workflow without writing custom glue code for each platform.

CodeRabbit
CodeRabbit
AI writes the code. CodeRabbit catches the slop.
Try For Free →
Keep your Mac awake
Keep your Mac awake
Keep your Mac awake while Claude Code and 40+ AI agents run. Sleeps when they're idle.
One time payment $9 →
Context.devContext.dev
Context.dev
Integrate web data into your AI product. One API to scrape website & brand data.
Get API Key Now →
Make your agent a DeFi expert
Make your agent a DeFi expert
Agent, run crypto. Access onchain data & trade routes via 1inch.
Install now →
Make money from your Skills
Make money from your Skills
On Capafy, your Skill runs online 24/7 as an agent product, and you get paid every time someone uses it.
Start earning →
AppSignal
AppSignal
Monitor with ease. Code with confidence.
Start Free Trial →
CodeRabbit
CodeRabbit
AI writes the code. CodeRabbit catches the slop.
Try For Free →
Keep your Mac awake
Keep your Mac awake
Keep your Mac awake while Claude Code and 40+ AI agents run. Sleeps when they're idle.
One time payment $9 →
Context.devContext.dev
Context.dev
Integrate web data into your AI product. One API to scrape website & brand data.
Get API Key Now →
Make your agent a DeFi expert
Make your agent a DeFi expert
Agent, run crypto. Access onchain data & trade routes via 1inch.
Install now →
Make money from your Skills
Make money from your Skills
On Capafy, your Skill runs online 24/7 as an agent product, and you get paid every time someone uses it.
Start earning →
AppSignal
AppSignal
Monitor with ease. Code with confidence.
Start Free Trial →

@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 • Multi-agent collaboration

npm Version MCP SDK License TypeScript Bun Status

Install in Claude Desktop Install in Cursor Install in VS Code

Framework


Tools

25 tools grouped by shape — connection tools bootstrap a session, find_* tools return a summarized page plus distributions and spill overflow rows into a canvas dataframe that agents on the same session can query or hand off by ID, get_* tools fetch a single record with companion counts, plus pedigree walking, an embedded SQL workspace over spilled rows (DuckDB-backed), file export for human handoff, an additive write surface for observations, and raw passthrough escape hatches.

Orient

ToolDescription
brapi_connectAuthenticate, register the connection under an alias, cache the capability profile, and return the orientation envelope inline. One call fully orients the agent.
brapi_server_infoRe-fetch the orientation envelope for a registered alias — identity, auth, capabilities, content counts, attribution, notes.
brapi_describe_filtersStatic BrAPI v2.1 filter catalog for any endpoint — powers extraFilters discovery on every find_* tool.

Retrieve

ToolDescription
brapi_find_studiesFind studies by crop / trial type / season / location / program. Distributions + dataframe spillover.
brapi_get_studyFetch a study with program / trial / location FKs resolved and companion counts (observations, units, variables).
brapi_find_germplasmFind germplasm by name, synonym, accession, PUI, crop, or free-text. Distributions + dataframe spillover.
brapi_get_germplasmFetch a germplasm with attributes, direct parents, and companion counts (studies, parents, descendants).
brapi_walk_pedigreeBFS-walk ancestry / descendancy as a deduplicated DAG with cycle detection, depth limits, and traversal stats.
brapi_find_variablesFind observation variables by name / class / ontology / free-text; ranked client-side via OntologyResolver when text is supplied.
brapi_find_observationsPull observation records by study / germplasm / variable / season / unit / timestamp. Dataframe spillover.
brapi_find_imagesFilter image metadata by unit / study / ontology / MIME type. Bytes via brapi_get_image.
brapi_get_imageFetch image bytes for up to 5 imageDbIds inline as type: image blocks. Prefers /imagecontent, falls back to imageURL.
brapi_find_locationsFind research stations by country / type / abbreviation, with optional client-side bbox filter.
brapi_find_variantsFind variant records by variant set, reference, or genomic region (1-based inclusive / exclusive).
brapi_find_genotype_callsPull genotype calls via async-search polling. Upstream pull bounded by BRAPI_GENOTYPE_CALLS_MAX_PULL (default 100k, max 500k).

Analyze

ToolDescription
brapi_dataframe_describeStart here after a spillover. Lists dataframes (or describes one) with column schema, row counts, and originating-source provenance.
brapi_dataframe_querySELECT SQL across in-memory dataframes (DuckDB-backed). Spilled find_* rows auto-register as df_<uuid>. Read-only — multi-statement, non-SELECT, file-reads, and exports rejected. Returns typed columns ({ name, type }[]).
brapi_dataframe_dropOpt-in via BRAPI_CANVAS_DROP_ENABLED=true. Drop a dataframe by name. Idempotent. Dataframes also expire via TTL when left unmanaged.
brapi_dataframe_exportOpt-in via BRAPI_EXPORT_DIR=<path>, stdio-only. Export a dataframe to disk (CSV / Parquet / JSON) under the configured directory and return the absolute path for the human to open. Optional columns projection or sql filter materializes a derived table for the export, dropped after.
brapi_build_phenotype_matrixBuild a germplasm × trait matrix from one or more studies and materialize it as a canvas dataframe. Supports wide (pivot) or long shape with configurable per-cell aggregation.
brapi_germplasm_performancePer-variable performance aggregates (n, mean, median, sd, min, max, studyCount) for a single germplasm across all studies where it has observations.
brapi_export_genotype_matrixExport genotype calls for a variant set as a germplasm × variant canvas dataframe; also serializes to VCF-lite or PLINK .ped/.map text.

Write (opt-in: BRAPI_ENABLE_WRITES=true)

ToolDescription
brapi_submit_observationsTwo-phase observation write — mode: preview validates; mode: apply elicits confirmation, then fans POST + PUT in parallel. Additive only — no destructive deletion.

Escape hatches

ToolDescription
brapi_raw_getPassthrough to any BrAPI GET /{path} not covered by curated tools. Emits a routing nudge when one applies.
brapi_raw_searchPassthrough to any POST /search/{noun} with async polling handled transparently. Same nudge pattern.

Alias discovery. Built-in and operator-configured aliases are appended to the brapi_connect description at server startup, so agents see the inventory on tools/list. Restart after env-var changes to refresh.


Resources

URI-addressable mirrors of the curated tool surface for clients that prefer resources. All resources use the default connection — multi-server workflows route through tools.

URI templateMirrors
brapi://server/infobrapi_server_info (default connection)
brapi://callsRaw capability profile
brapi://study/{studyDbId}brapi_get_study
brapi://germplasm/{germplasmDbId}brapi_get_germplasm
brapi://filters/{endpoint}brapi_describe_filters
brapi://variable/{observationVariableDbId}Observation variable record (trait, scale, method, ontology)

Prompts

Multi-step BrAPI workflow templates — pure user-message generators, no side effects.

NameArgsPurpose
brapi_eda_studystudyDbId, alias?EDA playbook for one study — orient, variables, coverage, missing data, outliers, pedigree, structured report.
brapi_meta_analysisgermplasmDbIds (CSV), traitName, alias?Cross-study meta-analysis — trait resolution, study discovery, harmonization, per-germplasm × per-study and across-study summaries.

Multi-agent workflows

The server has two stateful layers and two scoping axes:

LayerDefault scopeWhy
Connection state (aliases, exchanged tokens)Tenant + sessionCredentials and live tokens. Tenant gates by user (jwt/oauth) or collapses to 'default' (none). Session sub-scope (BRAPI_SESSION_ISOLATION=true, default) prevents concurrent HTTP sessions in one tenant from sharing each other's tokens.
Dataframes (df_<uuid> tables)Tenant + sessionWithin one (tenant, session), agents share by df_<uuid> name — possession grants full read/write/drop, auto-expires in 24h, provenance recorded. The underlying canvas is tenant-gated by the framework; the session sub-scope is enforced by the bridge's keying.

Within one (tenant, session), dataframes act as a self-cleaning shared notebook: hand the df_<uuid> name between parallel agents on the same MCP session, persist it across a multi-step workflow, query / project / aggregate / join from any position. Address-by-name, time-bounded, scoped to that session.

Default (isolated) shape. Under MCP_AUTH_MODE=none + HTTP stateful (the default), each MCP session carves its own connection state and its own canvas. Two researchers connected to the same host don't see each other's brapi_connect aliases, exchanged SGN/OAuth tokens, or spilled df_<uuid> rows. Stdio always behaves as one session (single-process, no concurrency).

Shared-workspace shape. Set BRAPI_SESSION_ISOLATION=false for cross-session collaboration in one tenant — multiple MCP sessions then share connection state and one default canvas, the way pre-0.5.3 deployments behaved. Useful when planning, analysis, and writeup agents run as separate MCP clients but operate as one researcher on shared upstream credentials.

On privileged data. The df_<uuid> name is a capability token within a canvas — not row-level access control. Anyone holding the name within the same (tenant, session) bucket can read its rows. Under default isolation, that bucket is one MCP session. Under BRAPI_SESSION_ISOLATION=false, the bucket widens to the whole tenant (all callers under auth=none, or one user's sessions under jwt/oauth). Treat dataframe names like authenticated share links — pass within the bucket, not externally. The 24h TTL caps blast radius; the provenance trail (originating tool, baseUrl, query) supports audit. Belt-and-braces: brapi_dataframe_describe requires an explicit dataframe name on shared-trust HTTP (no list-all enumeration), and brapi_dataframe_query rejects system-catalog reads (information_schema, pg_catalog, sqlite_master, duckdb_*) — so a caller without a known df_<uuid> name can't fish through either surface.


BrAPI-specific features

  • Dataframe spillover — find_* tools cap in-context rows at loadLimit and materialize larger unions (up to 50k rows / 50 pages) as DuckDB-backed df_<uuid> canvas dataframes. Discover with brapi_dataframe_describe, query with brapi_dataframe_query (SQL paging via LIMIT/OFFSET, projection, aggregation). Read-only enforcement at the SQL gate; session-scoped by default (tenant-scoped under BRAPI_SESSION_ISOLATION=false) — see Multi-agent workflows.
  • Multi-server session — ServerRegistry maps aliases to live BrAPI connections; one session can span Breedbase, T3, and Sweetpotatobase in parallel.
  • Built-in known-server registry — bti-cassava, bti-sweetpotato, bti-breedbase-demo, t3-wheat, t3-oat, t3-barley resolve out-of-the-box without env vars; orientation envelope carries CC-BY attribution.
  • Capability-aware calls — CapabilityRegistry caches /serverinfo per connection and guards every tool call against unsupported endpoints. Falls back to /calls when /serverinfo is sparse.
  • Dialect adaptation — spec / brapi-test / breedbase / cassavabase / bms dialects translate v2.1 plural filter keys to the singular form each server family honors, drop filters known to be broken, normalize sparse-shape encodings, and escalate to POST /search/{noun} when GET would silently downcast multi-value filters. Detected from /serverinfo (server-name / organization-name); pin per-alias via BRAPI_<ALIAS>_DIALECT. Verified-vs-inferred mapping counts surface on the orientation envelope so agents see the confidence floor at a glance.
  • DuckDB required — @duckdb/node-api is a regular dependency; startup fails closed when the framework canvas is unavailable. Not supported on Cloudflare Workers (no native binary in that runtime).
  • Async-search transparency — brapi_find_genotype_calls and brapi_raw_search handle the POST /search/{noun} → GET /search/{noun}/{id} 202-retry pattern automatically.
  • Pedigree DAG walks — brapi_walk_pedigree BFS-traverses ancestry / descendancy with cycle detection (BrAPI only exposes one generation per call); a 1,000-node safety cap bounds the walk and sets truncated when reached.
  • Image content — brapi_get_image fetches bytes inline as MCP type: image blocks, preferring /images/{id}/imagecontent with imageURL fallback.
  • Free-text variable ranking — OntologyResolver scores variables against a query (PUI / name / synonym / trait-class) so find_variables text:"..." returns ranked candidates even without /ontologies.
  • Auth variants in one schema — tagged-union covers none / bearer / api_key / sgn (session-token exchange) / oauth2 (client-credentials).
  • Typed error contracts — every declared failure mode carries a stable data.reason, an HTTP-style code, and a recovery.hint so clients can route deterministically.

Built on @cyanheads/mcp-ts-core — declarative definitions, unified error handling, pluggable auth (none / jwt / oauth), swappable storage, structured logging with optional OTel, STDIO + Streamable HTTP transports.


Working with dataframes

When a find_* tool's upstream total exceeds loadLimit, the full union materializes as a canvas dataframe and the response carries an inline dataframe handle ({ tableName, rowCount, columns, createdAt, expiresAt, … }). Upstream column names that aren't SQL-safe identifiers — reserved words like end, digit-leading IDs — are sanitized for the dataframe, and a columnLegend on the handle maps each renamed column back to its original key. SQL is the paging idiom — use LIMIT/OFFSET to walk pages, projection (SELECT col1, col2) to trim columns, and aggregation (COUNT, GROUP BY, AVG) to summarize without materializing every row.

Dataframe names are session-scoped capability tokens by default — pass tableName to any other agent on the same MCP session (or a downstream step in the same workflow) and they query the same workspace by name without re-pulling from the upstream. The brapi_dataframe_* tools offer SQL manipulation and more. See Multi-agent workflows for cross-session / cross-tenant rules.

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
4. brapi_dataframe_query { sql: "SELECT COUNT(*) AS n, AVG(CAST(value AS DOUBLE)) AS mean FROM df_<uuid> WHERE observationVariableDbId = 'V1'" }
   → aggregate without round-tripping all rows

Dataframes auto-expire via TTL (BRAPI_DATASET_TTL_SECONDS, default 24h). Set BRAPI_CANVAS_DROP_ENABLED=true to expose brapi_dataframe_drop for explicit cleanup.


Getting started

Add to your MCP client config — pick one runner:

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

Swap command/args for npx -y @cyanheads/brapi-mcp-server@latest (no Bun) or docker run -i --rm -e MCP_TRANSPORT_TYPE=stdio ghcr.io/cyanheads/brapi-mcp-server:latest.

For Streamable HTTP:

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 six built-in aliases (bti-cassava, bti-sweetpotato, bti-breedbase-demo, t3-wheat, t3-oat, t3-barley) resolve out-of-the-box, and agents can connect to any other BrAPI v2 URL at runtime via brapi_connect. For credentialed servers, prefer env vars over agent input so passwords / tokens / API keys stay out of the LLM context — see Per-alias credentials.

Prerequisites: Bun v1.3.11+ or Node.js v24+. @duckdb/node-api is a required dependency — supported on Linux/macOS/Windows × x64 plus Linux/macOS arm64 (no Windows arm64; no Cloudflare Workers).


Configuration

Every variable is optional.

VariableDescriptionDefault
BRAPI_DEFAULT_BASE_URLDefault BrAPI v2 base URL (e.g. https://test-server.brapi.org/brapi/v2).—
BRAPI_DEFAULT_USERNAME / _PASSWORDSGN session-token auth for the default connection.—
BRAPI_DEFAULT_OAUTH_CLIENT_ID / _OAUTH_CLIENT_SECRETOAuth2 client-credentials for the default connection.—
BRAPI_DEFAULT_API_KEY / _API_KEY_HEADERStatic API key for the default connection.header Authorization
BRAPI_BUILTIN_ALIASES_DISABLEDComma-separated alias names (case-insensitive) to remove from the built-in registry.—
BRAPI_LOAD_LIMITIn-context row cap returned by find_* tools before spilling to a canvas dataframe.1000
BRAPI_PAGE_SIZEUpstream pageSize used during canvas spillover walks (decoupled from BRAPI_LOAD_LIMIT). Dataframe ceiling = pageSize × 50.1000
BRAPI_MAX_CONCURRENT_REQUESTSPer-connection concurrency cap.4
BRAPI_RETRY_MAX_ATTEMPTS / BRAPI_RETRY_BASE_DELAY_MSRetry policy for 429/5xx with exponential backoff.3 / 500
BRAPI_REQUEST_TIMEOUT_MSPer-request HTTP timeout.30000
BRAPI_COMPANION_TIMEOUT_MSTighter timeout for non-critical companion enrichments (FK lookups, count probes). Companions also bypass the retry budget so a slow upstream surfaces as a warning instead of stretching the response.8000
BRAPI_SEARCH_POLL_TIMEOUT_MS / _INTERVAL_MSAsync /search polling budget + interval.60000 / 1000
BRAPI_DATASET_TTL_SECONDSTTL for dataframe provenance metadata persisted alongside spilled rows.86400
BRAPI_REFERENCE_CACHE_TTL_SECONDSTTL for programs / trials / locations / crops cache.3600
BRAPI_ALLOW_PRIVATE_IPSAllow RFC 1918 / loopback targets. Dev-only.false
BRAPI_ENABLE_WRITESOpt-in for brapi_submit_observations registration.false
BRAPI_GENOTYPE_CALLS_MAX_PULLUpstream row ceiling per brapi_find_genotype_calls invocation. Max 500,000.100000
BRAPI_CANVAS_DROP_ENABLEDOpt-in for brapi_dataframe_drop registration. Off by default; dataframes expire via TTL when left unmanaged.false
BRAPI_EXPORT_DIRDirectory for brapi_dataframe_export output files. Setting a path is the opt-in (no separate enable flag); unset leaves the tool out of tools/list. Stdio-only — the tool stays disabled under HTTP transport regardless of this value. Bridged to the framework's CANVAS_EXPORT_PATH automatically.—
BRAPI_CANVAS_MAX_ROWS / BRAPI_CANVAS_QUERY_TIMEOUT_MSPer-query response row cap and wall-clock timeout for brapi_dataframe_query.10000 / 30000
MCP_TRANSPORT_TYPE / MCP_HTTP_PORT / MCP_SESSION_MODETransport (stdio | http), HTTP port, session mode (stateful | stateless | auto).stdio / 3010 / auto
MCP_AUTH_MODE / MCP_LOG_LEVEL / STORAGE_PROVIDER_TYPE / OTEL_ENABLEDAuth mode (none | jwt | oauth), log level, storage backend, OpenTelemetry.none / info / in-memory / false
BRAPI_SESSION_ISOLATIONWhen true, scope ServerRegistry connection state and the CanvasBridge default canvas to ctx.sessionId (HTTP stateful/auto). Concurrent callers under MCP_AUTH_MODE=none operate in isolated workspaces. Set false for the shared-workspace collaboration model. No effect on stdio.true

Per-alias overrides follow the BRAPI_<ALIAS>_* pattern — see .env.example for every override and inline comments.

Per-alias credentials

brapi_connect resolves baseUrl and auth from env vars when the agent omits them — credentials never enter the LLM context. Four layers of precedence:

  1. Explicit agent input — always wins.
  2. Per-alias env vars — BRAPI_<ALIAS>_* (uppercased, hyphens → underscores: my-server → BRAPI_MY_SERVER_*).
  3. Built-in known-server registry — see Built-in aliases.
  4. Default env vars — BRAPI_DEFAULT_*, only when the alias differs from default. Not layered on top of a built-in URL — defaults belong to the default server.

Each alias carries one credential family — auth mode is derived from which fields are set:

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

Mixing families within an alias raises a ValidationError.

# .env — attach write credentials to the built-in 'bti-cassava' alias
BRAPI_BTI_CASSAVA_USERNAME=alice
BRAPI_BTI_CASSAVA_PASSWORD=...
# (BASE_URL omitted — built-in registry covers it)

# Static API key as alias 'prod'
BRAPI_PROD_BASE_URL=https://my-brapi.example.com/brapi/v2
BRAPI_PROD_API_KEY=...
BRAPI_PROD_API_KEY_HEADER=X-API-Key

Then the agent calls brapi_connect({ alias: 'bti-cassava' }) — no baseUrl, no auth, no secrets in the prompt.

Built-in aliases

The server ships with a curated registry of public BrAPI v2 endpoints. Each resolves out-of-the-box; the orientation envelope surfaces license, citation, and homepage in its attribution block under 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 — onboarding + tests.
t3-wheatwheat.triticeaetoolbox.orgTriticeae Toolbox (T3)WheatWheat CAP / IWYP.
t3-oatoat.triticeaetoolbox.orgTriticeae Toolbox (T3)OatGlobal Oat Genetics Database.
t3-barleybarley.triticeaetoolbox.orgTriticeae Toolbox (T3)BarleyT-CAP / US Wheat & Barley Scab Initiative.

Set BRAPI_<ALIAS>_BASE_URL to repoint at a staging mirror or fork (env wins over the built-in URL — hyphens in the alias become underscores in the env var, so t3-wheat → BRAPI_T3_WHEAT_BASE_URL). Set BRAPI_<ALIAS>_USERNAME etc. to attach credentials on top of the built-in URL — each Breedbase instance has its own user table, so write access requires separate registration on each upstream. Use BRAPI_BUILTIN_ALIASES_DISABLED=bti-cassava,t3-wheat to strip specific entries.

Citation: all six 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

# Hot-reload dev (Bun runs TS directly)
bun --watch src/index.ts

# Production
bun run rebuild
bun run start            # transport via MCP_TRANSPORT_TYPE (stdio default)
bun run start:stdio      # or pin explicitly
bun run start:http

# Checks
bun run devcheck         # lint + format + typecheck + security + changelog sync
bun run test             # Vitest
bun run lint:mcp         # validate MCP definitions

Docker

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

Defaults to HTTP transport, stateful session mode (engages the mcp-session-id lifecycle — precondition for BRAPI_SESSION_ISOLATION=true; hijack protection requires layering MCP_AUTH_MODE=jwt|oauth on top), logs to /var/log/brapi-mcp-server. OTel peer deps are installed by default — --build-arg OTEL_ENABLED=false to omit.

Deployment shapes

brapi-mcp-server runs in three shapes — pick the one that matches your trust domain. The differentiator is what isolates connection state (registered aliases, cached upstream tokens) and dataframes: nothing, the MCP session, or the auth tenant.

ShapeSettingsIsolationBest for
Per-session (default)MCP_AUTH_MODE=none + HTTP stateful + BRAPI_SESSION_ISOLATION=trueEach MCP session carves its own connection state and canvas. Concurrent HTTP callers don't see each other's aliases, exchanged tokens, or df_<uuid> rows.Multi-user host without SSO. Default for institutional / public deployment under shared-trust auth.
Per-user credentialsMCP_AUTH_MODE=jwt or oauth (+ HTTP stateful)Each user's JWT tid claim carves a tenant. Sessions sub-scope inside each tenant when isolation is on. Cross-user spillover impossible at the framework level.Multi-user host with institutional SSO (Shibboleth, Okta, etc.) — strongest separation.
Shared workspaceMCP_AUTH_MODE=none + BRAPI_SESSION_ISOLATION=falseAll callers in one tenant share connection state and one canvas. Possession of a df_<uuid> name = full read/write across the workspace.Solo, lab, or hosting where every caller is one researcher running parallel agents on shared upstream credentials.

Shape selection guide:

  • Multi-user public/institutional HTTP, no SSO. Use the per-session default. Each researcher's stateful HTTP session is isolated even though they all resolve to tenantId='default'.
  • Multi-user with institutional SSO. MCP_AUTH_MODE=jwt (HS256, MCP_AUTH_SECRET_KEY) or oauth (JWKS, OAUTH_ISSUER_URL + OAUTH_AUDIENCE). Each user's tid claim carves a tenant — the outer scope. BRAPI_SESSION_ISOLATION=true (default) then sub-scopes inside each tenant for users running parallel sessions, and JWT/OAuth identity binding gives real session-hijack protection on top.
  • One researcher, parallel agents. If multiple agents (planner, analyst, writeup) connect as separate MCP clients but should share one workspace, set BRAPI_SESSION_ISOLATION=false and rely on shared trust. This is the shared-workspace shape.
  • Stdio. Always one session; isolation is moot. The flag has no effect.

Belt-and-braces under shared trust. Even with BRAPI_SESSION_ISOLATION=false, brapi_dataframe_describe requires an explicit dataframe name on HTTP (no list-all enumeration), and brapi_dataframe_query rejects system-catalog reads (information_schema, pg_catalog, sqlite_master, duckdb_*). The dataframe name is the capability token; possession proves it.


Development

See CLAUDE.md for full architectural rules. Short version:

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for logging, ctx.state for storage — no console, no direct persistence
  • Register new tools in the tools array of createApp() in src/index.ts
  • Wrap upstream calls: validate raw → normalize → return output schema; never fabricate missing fields
git clone https://github.com/cyanheads/brapi-mcp-server.git
cd brapi-mcp-server
bun install
cp .env.example .env       # edit if you need credentials
bun run devcheck && bun run test

PRs welcome.


License

Apache-2.0 — see LICENSE.

Featured
CodeRabbit
CodeRabbit
AI writes the code. CodeRabbit catches the slop.
Try For Free →
Keep your Mac awake
Keep your Mac awake
Keep your Mac awake while Claude Code and 40+ AI agents run. Sleeps when they're idle.
One time payment $9 →
Context.devContext.dev
Context.dev
Integrate web data into your AI product. One API to scrape website & brand data.
Get API Key Now →
Make your agent a DeFi expert
Make your agent a DeFi expert
Agent, run crypto. Access onchain data & trade routes via 1inch.
Install now →
Make money from your Skills
Make money from your Skills
On Capafy, your Skill runs online 24/7 as an agent product, and you get paid every time someone uses it.
Start earning →
AppSignal
AppSignal
Monitor with ease. Code with confidence.
Start Free Trial →

Configuration

BRAPI_DEFAULT_BASE_URL

Default BrAPI v2 base URL. Optional — connections can be opened at runtime via the brapi_connect tool instead.

BRAPI_ENABLE_WRITESdefault: false

Opt-in flag for the write surface (brapi_submit_observations). Off by default — the tool is omitted from tools/list unless the operator opts in.

BRAPI_PAGE_SIZEdefault: 1000

Upstream pageSize used during canvas spillover walks (decoupled from BRAPI_LOAD_LIMIT). Dataframe ceiling = pageSize × 50 pages.

MCP_LOG_LEVELdefault: info

Sets the minimum log level for output (e.g., 'debug', 'info', 'warn').

MCP_HTTP_HOSTdefault: 127.0.0.1

The hostname for the HTTP server.

MCP_HTTP_PORTdefault: 3010

The port to run the HTTP server on.

MCP_HTTP_ENDPOINT_PATHdefault: /mcp

The endpoint path for the MCP server.

MCP_AUTH_MODEdefault: none

Authentication mode to use: 'none', 'jwt', or 'oauth'.

Registryactive
Package@cyanheads/brapi-mcp-server
TransportSTDIO, HTTP
UpdatedJun 8, 2026
View on GitHub