@cyanheads/brapi-mcp-server 0.6.3 → 0.7.0
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 +7 -3
- package/changelog/0.6.x/0.6.4.md +14 -0
- package/changelog/0.7.x/0.7.0.md +23 -0
- package/changelog/template.md +1 -1
- 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 +81 -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-connect.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/brapi-connect.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-dataframe-describe.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/brapi-dataframe-describe.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-dataframe-drop.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/brapi-dataframe-drop.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-dataframe-export.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/brapi-dataframe-export.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-dataframe-query.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/brapi-dataframe-query.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-describe-filters.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/brapi-describe-filters.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-export-genotype-matrix.tool.d.ts +88 -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 +7 -5
- 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 +67 -120
- 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 +8 -6
- package/dist/mcp-server/tools/definitions/brapi-find-germplasm.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-germplasm.tool.js +54 -33
- package/dist/mcp-server/tools/definitions/brapi-find-germplasm.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-images.tool.d.ts +8 -6
- package/dist/mcp-server/tools/definitions/brapi-find-images.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-images.tool.js +54 -33
- package/dist/mcp-server/tools/definitions/brapi-find-images.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.d.ts +8 -6
- package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.js +62 -41
- package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.d.ts +8 -6
- package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.js +54 -33
- package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.d.ts +8 -6
- package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.js +52 -33
- package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.d.ts +8 -6
- package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.js +54 -33
- package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.d.ts +8 -6
- package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.js +54 -33
- package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.js.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-get-germplasm.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/brapi-get-germplasm.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-get-image.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/brapi-get-image.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-get-study.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/brapi-get-study.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-raw-get.tool.d.ts +1 -1
- 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 -1
- package/dist/mcp-server/tools/definitions/brapi-raw-search.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-server-info.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/brapi-server-info.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-submit-observations.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/brapi-submit-observations.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/brapi-walk-pedigree.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/brapi-walk-pedigree.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/index.d.ts +225 -61
- 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 +32 -0
- package/dist/mcp-server/tools/shared/canvas-columns.d.ts.map +1 -0
- package/dist/mcp-server/tools/shared/canvas-columns.js +98 -0
- package/dist/mcp-server/tools/shared/canvas-columns.js.map +1 -0
- package/dist/mcp-server/tools/shared/find-helpers.d.ts +1 -1
- package/dist/mcp-server/tools/shared/find-helpers.d.ts.map +1 -1
- package/dist/mcp-server/tools/shared/find-helpers.js +6 -2
- 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/manifest.json +1 -1
- package/package.json +6 -5
- package/server.json +3 -3
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared genotype-call pull logic — the async-search machinery
|
|
3
|
+
* (`POST /search/calls` → poll → page-walk) and the `CallRow` schema extracted
|
|
4
|
+
* from `brapi-find-genotype-calls.tool.ts` so that `brapi_export_genotype_matrix`
|
|
5
|
+
* can reuse the same pull infrastructure without duplicating it.
|
|
6
|
+
*
|
|
7
|
+
* This module owns: the `CallRowSchema`, `CallFormatting` type, and the
|
|
8
|
+
* `collectCalls` / `consumePage` helpers. It does NOT contain `spillCalls` or
|
|
9
|
+
* any tool-level concerns — those stay in the consuming tool files.
|
|
10
|
+
*
|
|
11
|
+
* @module mcp-server/tools/shared/genotype-calls
|
|
12
|
+
*/
|
|
13
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
14
|
+
import { buildRequestOptions } from './find-helpers.js';
|
|
15
|
+
const PAGE_SIZE = 10_000;
|
|
16
|
+
export const CallRowSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
callSetDbId: z
|
|
19
|
+
.string()
|
|
20
|
+
.nullish()
|
|
21
|
+
.describe('FK to the call set (one germplasm × one variant set = one call set).'),
|
|
22
|
+
callSetName: z.string().nullish().describe('Display name of the call set.'),
|
|
23
|
+
variantDbId: z.string().nullish().describe('FK to the variant being called.'),
|
|
24
|
+
variantName: z.string().nullish().describe('Display name / alias of the variant.'),
|
|
25
|
+
variantSetDbId: z.string().nullish().describe('FK to the variant set the call belongs to.'),
|
|
26
|
+
genotype: z
|
|
27
|
+
.object({
|
|
28
|
+
values: z
|
|
29
|
+
.array(z.string().describe('Per-allele value string.'))
|
|
30
|
+
.nullish()
|
|
31
|
+
.describe('Encoded allele values — interpret using top-level `callFormatting`.'),
|
|
32
|
+
})
|
|
33
|
+
.passthrough()
|
|
34
|
+
.nullish()
|
|
35
|
+
.describe('Structured genotype payload (array of allele values plus server-specific fields).'),
|
|
36
|
+
genotypeValue: z
|
|
37
|
+
.string()
|
|
38
|
+
.nullish()
|
|
39
|
+
.describe('Legacy flat string form of the call (provided by some servers instead of `genotype`).'),
|
|
40
|
+
phaseSet: z
|
|
41
|
+
.string()
|
|
42
|
+
.nullish()
|
|
43
|
+
.describe('Phase-set identifier linking calls that share a haplotype phase.'),
|
|
44
|
+
})
|
|
45
|
+
.passthrough()
|
|
46
|
+
.describe('One genotype call row.');
|
|
47
|
+
/**
|
|
48
|
+
* Build the POST /search/calls body from named filter params. Handles both the
|
|
49
|
+
* singular `variantSetDbId` convenience and the plural `variantSetDbIds` array,
|
|
50
|
+
* merging and deduplicating them.
|
|
51
|
+
*/
|
|
52
|
+
export function buildCallsSearchBody(input) {
|
|
53
|
+
const body = {};
|
|
54
|
+
if (input.variantSetDbId)
|
|
55
|
+
body.variantSetDbIds = [input.variantSetDbId];
|
|
56
|
+
if (input.variantSetDbIds?.length) {
|
|
57
|
+
body.variantSetDbIds = Array.from(new Set([...(body.variantSetDbIds ?? []), ...input.variantSetDbIds]));
|
|
58
|
+
}
|
|
59
|
+
if (input.germplasmDbIds?.length)
|
|
60
|
+
body.germplasmDbIds = input.germplasmDbIds;
|
|
61
|
+
if (input.callSetDbIds?.length)
|
|
62
|
+
body.callSetDbIds = input.callSetDbIds;
|
|
63
|
+
if (input.variantDbIds?.length)
|
|
64
|
+
body.variantDbIds = input.variantDbIds;
|
|
65
|
+
if (input.callFormat)
|
|
66
|
+
body.callFormat = input.callFormat;
|
|
67
|
+
body.pageSize = PAGE_SIZE;
|
|
68
|
+
return body;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Pull all genotype call pages for a given search body, capping at `maxCalls`.
|
|
72
|
+
* Uses BrAPI's async-search pattern: `POST /search/calls` → `GET /search/calls/{id}`
|
|
73
|
+
* with 202-retry. Pages are collected until all results are fetched or `maxCalls`
|
|
74
|
+
* is reached.
|
|
75
|
+
*/
|
|
76
|
+
export async function collectCalls(input) {
|
|
77
|
+
const rows = [];
|
|
78
|
+
let callFormatting = {};
|
|
79
|
+
let truncated = false;
|
|
80
|
+
const firstBody = { ...input.body, page: 0 };
|
|
81
|
+
const first = await input.client.postSearch(input.connection.baseUrl, 'calls', firstBody, input.ctx, buildRequestOptions(input.connection));
|
|
82
|
+
let envelope;
|
|
83
|
+
let searchResultsDbId;
|
|
84
|
+
if (first.kind === 'sync') {
|
|
85
|
+
envelope = first.envelope;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
searchResultsDbId = first.searchResultsDbId;
|
|
89
|
+
envelope = await input.client.getSearchResults(input.connection.baseUrl, 'calls', first.searchResultsDbId, input.ctx, buildRequestOptions(input.connection));
|
|
90
|
+
}
|
|
91
|
+
consumePage(envelope, rows, (cf) => {
|
|
92
|
+
callFormatting = cf;
|
|
93
|
+
});
|
|
94
|
+
const totalPages = envelope.metadata?.pagination?.totalPages;
|
|
95
|
+
for (let page = 1; page < (totalPages ?? 1); page++) {
|
|
96
|
+
if (rows.length >= input.maxCalls) {
|
|
97
|
+
truncated = true;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
if (input.ctx.signal.aborted)
|
|
101
|
+
break;
|
|
102
|
+
let pageEnvelope;
|
|
103
|
+
if (searchResultsDbId) {
|
|
104
|
+
pageEnvelope = await input.client.getSearchResults(input.connection.baseUrl, 'calls', searchResultsDbId, input.ctx, buildRequestOptions(input.connection, { page, pageSize: PAGE_SIZE }));
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
const nextSearch = await input.client.postSearch(input.connection.baseUrl, 'calls', { ...input.body, page }, input.ctx, buildRequestOptions(input.connection));
|
|
108
|
+
if (nextSearch.kind === 'sync') {
|
|
109
|
+
pageEnvelope = nextSearch.envelope;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
pageEnvelope = await input.client.getSearchResults(input.connection.baseUrl, 'calls', nextSearch.searchResultsDbId, input.ctx, buildRequestOptions(input.connection));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
consumePage(pageEnvelope, rows, (cf) => {
|
|
116
|
+
callFormatting = cf;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (rows.length > input.maxCalls) {
|
|
120
|
+
rows.length = input.maxCalls;
|
|
121
|
+
truncated = true;
|
|
122
|
+
input.warnings.push(`Truncated at the deployment pull limit (${input.maxCalls} rows). Narrow the filters and re-pull; the captured slice is preserved in the spilled dataframe.`);
|
|
123
|
+
}
|
|
124
|
+
return { rows, callFormatting, truncated };
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Extract call rows and `callFormatting` hints from one BrAPI envelope page.
|
|
128
|
+
* Mutates `rows` in-place; invokes `setCallFormatting` when formatting hints
|
|
129
|
+
* are present so the caller can merge across pages.
|
|
130
|
+
*/
|
|
131
|
+
export function consumePage(envelope, rows, setCallFormatting) {
|
|
132
|
+
const result = envelope.result;
|
|
133
|
+
if (!result || typeof result !== 'object')
|
|
134
|
+
return;
|
|
135
|
+
const record = result;
|
|
136
|
+
const cf = {};
|
|
137
|
+
if (typeof record.expandHomozygotes === 'boolean')
|
|
138
|
+
cf.expandHomozygotes = record.expandHomozygotes;
|
|
139
|
+
if (typeof record.unknownString === 'string')
|
|
140
|
+
cf.unknownString = record.unknownString;
|
|
141
|
+
if (typeof record.sepPhased === 'string')
|
|
142
|
+
cf.sepPhased = record.sepPhased;
|
|
143
|
+
if (typeof record.sepUnphased === 'string')
|
|
144
|
+
cf.sepUnphased = record.sepUnphased;
|
|
145
|
+
setCallFormatting(cf);
|
|
146
|
+
const data = record.data;
|
|
147
|
+
if (!Array.isArray(data))
|
|
148
|
+
return;
|
|
149
|
+
for (const entry of data) {
|
|
150
|
+
if (typeof entry === 'object' && entry !== null) {
|
|
151
|
+
rows.push(entry);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Render the genotype string for one call row given the server's callFormatting
|
|
157
|
+
* hints. Prefers `genotype.values` joined with the appropriate separator;
|
|
158
|
+
* falls back to `genotypeValue`; returns the `unknownString` (or ".") when
|
|
159
|
+
* no data is available.
|
|
160
|
+
*/
|
|
161
|
+
export function renderGenotypeString(row, callFormatting, phased) {
|
|
162
|
+
const unknown = callFormatting.unknownString ?? '.';
|
|
163
|
+
const values = row.genotype?.values;
|
|
164
|
+
if (values && values.length > 0) {
|
|
165
|
+
const sep = phased ? (callFormatting.sepPhased ?? '|') : (callFormatting.sepUnphased ?? '/');
|
|
166
|
+
return values.join(sep);
|
|
167
|
+
}
|
|
168
|
+
if (row.genotypeValue)
|
|
169
|
+
return row.genotypeValue;
|
|
170
|
+
return unknown;
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=genotype-calls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"genotype-calls.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/shared/genotype-calls.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared observation-pull logic for the phenotype tools
|
|
3
|
+
* (`brapi_build_phenotype_matrix`, `brapi_germplasm_performance`). Owns the
|
|
4
|
+
* study-anchored observation collection strategy and the raw/normalized row
|
|
5
|
+
* shapes, so both tools pull observations identically without duplicating the
|
|
6
|
+
* server-dependent fallback chain.
|
|
7
|
+
*
|
|
8
|
+
* Observation pull strategy (server-dependent), per study:
|
|
9
|
+
* 1. Prefer `/observations?studyDbId=…` (BrAPI spec path).
|
|
10
|
+
* 2. Fall back to `/observationunits?studyDbId=…` with embedded
|
|
11
|
+
* `observations[]` arrays when `/observations` returns empty or the dialect
|
|
12
|
+
* drops the study filter — required for the BrAPI Community Test Server,
|
|
13
|
+
* where `/observations?studyDbId` is not honored.
|
|
14
|
+
* 3. When units carry `germplasmDbId`s but no embedded observations, pull
|
|
15
|
+
* `/observations?germplasmDbId=…` per germplasm and filter back to the
|
|
16
|
+
* study — stays study-anchored without an unbounded scan.
|
|
17
|
+
*
|
|
18
|
+
* `pullStudyObservations` returns `null` only when no usable observation path
|
|
19
|
+
* exists for the study (caller throws its typed `no_observation_path` error);
|
|
20
|
+
* an empty array means the paths exist but yielded no rows.
|
|
21
|
+
*
|
|
22
|
+
* @module mcp-server/tools/shared/observations
|
|
23
|
+
*/
|
|
24
|
+
import type { Context } from '@cyanheads/mcp-ts-core';
|
|
25
|
+
import type { ServerConfig } from '../../../config/server-config.js';
|
|
26
|
+
import { type BrapiClient } from '../../../services/brapi-client/index.js';
|
|
27
|
+
import type { BrapiDialect } from '../../../services/brapi-dialect/index.js';
|
|
28
|
+
import type { CallDescriptor } from '../../../services/capability-registry/types.js';
|
|
29
|
+
import type { RegisteredServer } from '../../../services/server-registry/index.js';
|
|
30
|
+
/** Raw BrAPI `/observations` row shape (passthrough). */
|
|
31
|
+
export interface RawObservationRow {
|
|
32
|
+
germplasmDbId?: string | null;
|
|
33
|
+
germplasmName?: string | null;
|
|
34
|
+
observationDbId?: string | null;
|
|
35
|
+
observationVariableDbId?: string | null;
|
|
36
|
+
observationVariableName?: string | null;
|
|
37
|
+
season?: unknown;
|
|
38
|
+
seasonDbId?: string | null;
|
|
39
|
+
studyDbId?: string | null;
|
|
40
|
+
value?: string | null;
|
|
41
|
+
[key: string]: unknown;
|
|
42
|
+
}
|
|
43
|
+
/** Normalized observation — required fields are non-empty strings after extraction. */
|
|
44
|
+
export interface NormObs {
|
|
45
|
+
germplasmDbId: string;
|
|
46
|
+
germplasmName: string;
|
|
47
|
+
observationVariableDbId: string;
|
|
48
|
+
observationVariableName: string;
|
|
49
|
+
/** Human season label (`"spring 2013"` / seasonDbId), when the row carries one. */
|
|
50
|
+
season?: string;
|
|
51
|
+
studyDbId: string;
|
|
52
|
+
value: string;
|
|
53
|
+
}
|
|
54
|
+
/** Filters that scope an observation pull beyond the study anchor. */
|
|
55
|
+
export interface ObservationPullFilters {
|
|
56
|
+
extraFilters?: Record<string, unknown> | undefined;
|
|
57
|
+
germplasm?: string[] | undefined;
|
|
58
|
+
variables?: string[] | undefined;
|
|
59
|
+
}
|
|
60
|
+
export interface PullStudyObservationsArgs {
|
|
61
|
+
client: BrapiClient;
|
|
62
|
+
config: ServerConfig;
|
|
63
|
+
connection: RegisteredServer;
|
|
64
|
+
ctx: Context;
|
|
65
|
+
dialect: BrapiDialect;
|
|
66
|
+
input: ObservationPullFilters;
|
|
67
|
+
loadLimit: number;
|
|
68
|
+
profile: Record<string, CallDescriptor>;
|
|
69
|
+
studyDbId: string;
|
|
70
|
+
warnings: string[];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Collect observations for a single study, trying `/observations` first and
|
|
74
|
+
* falling back to `/observationunits` (embedded, then germplasm-anchored).
|
|
75
|
+
* Returns `null` when no usable path exists; `[]` when paths exist but are
|
|
76
|
+
* empty.
|
|
77
|
+
*/
|
|
78
|
+
export declare function pullStudyObservations(args: PullStudyObservationsArgs): Promise<NormObs[] | null>;
|
|
79
|
+
/** Normalise a raw /observations row; drops rows missing any required field. */
|
|
80
|
+
export declare function normalizeObsRow(row: RawObservationRow): NormObs | null;
|
|
81
|
+
/**
|
|
82
|
+
* Derive a human season label from a BrAPI observation row. Servers carry
|
|
83
|
+
* season either as a nested object (`{ seasonName, year, seasonDbId }`) or a
|
|
84
|
+
* flat `seasonDbId` string. Returns undefined when neither is present.
|
|
85
|
+
*/
|
|
86
|
+
export declare function seasonLabel(row: RawObservationRow): string | undefined;
|
|
87
|
+
//# sourceMappingURL=observations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observations.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/shared/observations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,KAAK,WAAW,EAAuB,MAAM,kCAAkC,CAAC;AACzF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAQ5E,yDAAyD;AACzD,MAAM,WAAW,iBAAiB;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,uFAAuF;AACvF,MAAM,WAAW,OAAO;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,MAAM,CAAC;IAChC,uBAAuB,EAAE,MAAM,CAAC;IAChC,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,sEAAsE;AACtE,MAAM,WAAW,sBAAsB;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IACnD,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,sBAAsB,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAyH3B;AAyKD,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,GAAG,IAAI,CAuBtE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,GAAG,SAAS,CAetE"}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared observation-pull logic for the phenotype tools
|
|
3
|
+
* (`brapi_build_phenotype_matrix`, `brapi_germplasm_performance`). Owns the
|
|
4
|
+
* study-anchored observation collection strategy and the raw/normalized row
|
|
5
|
+
* shapes, so both tools pull observations identically without duplicating the
|
|
6
|
+
* server-dependent fallback chain.
|
|
7
|
+
*
|
|
8
|
+
* Observation pull strategy (server-dependent), per study:
|
|
9
|
+
* 1. Prefer `/observations?studyDbId=…` (BrAPI spec path).
|
|
10
|
+
* 2. Fall back to `/observationunits?studyDbId=…` with embedded
|
|
11
|
+
* `observations[]` arrays when `/observations` returns empty or the dialect
|
|
12
|
+
* drops the study filter — required for the BrAPI Community Test Server,
|
|
13
|
+
* where `/observations?studyDbId` is not honored.
|
|
14
|
+
* 3. When units carry `germplasmDbId`s but no embedded observations, pull
|
|
15
|
+
* `/observations?germplasmDbId=…` per germplasm and filter back to the
|
|
16
|
+
* study — stays study-anchored without an unbounded scan.
|
|
17
|
+
*
|
|
18
|
+
* `pullStudyObservations` returns `null` only when no usable observation path
|
|
19
|
+
* exists for the study (caller throws its typed `no_observation_path` error);
|
|
20
|
+
* an empty array means the paths exist but yielded no rows.
|
|
21
|
+
*
|
|
22
|
+
* @module mcp-server/tools/shared/observations
|
|
23
|
+
*/
|
|
24
|
+
import { isDialectAllDropped } from '../../../services/brapi-client/index.js';
|
|
25
|
+
import { companionRequestOptions, extractRows, mergeFilters, } from './find-helpers.js';
|
|
26
|
+
/**
|
|
27
|
+
* Collect observations for a single study, trying `/observations` first and
|
|
28
|
+
* falling back to `/observationunits` (embedded, then germplasm-anchored).
|
|
29
|
+
* Returns `null` when no usable path exists; `[]` when paths exist but are
|
|
30
|
+
* empty.
|
|
31
|
+
*/
|
|
32
|
+
export async function pullStudyObservations(args) {
|
|
33
|
+
const { studyDbId, input, client, connection, dialect, config, loadLimit, warnings, ctx } = args;
|
|
34
|
+
// Build named filters for observations endpoint
|
|
35
|
+
const namedFilters = {
|
|
36
|
+
studyDbIds: [studyDbId],
|
|
37
|
+
};
|
|
38
|
+
if (input.variables?.length)
|
|
39
|
+
namedFilters.observationVariableDbIds = input.variables;
|
|
40
|
+
if (input.germplasm?.length)
|
|
41
|
+
namedFilters.germplasmDbIds = input.germplasm;
|
|
42
|
+
const merged = mergeFilters(namedFilters, input.extraFilters, warnings);
|
|
43
|
+
// Apply dialect (plurals → singulars on SGN-family servers)
|
|
44
|
+
const adapted = dialect.adaptGetFilters('observations', merged);
|
|
45
|
+
warnings.push(...adapted.warnings);
|
|
46
|
+
// When the dialect drops ALL filters for /observations (known case: the
|
|
47
|
+
// brapi-test server doesn't honor studyDbId on /observations at all), skip
|
|
48
|
+
// the observations path entirely and go straight to the observationunits
|
|
49
|
+
// fallback rather than making an unscoped request.
|
|
50
|
+
const obsFiltersDropped = adapted.dropped.length > 0 && Object.keys(adapted.filters).length === 0;
|
|
51
|
+
const obsFilters = adapted.filters;
|
|
52
|
+
// ---- Path 1: /observations ----
|
|
53
|
+
const supportsObservations = supportsGet(args.profile, 'observations') && !obsFiltersDropped;
|
|
54
|
+
if (supportsObservations) {
|
|
55
|
+
const obs = await fetchObservationsPath(client, connection, dialect, config, obsFilters, loadLimit, ctx);
|
|
56
|
+
if (obs !== null && obs.length > 0) {
|
|
57
|
+
return obs;
|
|
58
|
+
}
|
|
59
|
+
if (obs !== null && obs.length === 0) {
|
|
60
|
+
// Server responded with empty for this study — note it and try fallback
|
|
61
|
+
warnings.push(`Study '${studyDbId}': /observations returned 0 rows — trying /observationunits fallback.`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else if (obsFiltersDropped) {
|
|
65
|
+
warnings.push(`Study '${studyDbId}': dialect '${dialect.id}' drops all study filters for /observations — using /observationunits fallback.`);
|
|
66
|
+
}
|
|
67
|
+
// ---- Path 2: /observationunits fallback ----
|
|
68
|
+
// Two sub-strategies:
|
|
69
|
+
// 2a. Units embed observations[] arrays — extract directly.
|
|
70
|
+
// 2b. Units carry germplasmDbIds but observations[] is null — pull
|
|
71
|
+
// /observations?germplasmDbId=X per germplasm found in the study.
|
|
72
|
+
const supportsObsUnits = supportsGet(args.profile, 'observationunits');
|
|
73
|
+
if (supportsObsUnits) {
|
|
74
|
+
const unitResult = await fetchObservationUnitsRaw(client, connection, dialect, config, studyDbId, loadLimit, ctx);
|
|
75
|
+
if (unitResult !== null) {
|
|
76
|
+
// 2a: check for embedded observations
|
|
77
|
+
const embeddedObs = extractEmbeddedObservations(unitResult, studyDbId);
|
|
78
|
+
if (embeddedObs.length > 0) {
|
|
79
|
+
warnings.push(`Study '${studyDbId}': Using /observationunits embedded observations[] fallback.`);
|
|
80
|
+
return embeddedObs;
|
|
81
|
+
}
|
|
82
|
+
// 2b: germplasm-anchored pull — units have germplasmDbIds, use them
|
|
83
|
+
let germplasmIds = [
|
|
84
|
+
...new Set(unitResult
|
|
85
|
+
.map((u) => u.germplasmDbId)
|
|
86
|
+
.filter((id) => typeof id === 'string' && id.length > 0)),
|
|
87
|
+
];
|
|
88
|
+
// When the caller scoped to specific germplasm, pull only those — avoids
|
|
89
|
+
// an O(all-germplasm-in-study) fan-out for single-germplasm callers (#9).
|
|
90
|
+
if (input.germplasm?.length) {
|
|
91
|
+
const want = new Set(input.germplasm);
|
|
92
|
+
germplasmIds = germplasmIds.filter((g) => want.has(g));
|
|
93
|
+
}
|
|
94
|
+
if (germplasmIds.length > 0) {
|
|
95
|
+
const germObs = await fetchObservationsByGermplasm(client, connection, dialect, config, germplasmIds, studyDbId, loadLimit, ctx);
|
|
96
|
+
if (germObs !== null && germObs.length > 0) {
|
|
97
|
+
warnings.push(`Study '${studyDbId}': Used /observationunits → /observations?germplasmDbId fallback because studyDbId filter is not honored on /observations.`);
|
|
98
|
+
return germObs;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Neither path yielded data. Distinguish "no path" from "empty".
|
|
104
|
+
if (!supportsObservations && !obsFiltersDropped && !supportsObsUnits) {
|
|
105
|
+
return null; // caller throws typed error
|
|
106
|
+
}
|
|
107
|
+
// Paths exist but all returned empty — treat as empty (not an error)
|
|
108
|
+
warnings.push(`Study '${studyDbId}': No observations found via /observations or /observationunits.`);
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
function supportsGet(profile, service) {
|
|
112
|
+
const descriptor = profile[service];
|
|
113
|
+
if (!descriptor)
|
|
114
|
+
return false;
|
|
115
|
+
if (!descriptor.methods || descriptor.methods.length === 0)
|
|
116
|
+
return true;
|
|
117
|
+
return descriptor.methods.includes('GET');
|
|
118
|
+
}
|
|
119
|
+
/** Fetch from /observations with study-anchored filters. Returns null on error. */
|
|
120
|
+
async function fetchObservationsPath(client, connection, dialect, config, filters, pageSize, ctx) {
|
|
121
|
+
try {
|
|
122
|
+
const opts = companionRequestOptions(connection, dialect, config, [], {
|
|
123
|
+
...filters,
|
|
124
|
+
pageSize,
|
|
125
|
+
});
|
|
126
|
+
const envelope = await client.get(connection.baseUrl, '/observations', ctx, opts);
|
|
127
|
+
const rows = extractRows(envelope.result);
|
|
128
|
+
return rows.flatMap(normalizeObsRow).filter(Boolean);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
if (isDialectAllDropped(err))
|
|
132
|
+
throw err;
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/** Fetch raw observationunit records (without extracting observations). Returns null on error. */
|
|
137
|
+
async function fetchObservationUnitsRaw(client, connection, dialect, config, studyDbId, pageSize, ctx) {
|
|
138
|
+
try {
|
|
139
|
+
const rawFilters = { studyDbIds: [studyDbId] };
|
|
140
|
+
const adapted = dialect.adaptGetFilters('observationunits', rawFilters);
|
|
141
|
+
const obsUnitFilters = adapted.filters;
|
|
142
|
+
const opts = companionRequestOptions(connection, dialect, config, [], {
|
|
143
|
+
...obsUnitFilters,
|
|
144
|
+
pageSize,
|
|
145
|
+
});
|
|
146
|
+
const envelope = await client.get(connection.baseUrl, '/observationunits', ctx, opts);
|
|
147
|
+
return extractRows(envelope.result);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
if (isDialectAllDropped(err))
|
|
151
|
+
throw err;
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/** Extract embedded observations[] from observationunit records (strategy 2a). */
|
|
156
|
+
function extractEmbeddedObservations(units, studyDbId) {
|
|
157
|
+
const obs = [];
|
|
158
|
+
for (const unit of units) {
|
|
159
|
+
const unitStudyDbId = unit.studyDbId ?? studyDbId;
|
|
160
|
+
const unitGermplasmDbId = unit.germplasmDbId ?? 'unknown';
|
|
161
|
+
const unitGermplasmName = typeof unit.germplasmName === 'string' && unit.germplasmName.length > 0
|
|
162
|
+
? unit.germplasmName
|
|
163
|
+
: unitGermplasmDbId;
|
|
164
|
+
const embedded = unit.observations;
|
|
165
|
+
if (!Array.isArray(embedded))
|
|
166
|
+
continue;
|
|
167
|
+
for (const raw of embedded) {
|
|
168
|
+
if (!raw || typeof raw !== 'object')
|
|
169
|
+
continue;
|
|
170
|
+
const r = raw;
|
|
171
|
+
const varId = r.observationVariableDbId ?? null;
|
|
172
|
+
const val = r.value ?? null;
|
|
173
|
+
if (!varId || val === null || val === undefined)
|
|
174
|
+
continue;
|
|
175
|
+
const season = seasonLabel(r);
|
|
176
|
+
obs.push({
|
|
177
|
+
germplasmDbId: unitGermplasmDbId,
|
|
178
|
+
germplasmName: unitGermplasmName,
|
|
179
|
+
observationVariableDbId: String(varId),
|
|
180
|
+
observationVariableName: typeof r.observationVariableName === 'string' && r.observationVariableName.length > 0
|
|
181
|
+
? r.observationVariableName
|
|
182
|
+
: String(varId),
|
|
183
|
+
studyDbId: unitStudyDbId,
|
|
184
|
+
value: String(val),
|
|
185
|
+
...(season ? { season } : {}),
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return obs;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Pull observations anchored by germplasmDbId (strategy 2b). Used when
|
|
193
|
+
* studyDbId filter is not honored on /observations but we know germplasmDbIds
|
|
194
|
+
* in the study from the observationunits response. Filters results to only
|
|
195
|
+
* observations for the target study.
|
|
196
|
+
*/
|
|
197
|
+
async function fetchObservationsByGermplasm(client, connection, dialect, config, germplasmIds, studyDbId, pageSize, ctx) {
|
|
198
|
+
try {
|
|
199
|
+
// Pull per germplasm to stay study-anchored. Bound by pageSize per call.
|
|
200
|
+
// For wide queries this is O(germplasmCount) requests but avoids an
|
|
201
|
+
// unbounded full-table scan on the observations endpoint.
|
|
202
|
+
const allObs = [];
|
|
203
|
+
for (const germId of germplasmIds) {
|
|
204
|
+
const rawFilters = { germplasmDbIds: [germId] };
|
|
205
|
+
const adapted = dialect.adaptGetFilters('observations', rawFilters);
|
|
206
|
+
// Skip if the germplasm filter is also dropped
|
|
207
|
+
if (adapted.dropped.length > 0 && Object.keys(adapted.filters).length === 0)
|
|
208
|
+
continue;
|
|
209
|
+
const opts = companionRequestOptions(connection, dialect, config, [], {
|
|
210
|
+
...adapted.filters,
|
|
211
|
+
pageSize,
|
|
212
|
+
});
|
|
213
|
+
try {
|
|
214
|
+
const envelope = await client.get(connection.baseUrl, '/observations', ctx, opts);
|
|
215
|
+
const rows = extractRows(envelope.result);
|
|
216
|
+
// Filter to only observations for the target study
|
|
217
|
+
for (const row of rows) {
|
|
218
|
+
if (row.studyDbId !== studyDbId)
|
|
219
|
+
continue;
|
|
220
|
+
const norm = normalizeObsRow(row);
|
|
221
|
+
if (norm)
|
|
222
|
+
allObs.push(norm);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch { }
|
|
226
|
+
}
|
|
227
|
+
return allObs;
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
if (isDialectAllDropped(err))
|
|
231
|
+
throw err;
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/** Normalise a raw /observations row; drops rows missing any required field. */
|
|
236
|
+
export function normalizeObsRow(row) {
|
|
237
|
+
const varId = row.observationVariableDbId ?? null;
|
|
238
|
+
const germId = row.germplasmDbId ?? null;
|
|
239
|
+
const studyId = row.studyDbId ?? null;
|
|
240
|
+
const val = row.value ?? null;
|
|
241
|
+
if (!varId || !germId || !studyId || val === null || val === undefined)
|
|
242
|
+
return null;
|
|
243
|
+
const norm = {
|
|
244
|
+
germplasmDbId: String(germId),
|
|
245
|
+
germplasmName: typeof row.germplasmName === 'string' && row.germplasmName.length > 0
|
|
246
|
+
? row.germplasmName
|
|
247
|
+
: String(germId),
|
|
248
|
+
observationVariableDbId: String(varId),
|
|
249
|
+
observationVariableName: typeof row.observationVariableName === 'string' && row.observationVariableName.length > 0
|
|
250
|
+
? row.observationVariableName
|
|
251
|
+
: String(varId),
|
|
252
|
+
studyDbId: String(studyId),
|
|
253
|
+
value: String(val),
|
|
254
|
+
};
|
|
255
|
+
const season = seasonLabel(row);
|
|
256
|
+
if (season)
|
|
257
|
+
norm.season = season;
|
|
258
|
+
return norm;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Derive a human season label from a BrAPI observation row. Servers carry
|
|
262
|
+
* season either as a nested object (`{ seasonName, year, seasonDbId }`) or a
|
|
263
|
+
* flat `seasonDbId` string. Returns undefined when neither is present.
|
|
264
|
+
*/
|
|
265
|
+
export function seasonLabel(row) {
|
|
266
|
+
const s = row.season;
|
|
267
|
+
if (s && typeof s === 'object') {
|
|
268
|
+
const so = s;
|
|
269
|
+
const name = typeof so.seasonName === 'string' && so.seasonName.length > 0 ? so.seasonName : undefined;
|
|
270
|
+
const year = typeof so.year === 'number' ? so.year : undefined;
|
|
271
|
+
if (name && year !== undefined)
|
|
272
|
+
return `${name} ${year}`;
|
|
273
|
+
if (name)
|
|
274
|
+
return name;
|
|
275
|
+
if (typeof so.seasonDbId === 'string' && so.seasonDbId.length > 0)
|
|
276
|
+
return so.seasonDbId;
|
|
277
|
+
}
|
|
278
|
+
else if (typeof s === 'string' && s.length > 0) {
|
|
279
|
+
return s;
|
|
280
|
+
}
|
|
281
|
+
if (typeof row.seasonDbId === 'string' && row.seasonDbId.length > 0)
|
|
282
|
+
return row.seasonDbId;
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
//# sourceMappingURL=observations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observations.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/shared/observations.ts"],"names":[],"mappings":"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"}
|
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/brapi-mcp-server",
|
|
3
3
|
"mcpName": "io.github.cyanheads/brapi-mcp-server",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.0",
|
|
5
5
|
"description": "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.",
|
|
6
6
|
"author": "Casey Hand @cyanheads (https://github.com/cyanheads/brapi-mcp-server#readme)",
|
|
7
7
|
"type": "module",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
"clean": "bun run scripts/clean.ts",
|
|
27
27
|
"devcheck": "bun run scripts/devcheck.ts",
|
|
28
28
|
"tree": "bun run scripts/tree.ts",
|
|
29
|
-
"format": "biome check --write
|
|
29
|
+
"format": "biome check --write .",
|
|
30
|
+
"format:unsafe": "biome check --write --unsafe .",
|
|
30
31
|
"lint:mcp": "bun run scripts/lint-mcp.ts",
|
|
31
32
|
"lint:packaging": "bun run scripts/lint-packaging.ts",
|
|
32
33
|
"list-skills": "bun run scripts/list-skills.ts",
|
|
@@ -79,8 +80,8 @@
|
|
|
79
80
|
"access": "public"
|
|
80
81
|
},
|
|
81
82
|
"dependencies": {
|
|
82
|
-
"@cyanheads/mcp-ts-core": "^0.9.
|
|
83
|
-
"@duckdb/node-api": "^1.5.3-r.
|
|
83
|
+
"@cyanheads/mcp-ts-core": "^0.9.16",
|
|
84
|
+
"@duckdb/node-api": "^1.5.3-r.3",
|
|
84
85
|
"pino-pretty": "^13.1.3",
|
|
85
86
|
"zod": "^4.4.3"
|
|
86
87
|
},
|
|
@@ -91,6 +92,6 @@
|
|
|
91
92
|
"ignore": "^7.0.5",
|
|
92
93
|
"tsc-alias": "^1.8.17",
|
|
93
94
|
"typescript": "^6.0.3",
|
|
94
|
-
"vitest": "^4.1.
|
|
95
|
+
"vitest": "^4.1.8"
|
|
95
96
|
}
|
|
96
97
|
}
|
package/server.json
CHANGED
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
"url": "https://github.com/cyanheads/brapi-mcp-server",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.7.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
14
14
|
"identifier": "@cyanheads/brapi-mcp-server",
|
|
15
15
|
"runtimeHint": "bun",
|
|
16
|
-
"version": "0.
|
|
16
|
+
"version": "0.7.0",
|
|
17
17
|
"packageArguments": [
|
|
18
18
|
{ "type": "positional", "value": "run" },
|
|
19
19
|
{ "type": "positional", "value": "start:stdio" }
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
50
50
|
"identifier": "@cyanheads/brapi-mcp-server",
|
|
51
51
|
"runtimeHint": "bun",
|
|
52
|
-
"version": "0.
|
|
52
|
+
"version": "0.7.0",
|
|
53
53
|
"packageArguments": [
|
|
54
54
|
{ "type": "positional", "value": "run" },
|
|
55
55
|
{ "type": "positional", "value": "start:http" }
|