.codex-plugin — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited .codex-plugin (MCP Server) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
<div align="center"> <h1>@cyanheads/brapi-mcp-server</h1> <p><b>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.</b> <div>25 Tools • 6 Resources • 2 Prompts • Multi-agent collaboration</div> </p> </div>
<div align="center">
</div>
<div align="center">
</div>
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.
| Tool | Description |
|---|---|
brapi_connect | Authenticate, register the connection under an alias, cache the capability profile, and return the orientation envelope inline. One call fully orients the agent. |
brapi_server_info | Re-fetch the orientation envelope for a registered alias — identity, auth, capabilities, content counts, attribution, notes. |
brapi_describe_filters | Static BrAPI v2.1 filter catalog for any endpoint — powers extraFilters discovery on every find_* tool. |
| Tool | Description |
|---|---|
brapi_find_studies | Find studies by crop / trial type / season / location / program. Distributions + dataframe spillover. |
brapi_get_study | Fetch a study with program / trial / location FKs resolved and companion counts (observations, units, variables). |
brapi_find_germplasm | Find germplasm by name, synonym, accession, PUI, crop, or free-text. Distributions + dataframe spillover. |
brapi_get_germplasm | Fetch a germplasm with attributes, direct parents, and companion counts (studies, parents, descendants). |
brapi_walk_pedigree | BFS-walk ancestry / descendancy as a deduplicated DAG with cycle detection, depth limits, and traversal stats. |
brapi_find_variables | Find observation variables by name / class / ontology / free-text; ranked client-side via OntologyResolver when text is supplied. |
brapi_find_observations | Pull observation records by study / germplasm / variable / season / unit / timestamp. Dataframe spillover. |
brapi_find_images | Filter image metadata by unit / study / ontology / MIME type. Bytes via brapi_get_image. |
brapi_get_image | Fetch image bytes for up to 5 imageDbIds inline as type: image blocks. Prefers /imagecontent, falls back to imageURL. |
brapi_find_locations | Find research stations by country / type / abbreviation, with optional client-side bbox filter. |
brapi_find_variants | Find variant records by variant set, reference, or genomic region (1-based inclusive / exclusive). |
brapi_find_genotype_calls | Pull genotype calls via async-search polling. Upstream pull bounded by BRAPI_GENOTYPE_CALLS_MAX_PULL (default 100k, max 500k). |
| Tool | Description |
|---|---|
brapi_dataframe_describe | Start here after a spillover. Lists dataframes (or describes one) with column schema, row counts, and originating-source provenance. |
brapi_dataframe_query | SELECT 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_drop | _Opt-in via BRAPI_CANVAS_DROP_ENABLED=true._ Drop a dataframe by name. Idempotent. Dataframes also expire via TTL when left unmanaged. |
brapi_dataframe_export | _Opt-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_matrix | Build 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_performance | Per-variable performance aggregates (n, mean, median, sd, min, max, studyCount) for a single germplasm across all studies where it has observations. |
brapi_export_genotype_matrix | Export genotype calls for a variant set as a germplasm × variant canvas dataframe; also serializes to VCF-lite or PLINK .ped/.map text. |
BRAPI_ENABLE_WRITES=true)| Tool | Description |
|---|---|
brapi_submit_observations | Two-phase observation write — mode: preview validates; mode: apply elicits confirmation, then fans POST + PUT in parallel. Additive only — no destructive deletion. |
| Tool | Description |
|---|---|
brapi_raw_get | Passthrough to any BrAPI GET /{path} not covered by curated tools. Emits a routing nudge when one applies. |
brapi_raw_search | Passthrough to any POST /search/{noun} with async polling handled transparently. Same nudge pattern. |
Alias discovery. Built-in and operator-configured aliases are appended to thebrapi_connectdescription at server startup, so agents see the inventory ontools/list. Restart after env-var changes to refresh.
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 template | Mirrors |
|---|---|
brapi://server/info | brapi_server_info (default connection) |
brapi://calls | Raw 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) |
Multi-step BrAPI workflow templates — pure user-message generators, no side effects.
| Name | Args | Purpose |
|---|---|---|
brapi_eda_study | studyDbId, alias? | EDA playbook for one study — orient, variables, coverage, missing data, outliers, pedigree, structured report. |
brapi_meta_analysis | germplasmDbIds (CSV), traitName, alias? | Cross-study meta-analysis — trait resolution, study discovery, harmonization, per-germplasm × per-study and across-study summaries. |
The server has two stateful layers and two scoping axes:
| Layer | Default scope | Why |
|---|---|---|
| Connection state (aliases, exchanged tokens) | Tenant + session | Credentials 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 + session | Within 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.
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.ServerRegistry maps aliases to live BrAPI connections; one session can span Breedbase, T3, and Sweetpotatobase in parallel.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.CapabilityRegistry caches /serverinfo per connection and guards every tool call against unsupported endpoints. Falls back to /calls when /serverinfo is sparse.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/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).brapi_find_genotype_calls and brapi_raw_search handle the POST /search/{noun} → GET /search/{noun}/{id} 202-retry pattern automatically.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.brapi_get_image fetches bytes inline as MCP type: image blocks, preferring /images/{id}/imagecontent with imageURL fallback.OntologyResolver scores variables against a query (PUI / name / synonym / trait-class) so find_variables text:"..." returns ranked candidates even without /ontologies.none / bearer / api_key / sgn (session-token exchange) / oauth2 (client-credentials).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.
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 rowsDataframes auto-expire via TTL (BRAPI_DATASET_TTL_SECONDS, default 24h). Set BRAPI_CANVAS_DROP_ENABLED=true to expose brapi_dataframe_drop for explicit cleanup.
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/mcpNo 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).
Every variable is optional.
| Variable | Description | Default | |||
|---|---|---|---|---|---|
BRAPI_DEFAULT_BASE_URL | Default BrAPI v2 base URL (e.g. https://test-server.brapi.org/brapi/v2). | — | |||
BRAPI_DEFAULT_USERNAME / _PASSWORD | SGN session-token auth for the default connection. | — | |||
BRAPI_DEFAULT_OAUTH_CLIENT_ID / _OAUTH_CLIENT_SECRET | OAuth2 client-credentials for the default connection. | — | |||
BRAPI_DEFAULT_API_KEY / _API_KEY_HEADER | Static API key for the default connection. | header Authorization | |||
BRAPI_BUILTIN_ALIASES_DISABLED | Comma-separated alias names (case-insensitive) to remove from the built-in registry. | — | |||
BRAPI_LOAD_LIMIT | In-context row cap returned by find_* tools before spilling to a canvas dataframe. | 1000 | |||
BRAPI_PAGE_SIZE | Upstream pageSize used during canvas spillover walks (decoupled from BRAPI_LOAD_LIMIT). Dataframe ceiling = pageSize × 50. | 1000 | |||
BRAPI_MAX_CONCURRENT_REQUESTS | Per-connection concurrency cap. | 4 | |||
BRAPI_RETRY_MAX_ATTEMPTS / BRAPI_RETRY_BASE_DELAY_MS | Retry policy for 429/5xx with exponential backoff. | 3 / 500 | |||
BRAPI_REQUEST_TIMEOUT_MS | Per-request HTTP timeout. | 30000 | |||
BRAPI_COMPANION_TIMEOUT_MS | Tighter 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_MS | Async /search polling budget + interval. | 60000 / 1000 | |||
BRAPI_DATASET_TTL_SECONDS | TTL for dataframe provenance metadata persisted alongside spilled rows. | 86400 | |||
BRAPI_REFERENCE_CACHE_TTL_SECONDS | TTL for programs / trials / locations / crops cache. | 3600 | |||
BRAPI_ALLOW_PRIVATE_IPS | Allow RFC 1918 / loopback targets. Dev-only. | false | |||
BRAPI_ENABLE_WRITES | Opt-in for brapi_submit_observations registration. | false | |||
BRAPI_GENOTYPE_CALLS_MAX_PULL | Upstream row ceiling per brapi_find_genotype_calls invocation. Max 500,000. | 100000 | |||
BRAPI_CANVAS_DROP_ENABLED | Opt-in for brapi_dataframe_drop registration. Off by default; dataframes expire via TTL when left unmanaged. | false | |||
BRAPI_EXPORT_DIR | Directory 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_MS | Per-query response row cap and wall-clock timeout for brapi_dataframe_query. | 10000 / 30000 | |||
MCP_TRANSPORT_TYPE / MCP_HTTP_PORT / MCP_SESSION_MODE | Transport (stdio \ | http), HTTP port, session mode (stateful \ | stateless \ | auto). | stdio / 3010 / auto |
MCP_AUTH_MODE / MCP_LOG_LEVEL / STORAGE_PROVIDER_TYPE / OTEL_ENABLED | Auth mode (none \ | jwt \ | oauth), log level, storage backend, OpenTelemetry. | none / info / in-memory / false | |
BRAPI_SESSION_ISOLATION | When 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.
brapi_connect resolves baseUrl and auth from env vars when the agent omits them — credentials never enter the LLM context. Four layers of precedence:
BRAPI_<ALIAS>_* (uppercased, hyphens → underscores: my-server → BRAPI_MY_SERVER_*).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 set | Resolved mode |
|---|---|
_USERNAME + _PASSWORD | sgn (Breedbase /token exchange) |
_BEARER_TOKEN | bearer |
_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-KeyThen the agent calls brapi_connect({ alias: 'bti-cassava' }) — no baseUrl, no auth, no secrets in the prompt.
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.
| Alias | Upstream | Hosted by | Crop | Notes |
|---|---|---|---|---|
bti-cassava | cassavabase.org | Boyce Thompson Institute | Cassava | NextGen Cassava |
bti-sweetpotato | sweetpotatobase.org | Boyce Thompson Institute | Sweet potato | |
bti-breedbase-demo | breedbase.org | Boyce Thompson Institute | _Demo_ | Sample data only — onboarding + tests. |
t3-wheat | wheat.triticeaetoolbox.org | Triticeae Toolbox (T3) | Wheat | Wheat CAP / IWYP. |
t3-oat | oat.triticeaetoolbox.org | Triticeae Toolbox (T3) | Oat | Global Oat Genetics Database. |
t3-barley | barley.triticeaetoolbox.org | Triticeae Toolbox (T3) | Barley | T-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.
# 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 definitionsdocker build -t brapi-mcp-server .
docker run --rm -p 3010:3010 brapi-mcp-serverDefaults 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.
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.
| Shape | Settings | Isolation | Best for |
|---|---|---|---|
| Per-session (default) | MCP_AUTH_MODE=none + HTTP stateful + BRAPI_SESSION_ISOLATION=true | Each 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 credentials | MCP_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 workspace | MCP_AUTH_MODE=none + BRAPI_SESSION_ISOLATION=false | All 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:
tenantId='default'.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.BRAPI_SESSION_ISOLATION=false and rely on shared trust. This is the shared-workspace shape.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.
See CLAUDE.md for full architectural rules. Short version:
try/catch in tool logicctx.log for logging, ctx.state for storage — no console, no direct persistencetools array of createApp() in src/index.tsgit 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 testPRs welcome.
Apache-2.0 — see LICENSE.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.