@cyanheads/brapi-mcp-server 0.6.4 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +11 -4
- package/README.md +10 -6
- package/changelog/0.7.x/0.7.0.md +23 -0
- package/changelog/0.7.x/0.7.1.md +17 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/resources/definitions/brapi-variable.resource.d.ts +30 -0
- package/dist/mcp-server/resources/definitions/brapi-variable.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/brapi-variable.resource.js +78 -0
- package/dist/mcp-server/resources/definitions/brapi-variable.resource.js.map +1 -0
- package/dist/mcp-server/tools/definitions/brapi-build-phenotype-matrix.tool.d.ts +82 -0
- package/dist/mcp-server/tools/definitions/brapi-build-phenotype-matrix.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/brapi-build-phenotype-matrix.tool.js +366 -0
- package/dist/mcp-server/tools/definitions/brapi-build-phenotype-matrix.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/brapi-export-genotype-matrix.tool.d.ts +89 -0
- package/dist/mcp-server/tools/definitions/brapi-export-genotype-matrix.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/brapi-export-genotype-matrix.tool.js +494 -0
- package/dist/mcp-server/tools/definitions/brapi-export-genotype-matrix.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/brapi-find-genotype-calls.tool.d.ts +1 -0
- package/dist/mcp-server/tools/definitions/brapi-find-genotype-calls.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-genotype-calls.tool.js +17 -93
- package/dist/mcp-server/tools/definitions/brapi-find-genotype-calls.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-germplasm.tool.d.ts +1 -0
- package/dist/mcp-server/tools/definitions/brapi-find-germplasm.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-images.tool.d.ts +1 -0
- package/dist/mcp-server/tools/definitions/brapi-find-images.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.d.ts +1 -0
- package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.d.ts +1 -0
- package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.d.ts +1 -0
- package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.d.ts +1 -0
- package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.d.ts +1 -0
- package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-germplasm-performance.tool.d.ts +60 -0
- package/dist/mcp-server/tools/definitions/brapi-germplasm-performance.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/brapi-germplasm-performance.tool.js +416 -0
- package/dist/mcp-server/tools/definitions/brapi-germplasm-performance.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/brapi-raw-get.tool.d.ts +1 -0
- package/dist/mcp-server/tools/definitions/brapi-raw-get.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-raw-search.tool.d.ts +1 -0
- package/dist/mcp-server/tools/definitions/brapi-raw-search.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-walk-pedigree.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-walk-pedigree.tool.js +19 -6
- package/dist/mcp-server/tools/definitions/brapi-walk-pedigree.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/index.d.ts +160 -0
- package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/index.js +6 -0
- package/dist/mcp-server/tools/definitions/index.js.map +1 -1
- package/dist/mcp-server/tools/shared/canvas-columns.d.ts +55 -0
- package/dist/mcp-server/tools/shared/canvas-columns.d.ts.map +1 -0
- package/dist/mcp-server/tools/shared/canvas-columns.js +151 -0
- package/dist/mcp-server/tools/shared/canvas-columns.js.map +1 -0
- package/dist/mcp-server/tools/shared/find-helpers.d.ts +1 -0
- package/dist/mcp-server/tools/shared/find-helpers.d.ts.map +1 -1
- package/dist/mcp-server/tools/shared/find-helpers.js +12 -0
- package/dist/mcp-server/tools/shared/find-helpers.js.map +1 -1
- package/dist/mcp-server/tools/shared/genotype-calls.d.ts +81 -0
- package/dist/mcp-server/tools/shared/genotype-calls.d.ts.map +1 -0
- package/dist/mcp-server/tools/shared/genotype-calls.js +172 -0
- package/dist/mcp-server/tools/shared/genotype-calls.js.map +1 -0
- package/dist/mcp-server/tools/shared/observations.d.ts +87 -0
- package/dist/mcp-server/tools/shared/observations.d.ts.map +1 -0
- package/dist/mcp-server/tools/shared/observations.js +285 -0
- package/dist/mcp-server/tools/shared/observations.js.map +1 -0
- package/dist/services/canvas-bridge/canvas-bridge.d.ts +6 -0
- package/dist/services/canvas-bridge/canvas-bridge.d.ts.map +1 -1
- package/dist/services/canvas-bridge/canvas-bridge.js +10 -2
- package/dist/services/canvas-bridge/canvas-bridge.js.map +1 -1
- package/manifest.json +1 -1
- package/package.json +3 -3
- package/server.json +3 -3
package/CLAUDE.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Agent Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** brapi-mcp-server
|
|
4
|
-
**Version:** 0.
|
|
4
|
+
**Version:** 0.7.1
|
|
5
5
|
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
|
|
6
6
|
|
|
7
7
|
> **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
|
|
@@ -168,7 +168,7 @@ Handlers receive a unified `ctx` object. Currently used surface:
|
|
|
168
168
|
| `ctx.requestId` | Unique request ID — auto-attached to every `ctx.log` entry. |
|
|
169
169
|
| `ctx.tenantId` | Tenant ID from JWT or `'default'` for stdio / HTTP+`auth=none` — outer scope on all `ctx.state` reads/writes. |
|
|
170
170
|
|
|
171
|
-
`ctx.elicit` is used by `brapi_submit_observations` to gate apply-mode writes behind user confirmation (with explicit `force: true` as the bypass). `ctx.sample` and `ctx.progress` are not used yet — they'll show up when long-running workflows (pedigree traversal, genotype-call pulls) need progress reporting or LLM sampling. `ctx.fail(reason, …)` is the typed thrower keyed off declared `errors[]` contracts — used by
|
|
171
|
+
`ctx.elicit` is used by `brapi_submit_observations` to gate apply-mode writes behind user confirmation (with explicit `force: true` as the bypass). `ctx.sample` and `ctx.progress` are not used yet — they'll show up when long-running workflows (pedigree traversal, genotype-call pulls) need progress reporting or LLM sampling. `ctx.fail(reason, …)` is the typed thrower keyed off declared `errors[]` contracts — used by 14 tools and 1 resource today. `ctx.recoveryFor(reason)` resolves the matching contract entry's recovery hint into `data.recovery.hint` so it surfaces on the wire.
|
|
172
172
|
|
|
173
173
|
---
|
|
174
174
|
|
|
@@ -176,7 +176,7 @@ Handlers receive a unified `ctx` object. Currently used surface:
|
|
|
176
176
|
|
|
177
177
|
Handlers throw — the framework catches, classifies, and formats.
|
|
178
178
|
|
|
179
|
-
**Default for new tools: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` to receive a typed `ctx.fail(reason, …)` keyed by the declared reason union. TypeScript catches `ctx.fail('typo')` at compile time, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. The `recovery` field is required descriptive metadata (≥ 5 words, lint-validated); to surface it on the wire, spread `...ctx.recoveryFor('reason')` into `data` or pass an explicit `{ recovery: { hint: '...' } }` when runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring. Live across the BrAPI surface today: `
|
|
179
|
+
**Default for new tools: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` to receive a typed `ctx.fail(reason, …)` keyed by the declared reason union. TypeScript catches `ctx.fail('typo')` at compile time, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. The `recovery` field is required descriptive metadata (≥ 5 words, lint-validated); to surface it on the wire, spread `...ctx.recoveryFor('reason')` into `data` or pass an explicit `{ recovery: { hint: '...' } }` when runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring. Live across the BrAPI surface today: `brapi_build_phenotype_matrix`, `brapi_dataframe_describe`, `brapi_dataframe_export`, `brapi_dataframe_query`, `brapi_describe_filters`, `brapi_export_genotype_matrix`, `brapi_find_genotype_calls`, `brapi_germplasm_performance`, `brapi_get_germplasm`, `brapi_get_image`, `brapi_get_study`, `brapi_raw_get`, `brapi_raw_search`, `brapi_submit_observations`, plus the `brapi://variable/{observationVariableDbId}` resource.
|
|
180
180
|
|
|
181
181
|
```ts
|
|
182
182
|
errors: [
|
|
@@ -219,7 +219,7 @@ Available factories include `notFound`, `validationError`, `forbidden`, `unautho
|
|
|
219
219
|
|
|
220
220
|
```text
|
|
221
221
|
src/
|
|
222
|
-
index.ts # createApp() entry point — registers
|
|
222
|
+
index.ts # createApp() entry point — registers 25 tools, 6 resources, 2 prompts; inits 7 services
|
|
223
223
|
config/
|
|
224
224
|
server-config.ts # BRAPI_* env vars (Zod schema, lazy-parsed)
|
|
225
225
|
alias-credentials.ts # Per-alias env-var resolution (BRAPI_<ALIAS>_*) for brapi_connect
|
|
@@ -254,6 +254,9 @@ src/
|
|
|
254
254
|
brapi-dataframe-query.tool.ts # Run SQL across canvas dataframes (SELECT only); typed columns response
|
|
255
255
|
brapi-dataframe-drop.tool.ts # Drop a dataframe by name (opt-in via BRAPI_CANVAS_DROP_ENABLED)
|
|
256
256
|
brapi-dataframe-export.tool.ts # Write CSV/Parquet/JSON to BRAPI_EXPORT_DIR (opt-in, stdio-only)
|
|
257
|
+
brapi-build-phenotype-matrix.tool.ts # Germplasm × trait matrix from studies; materialized as canvas dataframe
|
|
258
|
+
brapi-germplasm-performance.tool.ts # Per-variable aggregates (n, mean, median, sd) for a single germplasm
|
|
259
|
+
brapi-export-genotype-matrix.tool.ts # Genotype calls → germplasm × variant dataframe + VCF-lite / PLINK serialization
|
|
257
260
|
brapi-submit-observations.tool.ts # Two-phase observation write — preview / apply (POST + PUT) with elicit gate
|
|
258
261
|
brapi-raw-get.tool.ts # Last-resort GET passthrough with routing nudge
|
|
259
262
|
brapi-raw-search.tool.ts # Last-resort POST /search passthrough with async polling
|
|
@@ -262,6 +265,9 @@ src/
|
|
|
262
265
|
orientation-envelope.ts # Shared envelope builder + formatter
|
|
263
266
|
find-helpers.ts # Alias / loadLimit / extraFilters fragments, mergeFilters, maybeSpill, DataframeHandleSchema
|
|
264
267
|
raw-routing-hints.ts # Routing nudges emitted by raw_get / raw_search when a curated tool exists
|
|
268
|
+
canvas-columns.ts # SQL-safe column-name sanitizer + variableLegend builder (shared by matrix tools)
|
|
269
|
+
observations.ts # Study-anchored observation pull shared by phenotype-matrix and germplasm-performance
|
|
270
|
+
genotype-calls.ts # Async genotype-call collector shared by find-genotype-calls and export-genotype-matrix
|
|
265
271
|
resources/
|
|
266
272
|
definitions/
|
|
267
273
|
brapi-server-info.resource.ts # brapi://server/info — orientation envelope (default connection)
|
|
@@ -269,6 +275,7 @@ src/
|
|
|
269
275
|
brapi-study.resource.ts # brapi://study/{studyDbId} — single study with FKs
|
|
270
276
|
brapi-germplasm.resource.ts # brapi://germplasm/{germplasmDbId} — single germplasm with attributes + parents
|
|
271
277
|
brapi-filters.resource.ts # brapi://filters/{endpoint} — filter catalog
|
|
278
|
+
brapi-variable.resource.ts # brapi://variable/{observationVariableDbId} — observation variable (trait, scale, method)
|
|
272
279
|
prompts/
|
|
273
280
|
definitions/
|
|
274
281
|
brapi-eda-study.prompt.ts # EDA playbook for one study (orient → variables → coverage → outliers → report)
|
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<h1>@cyanheads/brapi-mcp-server</h1>
|
|
3
3
|
<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>
|
|
4
|
-
<div>
|
|
4
|
+
<div>25 Tools • 6 Resources • 2 Prompts • Multi-agent collaboration</div>
|
|
5
5
|
</p>
|
|
6
6
|
</div>
|
|
7
7
|
|
|
8
8
|
<div align="center">
|
|
9
9
|
|
|
10
|
-
[](https://www.npmjs.com/package/@cyanheads/brapi-mcp-server) [](https://www.npmjs.com/package/@cyanheads/brapi-mcp-server) [](./CHANGELOG.md) [](https://modelcontextprotocol.io/) [](./LICENSE) [](https://www.typescriptlang.org/) [](https://bun.sh/) [](./CHANGELOG.md)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
## Tools
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
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.
|
|
27
27
|
|
|
28
28
|
### Orient
|
|
29
29
|
|
|
@@ -58,6 +58,9 @@
|
|
|
58
58
|
| `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 }[]`). |
|
|
59
59
|
| `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. |
|
|
60
60
|
| `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. |
|
|
61
|
+
| `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. |
|
|
62
|
+
| `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. |
|
|
63
|
+
| `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. |
|
|
61
64
|
|
|
62
65
|
### Write (opt-in: `BRAPI_ENABLE_WRITES=true`)
|
|
63
66
|
|
|
@@ -87,6 +90,7 @@ URI-addressable mirrors of the curated tool surface for clients that prefer reso
|
|
|
87
90
|
| `brapi://study/{studyDbId}` | `brapi_get_study` |
|
|
88
91
|
| `brapi://germplasm/{germplasmDbId}` | `brapi_get_germplasm` |
|
|
89
92
|
| `brapi://filters/{endpoint}` | `brapi_describe_filters` |
|
|
93
|
+
| `brapi://variable/{observationVariableDbId}` | Observation variable record (trait, scale, method, ontology) |
|
|
90
94
|
|
|
91
95
|
---
|
|
92
96
|
|
|
@@ -129,7 +133,7 @@ Within one (tenant, session), dataframes act as a self-cleaning shared notebook:
|
|
|
129
133
|
- **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.
|
|
130
134
|
- **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).
|
|
131
135
|
- **Async-search transparency** — `brapi_find_genotype_calls` and `brapi_raw_search` handle the `POST /search/{noun}` → `GET /search/{noun}/{id}` 202-retry pattern automatically.
|
|
132
|
-
- **Pedigree DAG walks** — `brapi_walk_pedigree` BFS-traverses ancestry / descendancy with cycle detection (BrAPI only exposes one generation per call).
|
|
136
|
+
- **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.
|
|
133
137
|
- **Image content** — `brapi_get_image` fetches bytes inline as MCP `type: image` blocks, preferring `/images/{id}/imagecontent` with `imageURL` fallback.
|
|
134
138
|
- **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`.
|
|
135
139
|
- **Auth variants in one schema** — tagged-union covers `none` / `bearer` / `api_key` / `sgn` (session-token exchange) / `oauth2` (client-credentials).
|
|
@@ -141,7 +145,7 @@ Built on [`@cyanheads/mcp-ts-core`](https://www.npmjs.com/package/@cyanheads/mcp
|
|
|
141
145
|
|
|
142
146
|
## Working with dataframes
|
|
143
147
|
|
|
144
|
-
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, … }`). 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.
|
|
148
|
+
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.
|
|
145
149
|
|
|
146
150
|
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](#multi-agent-workflows) for cross-session / cross-tenant rules.
|
|
147
151
|
|
|
@@ -167,7 +171,7 @@ Add to your MCP client config — pick one runner:
|
|
|
167
171
|
```json
|
|
168
172
|
{
|
|
169
173
|
"mcpServers": {
|
|
170
|
-
"brapi": {
|
|
174
|
+
"brapi-mcp-server": {
|
|
171
175
|
"type": "stdio",
|
|
172
176
|
"command": "bunx",
|
|
173
177
|
"args": ["@cyanheads/brapi-mcp-server@latest"],
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "phenotype matrix, germplasm performance, genotype export, observation variable resource"
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.7.0 — 2026-06-01
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`brapi_build_phenotype_matrix`** — builds a germplasm × trait observation matrix from one or more studies and materializes it as a canvas dataframe. Supports wide (pivot) and long shape with configurable per-cell aggregation; wide-column names are SQL-safe identifiers with a `variableLegend` map back to display names. Study-anchored pull prefers `GET /observations?studyDbId`, falls back to observation-unit embedded observations then germplasm-anchored observations for servers where the `studyDbId` filter is unsupported. Shared observation-pull machinery with `brapi_germplasm_performance`. ([#8](https://github.com/cyanheads/brapi-mcp-server/issues/8))
|
|
12
|
+
- **`brapi_germplasm_performance`** — per-variable performance aggregates (n, mean, median, sample sd, min, max, studyCount, studyDbIds, seasons) for a single germplasm across all studies where it has observations. Discovers candidate studies via `GET /studies?germplasmDbIds` with a dialect-honor cross-check that warns when a server silently ignores the filter. ([#9](https://github.com/cyanheads/brapi-mcp-server/issues/9))
|
|
13
|
+
- **`brapi_export_genotype_matrix`** — exports genotype calls for a variant set as a germplasm × variant canvas dataframe (SQL-safe column names, `variantColumnLegend` map); also serializes to VCF-lite (`#CHROM POS ID REF ALT` + per-sample genotype columns) and PLINK (`.ped` + `.map`). Reuses `tools/shared/genotype-calls.ts` extracted from `brapi_find_genotype_calls`. ([#12](https://github.com/cyanheads/brapi-mcp-server/issues/12))
|
|
14
|
+
- **`brapi://variable/{observationVariableDbId}`** resource — single observation-variable record (trait, scale, method, ontology) on the default connection via `GET /variables/{id}`. ([#14](https://github.com/cyanheads/brapi-mcp-server/issues/14))
|
|
15
|
+
- **`tools/shared/canvas-columns.ts`** — SQL-safe column-name sanitizer and `variableLegend` / `variantColumnLegend` builder, shared by `brapi_build_phenotype_matrix` and `brapi_export_genotype_matrix`.
|
|
16
|
+
- **`tools/shared/observations.ts`** — study-anchored observation pull shared by `brapi_build_phenotype_matrix` and `brapi_germplasm_performance`.
|
|
17
|
+
- **`tools/shared/genotype-calls.ts`** — async genotype-call collector extracted from `brapi_find_genotype_calls`, shared by `brapi_export_genotype_matrix`.
|
|
18
|
+
|
|
19
|
+
## Changed
|
|
20
|
+
|
|
21
|
+
- **`brapi_find_genotype_calls`** refactored to consume `tools/shared/genotype-calls.ts`; behavior unchanged.
|
|
22
|
+
- **`@duckdb/node-api`** `^1.5.3-r.2` → `^1.5.3-r.3`
|
|
23
|
+
- **`vitest`** `^4.1.7` → `^4.1.8` (dev)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "fix: find_* dataframe spillover survives reserved-word columns (end, order, etc.) via centralized sanitization; brapi_walk_pedigree now enforces its 1,000-node cap mid-traversal as a hard ceiling"
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.7.1 — 2026-06-01
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`DataframeHandleSchema.columnLegend`** — optional `Record<string, string>` mapping each sanitized SQL column name back to its original upstream key (e.g. `end_` → `end`). Present only when one or more columns were renamed; absent otherwise. Surfaces in `brapi_dataframe_query`/`brapi_dataframe_describe` consumers so agents know which columns to reference in SQL. ([#37](https://github.com/cyanheads/brapi-mcp-server/issues/37))
|
|
12
|
+
- **`brapi_walk_pedigree` truncation warning** — emits a warning when the 1,000-node cap cuts a walk short; `truncated: true` propagates to the result. ([#17](https://github.com/cyanheads/brapi-mcp-server/issues/17))
|
|
13
|
+
|
|
14
|
+
## Fixed
|
|
15
|
+
|
|
16
|
+
- **`find_*` dataframe spillover** no longer throws `Canvas column name "end" is a reserved SQL keyword` when an upstream row carries a reserved word or invalid identifier shape. Sanitization is centralized in `CanvasBridge.registerDataframe` via `sanitizeRowColumns`, covering germplasm, studies, observations, locations, images, variables, variants, and any future `find_*` tool automatically. ([#37](https://github.com/cyanheads/brapi-mcp-server/issues/37))
|
|
17
|
+
- **`brapi_walk_pedigree` node cap** was enforced only at the start of each depth iteration; a single high-fan-out expansion could overshoot to ~4× the limit before the next check. The cap is now a hard per-registration ceiling enforced inside the expansion loop. ([#17](https://github.com/cyanheads/brapi-mcp-server/issues/17))
|
package/dist/index.js
CHANGED
|
@@ -46,6 +46,7 @@ import { brapiFiltersResource } from './mcp-server/resources/definitions/brapi-f
|
|
|
46
46
|
import { brapiGermplasmResource } from './mcp-server/resources/definitions/brapi-germplasm.resource.js';
|
|
47
47
|
import { brapiServerInfoResource } from './mcp-server/resources/definitions/brapi-server-info.resource.js';
|
|
48
48
|
import { brapiStudyResource } from './mcp-server/resources/definitions/brapi-study.resource.js';
|
|
49
|
+
import { brapiVariableResource } from './mcp-server/resources/definitions/brapi-variable.resource.js';
|
|
49
50
|
import { dropToolDefinitions, exportToolDefinitions, readOnlyToolDefinitions, writeToolDefinitions, } from './mcp-server/tools/definitions/index.js';
|
|
50
51
|
const serverConfig = getServerConfig();
|
|
51
52
|
/**
|
|
@@ -100,6 +101,7 @@ await createApp({
|
|
|
100
101
|
brapiCallsResource,
|
|
101
102
|
brapiStudyResource,
|
|
102
103
|
brapiGermplasmResource,
|
|
104
|
+
brapiVariableResource,
|
|
103
105
|
brapiFiltersResource,
|
|
104
106
|
],
|
|
105
107
|
prompts: [brapiEdaStudy, brapiMetaAnalysis],
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AAEH,0EAA0E;AAC1E,yEAAyE;AACzE,qEAAqE;AACrE,uEAAuE;AACvE,0BAA0B;AAC1B,EAAE;AACF,2EAA2E;AAC3E,uEAAuE;AACvE,0EAA0E;AAC1E,gEAAgE;AAChE,wDAAwD;AACxD,OAAO,CAAC,GAAG,CAAC,oBAAoB,KAAK,QAAQ,CAAC;AAE9C,6EAA6E;AAC7E,wEAAwE;AACxE,4EAA4E;AAC5E,yEAAyE;AACzE,wEAAwE;AACxE,yEAAyE;AACzE,kBAAkB;AAClB,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACjC,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;AAClE,CAAC;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAC;AAClF,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,4DAA4D,CAAC;AAC3F,OAAO,EAAE,iBAAiB,EAAE,MAAM,gEAAgE,CAAC;AACnG,OAAO,EAAE,kBAAkB,EAAE,MAAM,4DAA4D,CAAC;AAChG,OAAO,EAAE,oBAAoB,EAAE,MAAM,8DAA8D,CAAC;AACpG,OAAO,EAAE,sBAAsB,EAAE,MAAM,gEAAgE,CAAC;AACxG,OAAO,EAAE,uBAAuB,EAAE,MAAM,kEAAkE,CAAC;AAC3G,OAAO,EAAE,kBAAkB,EAAE,MAAM,4DAA4D,CAAC;AAChG,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,yCAAyC,CAAC;AAEjD,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;AAEvC;;;;GAIG;AACH,MAAM,YAAY,GAAG;IACnB,MAAM,EAAE,gEAAgE;IACxE,IAAI,EAAE,iHAAiH;CACxH,CAAC;AAEF,MAAM,cAAc,GAAG;IACrB,MAAM,EAAE,kDAAkD;IAC1D,IAAI,EAAE,gEAAgE;CACvE,CAAC;AAEF;;;;;;;;GAQG;AACH,SAAS,iBAAiB;IACxB,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC;IAChE,IAAI,aAAa,KAAK,OAAO,EAAE,CAAC;QAC9B,OAAO;YACL,MAAM,EAAE,4CAA4C;YACpD,IAAI,EAAE,mHAAmH;SAC1H,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC;QAC5B,OAAO;YACL,MAAM,EAAE,4DAA4D;YACpE,IAAI,EAAE,wFAAwF;SAC/F,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,cAAc,GAAG,iBAAiB,EAAE,CAAC;AAE3C,MAAM,KAAK,GAAG;IACZ,GAAG,uBAAuB;IAC1B,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC/B,YAAY,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,YAAY,CAAC,CACnE;IACD,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3F,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAChC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,cAAc,CAAC,CAChE;CACF,CAAC;AAEF,MAAM,SAAS,CAAC;IACd,KAAK;IACL,SAAS,EAAE;QACT,uBAAuB;QACvB,kBAAkB;QAClB,kBAAkB;QAClB,sBAAsB;QACtB,oBAAoB;KACrB;IACD,OAAO,EAAE,CAAC,aAAa,EAAE,iBAAiB,CAAC;IAC3C,+EAA+E;IAC/E,6EAA6E;IAC7E,+EAA+E;IAC/E,OAAO,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE;IAC/B,YAAY,EACV,onBAAonB;IACtnB,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,kBAAkB,CACtB,kLAAkL,EAClL,EAAE,kBAAkB,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,QAAQ,EAAE,CACrE,CAAC;QACJ,CAAC;QACD,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAC5C,eAAe,CAAC,YAAY,CAAC,CAAC;QAC9B,sBAAsB,CAAC,YAAY,CAAC,CAAC;QACrC,wBAAwB,EAAE,CAAC;QAC3B,sBAAsB,CAAC,YAAY,CAAC,CAAC;QACrC,kBAAkB,CAAC,YAAY,CAAC,CAAC;QACjC,oBAAoB,EAAE,CAAC;IACzB,CAAC;CACF,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AAEH,0EAA0E;AAC1E,yEAAyE;AACzE,qEAAqE;AACrE,uEAAuE;AACvE,0BAA0B;AAC1B,EAAE;AACF,2EAA2E;AAC3E,uEAAuE;AACvE,0EAA0E;AAC1E,gEAAgE;AAChE,wDAAwD;AACxD,OAAO,CAAC,GAAG,CAAC,oBAAoB,KAAK,QAAQ,CAAC;AAE9C,6EAA6E;AAC7E,wEAAwE;AACxE,4EAA4E;AAC5E,yEAAyE;AACzE,wEAAwE;AACxE,yEAAyE;AACzE,kBAAkB;AAClB,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACjC,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;AAClE,CAAC;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAC;AAClF,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,4DAA4D,CAAC;AAC3F,OAAO,EAAE,iBAAiB,EAAE,MAAM,gEAAgE,CAAC;AACnG,OAAO,EAAE,kBAAkB,EAAE,MAAM,4DAA4D,CAAC;AAChG,OAAO,EAAE,oBAAoB,EAAE,MAAM,8DAA8D,CAAC;AACpG,OAAO,EAAE,sBAAsB,EAAE,MAAM,gEAAgE,CAAC;AACxG,OAAO,EAAE,uBAAuB,EAAE,MAAM,kEAAkE,CAAC;AAC3G,OAAO,EAAE,kBAAkB,EAAE,MAAM,4DAA4D,CAAC;AAChG,OAAO,EAAE,qBAAqB,EAAE,MAAM,+DAA+D,CAAC;AACtG,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,yCAAyC,CAAC;AAEjD,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;AAEvC;;;;GAIG;AACH,MAAM,YAAY,GAAG;IACnB,MAAM,EAAE,gEAAgE;IACxE,IAAI,EAAE,iHAAiH;CACxH,CAAC;AAEF,MAAM,cAAc,GAAG;IACrB,MAAM,EAAE,kDAAkD;IAC1D,IAAI,EAAE,gEAAgE;CACvE,CAAC;AAEF;;;;;;;;GAQG;AACH,SAAS,iBAAiB;IACxB,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC;IAChE,IAAI,aAAa,KAAK,OAAO,EAAE,CAAC;QAC9B,OAAO;YACL,MAAM,EAAE,4CAA4C;YACpD,IAAI,EAAE,mHAAmH;SAC1H,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC;QAC5B,OAAO;YACL,MAAM,EAAE,4DAA4D;YACpE,IAAI,EAAE,wFAAwF;SAC/F,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,cAAc,GAAG,iBAAiB,EAAE,CAAC;AAE3C,MAAM,KAAK,GAAG;IACZ,GAAG,uBAAuB;IAC1B,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC/B,YAAY,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,YAAY,CAAC,CACnE;IACD,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3F,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAChC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,cAAc,CAAC,CAChE;CACF,CAAC;AAEF,MAAM,SAAS,CAAC;IACd,KAAK;IACL,SAAS,EAAE;QACT,uBAAuB;QACvB,kBAAkB;QAClB,kBAAkB;QAClB,sBAAsB;QACtB,qBAAqB;QACrB,oBAAoB;KACrB;IACD,OAAO,EAAE,CAAC,aAAa,EAAE,iBAAiB,CAAC;IAC3C,+EAA+E;IAC/E,6EAA6E;IAC7E,+EAA+E;IAC/E,OAAO,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE;IAC/B,YAAY,EACV,onBAAonB;IACtnB,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,kBAAkB,CACtB,kLAAkL,EAClL,EAAE,kBAAkB,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,QAAQ,EAAE,CACrE,CAAC;QACJ,CAAC;QACD,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAC5C,eAAe,CAAC,YAAY,CAAC,CAAC;QAC9B,sBAAsB,CAAC,YAAY,CAAC,CAAC;QACrC,wBAAwB,EAAE,CAAC;QAC3B,sBAAsB,CAAC,YAAY,CAAC,CAAC;QACrC,kBAAkB,CAAC,YAAY,CAAC,CAAC;QACjC,oBAAoB,EAAE,CAAC;IACzB,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `brapi://variable/{observationVariableDbId}` — single
|
|
3
|
+
* observation-variable record on the default connection. The single-record,
|
|
4
|
+
* URI-addressable counterpart to `brapi_find_variables` (which returns a list
|
|
5
|
+
* envelope). Fills the resource gap alongside `brapi://study/{id}` and
|
|
6
|
+
* `brapi://germplasm/{id}`.
|
|
7
|
+
*
|
|
8
|
+
* Fetches the canonical `/variables/{observationVariableDbId}` payload directly
|
|
9
|
+
* (trait, scale, method, ontology) and returns it verbatim — mirroring how
|
|
10
|
+
* `brapi_get_germplasm` reads `/germplasm/{id}`. A 404 (or an empty record)
|
|
11
|
+
* surfaces as the typed `variable_not_found` error.
|
|
12
|
+
*
|
|
13
|
+
* @module mcp-server/resources/definitions/brapi-variable.resource
|
|
14
|
+
*/
|
|
15
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
16
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
17
|
+
export declare const brapiVariableResource: import("@cyanheads/mcp-ts-core").ResourceDefinition<z.ZodObject<{
|
|
18
|
+
observationVariableDbId: z.ZodString;
|
|
19
|
+
}, z.core.$strip>, undefined, readonly [{
|
|
20
|
+
readonly reason: "unknown_alias";
|
|
21
|
+
readonly code: JsonRpcErrorCode.NotFound;
|
|
22
|
+
readonly when: "No default BrAPI connection has been registered";
|
|
23
|
+
readonly recovery: "Call brapi_connect (without an alias, or with alias `default`) before reading this resource.";
|
|
24
|
+
}, {
|
|
25
|
+
readonly reason: "variable_not_found";
|
|
26
|
+
readonly code: JsonRpcErrorCode.NotFound;
|
|
27
|
+
readonly when: "Upstream returned no observation variable record for the requested DbId";
|
|
28
|
+
readonly recovery: "Verify the observationVariableDbId on the target server, or run brapi_find_variables to discover valid IDs.";
|
|
29
|
+
}]>;
|
|
30
|
+
//# sourceMappingURL=brapi-variable.resource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brapi-variable.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/brapi-variable.resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAQjE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;GA8EhC,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `brapi://variable/{observationVariableDbId}` — single
|
|
3
|
+
* observation-variable record on the default connection. The single-record,
|
|
4
|
+
* URI-addressable counterpart to `brapi_find_variables` (which returns a list
|
|
5
|
+
* envelope). Fills the resource gap alongside `brapi://study/{id}` and
|
|
6
|
+
* `brapi://germplasm/{id}`.
|
|
7
|
+
*
|
|
8
|
+
* Fetches the canonical `/variables/{observationVariableDbId}` payload directly
|
|
9
|
+
* (trait, scale, method, ontology) and returns it verbatim — mirroring how
|
|
10
|
+
* `brapi_get_germplasm` reads `/germplasm/{id}`. A 404 (or an empty record)
|
|
11
|
+
* surfaces as the typed `variable_not_found` error.
|
|
12
|
+
*
|
|
13
|
+
* @module mcp-server/resources/definitions/brapi-variable.resource
|
|
14
|
+
*/
|
|
15
|
+
import { resource, z } from '@cyanheads/mcp-ts-core';
|
|
16
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
17
|
+
import { buildRequestOptions, isUpstreamNotFound, requireRegisteredConnection, } from '../../../mcp-server/tools/shared/find-helpers.js';
|
|
18
|
+
import { getBrapiClient } from '../../../services/brapi-client/index.js';
|
|
19
|
+
export const brapiVariableResource = resource('brapi://variable/{observationVariableDbId}', {
|
|
20
|
+
name: 'brapi-variable',
|
|
21
|
+
title: 'BrAPI observation variable record',
|
|
22
|
+
description: 'Single observation-variable resource on the default BrAPI connection — the canonical /variables/{id} record (trait, scale, method, ontology), addressable by URI. The single-record equivalent of brapi_find_variables.',
|
|
23
|
+
mimeType: 'application/json',
|
|
24
|
+
errors: [
|
|
25
|
+
{
|
|
26
|
+
reason: 'unknown_alias',
|
|
27
|
+
code: JsonRpcErrorCode.NotFound,
|
|
28
|
+
when: 'No default BrAPI connection has been registered',
|
|
29
|
+
recovery: 'Call brapi_connect (without an alias, or with alias `default`) before reading this resource.',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
reason: 'variable_not_found',
|
|
33
|
+
code: JsonRpcErrorCode.NotFound,
|
|
34
|
+
when: 'Upstream returned no observation variable record for the requested DbId',
|
|
35
|
+
recovery: 'Verify the observationVariableDbId on the target server, or run brapi_find_variables to discover valid IDs.',
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
params: z.object({
|
|
39
|
+
observationVariableDbId: z
|
|
40
|
+
.string()
|
|
41
|
+
.min(1)
|
|
42
|
+
.describe('Observation variable identifier on the default connection.'),
|
|
43
|
+
}),
|
|
44
|
+
async handler(params, ctx) {
|
|
45
|
+
const client = getBrapiClient();
|
|
46
|
+
const connection = await requireRegisteredConnection(ctx, undefined);
|
|
47
|
+
const id = encodeURIComponent(params.observationVariableDbId);
|
|
48
|
+
let env;
|
|
49
|
+
try {
|
|
50
|
+
env = await client.get(connection.baseUrl, `/variables/${id}`, ctx, buildRequestOptions(connection, undefined, { singleton: true }));
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
if (isUpstreamNotFound(err)) {
|
|
54
|
+
throw ctx.fail('variable_not_found', `Observation variable '${params.observationVariableDbId}' not found on ${connection.baseUrl}.`, {
|
|
55
|
+
observationVariableDbId: params.observationVariableDbId,
|
|
56
|
+
baseUrl: connection.baseUrl,
|
|
57
|
+
...ctx.recoveryFor('variable_not_found'),
|
|
58
|
+
}, { cause: err });
|
|
59
|
+
}
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
const variable = env.result;
|
|
63
|
+
if (!variable ||
|
|
64
|
+
typeof variable !== 'object' ||
|
|
65
|
+
!variable.observationVariableDbId) {
|
|
66
|
+
throw ctx.fail('variable_not_found', `Observation variable '${params.observationVariableDbId}' not found on ${connection.baseUrl}.`, {
|
|
67
|
+
observationVariableDbId: params.observationVariableDbId,
|
|
68
|
+
baseUrl: connection.baseUrl,
|
|
69
|
+
...ctx.recoveryFor('variable_not_found'),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return variable;
|
|
73
|
+
},
|
|
74
|
+
list: async () => ({
|
|
75
|
+
resources: [],
|
|
76
|
+
}),
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=brapi-variable.resource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brapi-variable.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/brapi-variable.resource.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `brapi_build_phenotype_matrix` — pull observations across one or
|
|
3
|
+
* more studies and pivot them into a germplasm × trait matrix materialized as a
|
|
4
|
+
* canvas dataframe. Returns a dataframe handle plus a summary of dimensions,
|
|
5
|
+
* aggregate method, and any warnings encountered during the pull.
|
|
6
|
+
*
|
|
7
|
+
* The per-study observation pull (with its `/observations` → `/observationunits`
|
|
8
|
+
* fallback chain) lives in `../shared/observations.ts`, shared with
|
|
9
|
+
* `brapi_germplasm_performance`.
|
|
10
|
+
*
|
|
11
|
+
* Column safety: wide-matrix column names are SQL-safe identifiers derived from
|
|
12
|
+
* `observationVariableDbId` via `../shared/canvas-columns.ts` — BrAPI DbIds are
|
|
13
|
+
* routinely numeric (Breedbase) or collide with reserved SQL words, both of
|
|
14
|
+
* which the canvas identifier gate rejects. A `variableLegend` mapping safe
|
|
15
|
+
* column → display name is returned so callers can resolve columns back.
|
|
16
|
+
*
|
|
17
|
+
* @module mcp-server/tools/definitions/brapi-build-phenotype-matrix.tool
|
|
18
|
+
*/
|
|
19
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
20
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
21
|
+
export declare const brapiBuildPhenotypeMatrix: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
|
|
22
|
+
alias: z.ZodOptional<z.ZodString>;
|
|
23
|
+
studies: z.ZodArray<z.ZodString>;
|
|
24
|
+
variables: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
25
|
+
germplasm: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
26
|
+
shape: z.ZodDefault<z.ZodEnum<{
|
|
27
|
+
wide: "wide";
|
|
28
|
+
long: "long";
|
|
29
|
+
}>>;
|
|
30
|
+
aggregate: z.ZodDefault<z.ZodEnum<{
|
|
31
|
+
all: "all";
|
|
32
|
+
mean: "mean";
|
|
33
|
+
median: "median";
|
|
34
|
+
first: "first";
|
|
35
|
+
}>>;
|
|
36
|
+
loadLimit: z.ZodOptional<z.ZodNumber>;
|
|
37
|
+
extraFilters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
38
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
39
|
+
alias: z.ZodString;
|
|
40
|
+
studies: z.ZodArray<z.ZodString>;
|
|
41
|
+
shape: z.ZodEnum<{
|
|
42
|
+
wide: "wide";
|
|
43
|
+
long: "long";
|
|
44
|
+
}>;
|
|
45
|
+
aggregate: z.ZodEnum<{
|
|
46
|
+
all: "all";
|
|
47
|
+
mean: "mean";
|
|
48
|
+
median: "median";
|
|
49
|
+
first: "first";
|
|
50
|
+
}>;
|
|
51
|
+
observationCount: z.ZodNumber;
|
|
52
|
+
germplasmCount: z.ZodNumber;
|
|
53
|
+
variableCount: z.ZodNumber;
|
|
54
|
+
variableLegend: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
55
|
+
dataframe: z.ZodOptional<z.ZodObject<{
|
|
56
|
+
tableName: z.ZodString;
|
|
57
|
+
rowCount: z.ZodNumber;
|
|
58
|
+
columns: z.ZodArray<z.ZodString>;
|
|
59
|
+
columnLegend: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
60
|
+
createdAt: z.ZodString;
|
|
61
|
+
expiresAt: z.ZodString;
|
|
62
|
+
truncated: z.ZodOptional<z.ZodBoolean>;
|
|
63
|
+
maxRows: z.ZodOptional<z.ZodNumber>;
|
|
64
|
+
}, z.core.$strip>>;
|
|
65
|
+
warnings: z.ZodArray<z.ZodString>;
|
|
66
|
+
}, z.core.$strip>, readonly [{
|
|
67
|
+
readonly reason: "unknown_alias";
|
|
68
|
+
readonly code: JsonRpcErrorCode.NotFound;
|
|
69
|
+
readonly when: "No connection has been registered under the requested alias";
|
|
70
|
+
readonly recovery: "Run brapi_connect with this alias (or omit `alias` to use the default connection) before calling brapi_build_phenotype_matrix.";
|
|
71
|
+
}, {
|
|
72
|
+
readonly reason: "all_filters_dropped";
|
|
73
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
74
|
+
readonly when: "The active dialect dropped every filter supplied — the call would silently widen to the unfiltered baseline";
|
|
75
|
+
readonly recovery: "Drop unsupported filters or use studies / germplasm / variables to scope the query to supported filter paths on the active dialect.";
|
|
76
|
+
}, {
|
|
77
|
+
readonly reason: "no_observation_path";
|
|
78
|
+
readonly code: JsonRpcErrorCode.ServiceUnavailable;
|
|
79
|
+
readonly when: "Neither /observations nor /observationunits returned data for any requested study after probing both paths";
|
|
80
|
+
readonly recovery: "Verify the studyDbIds exist and the BrAPI server exposes /observations or /observationunits. Use brapi_server_info to inspect the capability list.";
|
|
81
|
+
}], undefined>;
|
|
82
|
+
//# sourceMappingURL=brapi-build-phenotype-matrix.tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brapi-build-phenotype-matrix.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/brapi-build-phenotype-matrix.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAoEjE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAgPpC,CAAC"}
|