@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.
Files changed (106) hide show
  1. package/CLAUDE.md +11 -4
  2. package/README.md +7 -3
  3. package/changelog/0.6.x/0.6.4.md +14 -0
  4. package/changelog/0.7.x/0.7.0.md +23 -0
  5. package/changelog/template.md +1 -1
  6. package/dist/index.js +2 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/mcp-server/resources/definitions/brapi-variable.resource.d.ts +30 -0
  9. package/dist/mcp-server/resources/definitions/brapi-variable.resource.d.ts.map +1 -0
  10. package/dist/mcp-server/resources/definitions/brapi-variable.resource.js +78 -0
  11. package/dist/mcp-server/resources/definitions/brapi-variable.resource.js.map +1 -0
  12. package/dist/mcp-server/tools/definitions/brapi-build-phenotype-matrix.tool.d.ts +81 -0
  13. package/dist/mcp-server/tools/definitions/brapi-build-phenotype-matrix.tool.d.ts.map +1 -0
  14. package/dist/mcp-server/tools/definitions/brapi-build-phenotype-matrix.tool.js +366 -0
  15. package/dist/mcp-server/tools/definitions/brapi-build-phenotype-matrix.tool.js.map +1 -0
  16. package/dist/mcp-server/tools/definitions/brapi-connect.tool.d.ts +1 -1
  17. package/dist/mcp-server/tools/definitions/brapi-connect.tool.d.ts.map +1 -1
  18. package/dist/mcp-server/tools/definitions/brapi-dataframe-describe.tool.d.ts +1 -1
  19. package/dist/mcp-server/tools/definitions/brapi-dataframe-describe.tool.d.ts.map +1 -1
  20. package/dist/mcp-server/tools/definitions/brapi-dataframe-drop.tool.d.ts +1 -1
  21. package/dist/mcp-server/tools/definitions/brapi-dataframe-drop.tool.d.ts.map +1 -1
  22. package/dist/mcp-server/tools/definitions/brapi-dataframe-export.tool.d.ts +1 -1
  23. package/dist/mcp-server/tools/definitions/brapi-dataframe-export.tool.d.ts.map +1 -1
  24. package/dist/mcp-server/tools/definitions/brapi-dataframe-query.tool.d.ts +1 -1
  25. package/dist/mcp-server/tools/definitions/brapi-dataframe-query.tool.d.ts.map +1 -1
  26. package/dist/mcp-server/tools/definitions/brapi-describe-filters.tool.d.ts +1 -1
  27. package/dist/mcp-server/tools/definitions/brapi-describe-filters.tool.d.ts.map +1 -1
  28. package/dist/mcp-server/tools/definitions/brapi-export-genotype-matrix.tool.d.ts +88 -0
  29. package/dist/mcp-server/tools/definitions/brapi-export-genotype-matrix.tool.d.ts.map +1 -0
  30. package/dist/mcp-server/tools/definitions/brapi-export-genotype-matrix.tool.js +494 -0
  31. package/dist/mcp-server/tools/definitions/brapi-export-genotype-matrix.tool.js.map +1 -0
  32. package/dist/mcp-server/tools/definitions/brapi-find-genotype-calls.tool.d.ts +7 -5
  33. package/dist/mcp-server/tools/definitions/brapi-find-genotype-calls.tool.d.ts.map +1 -1
  34. package/dist/mcp-server/tools/definitions/brapi-find-genotype-calls.tool.js +67 -120
  35. package/dist/mcp-server/tools/definitions/brapi-find-genotype-calls.tool.js.map +1 -1
  36. package/dist/mcp-server/tools/definitions/brapi-find-germplasm.tool.d.ts +8 -6
  37. package/dist/mcp-server/tools/definitions/brapi-find-germplasm.tool.d.ts.map +1 -1
  38. package/dist/mcp-server/tools/definitions/brapi-find-germplasm.tool.js +54 -33
  39. package/dist/mcp-server/tools/definitions/brapi-find-germplasm.tool.js.map +1 -1
  40. package/dist/mcp-server/tools/definitions/brapi-find-images.tool.d.ts +8 -6
  41. package/dist/mcp-server/tools/definitions/brapi-find-images.tool.d.ts.map +1 -1
  42. package/dist/mcp-server/tools/definitions/brapi-find-images.tool.js +54 -33
  43. package/dist/mcp-server/tools/definitions/brapi-find-images.tool.js.map +1 -1
  44. package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.d.ts +8 -6
  45. package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.d.ts.map +1 -1
  46. package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.js +62 -41
  47. package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.js.map +1 -1
  48. package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.d.ts +8 -6
  49. package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.d.ts.map +1 -1
  50. package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.js +54 -33
  51. package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.js.map +1 -1
  52. package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.d.ts +8 -6
  53. package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.d.ts.map +1 -1
  54. package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.js +52 -33
  55. package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.js.map +1 -1
  56. package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.d.ts +8 -6
  57. package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.d.ts.map +1 -1
  58. package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.js +54 -33
  59. package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.js.map +1 -1
  60. package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.d.ts +8 -6
  61. package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.d.ts.map +1 -1
  62. package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.js +54 -33
  63. package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.js.map +1 -1
  64. package/dist/mcp-server/tools/definitions/brapi-germplasm-performance.tool.d.ts +60 -0
  65. package/dist/mcp-server/tools/definitions/brapi-germplasm-performance.tool.d.ts.map +1 -0
  66. package/dist/mcp-server/tools/definitions/brapi-germplasm-performance.tool.js +416 -0
  67. package/dist/mcp-server/tools/definitions/brapi-germplasm-performance.tool.js.map +1 -0
  68. package/dist/mcp-server/tools/definitions/brapi-get-germplasm.tool.d.ts +1 -1
  69. package/dist/mcp-server/tools/definitions/brapi-get-germplasm.tool.d.ts.map +1 -1
  70. package/dist/mcp-server/tools/definitions/brapi-get-image.tool.d.ts +1 -1
  71. package/dist/mcp-server/tools/definitions/brapi-get-image.tool.d.ts.map +1 -1
  72. package/dist/mcp-server/tools/definitions/brapi-get-study.tool.d.ts +1 -1
  73. package/dist/mcp-server/tools/definitions/brapi-get-study.tool.d.ts.map +1 -1
  74. package/dist/mcp-server/tools/definitions/brapi-raw-get.tool.d.ts +1 -1
  75. package/dist/mcp-server/tools/definitions/brapi-raw-get.tool.d.ts.map +1 -1
  76. package/dist/mcp-server/tools/definitions/brapi-raw-search.tool.d.ts +1 -1
  77. package/dist/mcp-server/tools/definitions/brapi-raw-search.tool.d.ts.map +1 -1
  78. package/dist/mcp-server/tools/definitions/brapi-server-info.tool.d.ts +1 -1
  79. package/dist/mcp-server/tools/definitions/brapi-server-info.tool.d.ts.map +1 -1
  80. package/dist/mcp-server/tools/definitions/brapi-submit-observations.tool.d.ts +1 -1
  81. package/dist/mcp-server/tools/definitions/brapi-submit-observations.tool.d.ts.map +1 -1
  82. package/dist/mcp-server/tools/definitions/brapi-walk-pedigree.tool.d.ts +1 -1
  83. package/dist/mcp-server/tools/definitions/brapi-walk-pedigree.tool.d.ts.map +1 -1
  84. package/dist/mcp-server/tools/definitions/index.d.ts +225 -61
  85. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
  86. package/dist/mcp-server/tools/definitions/index.js +6 -0
  87. package/dist/mcp-server/tools/definitions/index.js.map +1 -1
  88. package/dist/mcp-server/tools/shared/canvas-columns.d.ts +32 -0
  89. package/dist/mcp-server/tools/shared/canvas-columns.d.ts.map +1 -0
  90. package/dist/mcp-server/tools/shared/canvas-columns.js +98 -0
  91. package/dist/mcp-server/tools/shared/canvas-columns.js.map +1 -0
  92. package/dist/mcp-server/tools/shared/find-helpers.d.ts +1 -1
  93. package/dist/mcp-server/tools/shared/find-helpers.d.ts.map +1 -1
  94. package/dist/mcp-server/tools/shared/find-helpers.js +6 -2
  95. package/dist/mcp-server/tools/shared/find-helpers.js.map +1 -1
  96. package/dist/mcp-server/tools/shared/genotype-calls.d.ts +81 -0
  97. package/dist/mcp-server/tools/shared/genotype-calls.d.ts.map +1 -0
  98. package/dist/mcp-server/tools/shared/genotype-calls.js +172 -0
  99. package/dist/mcp-server/tools/shared/genotype-calls.js.map +1 -0
  100. package/dist/mcp-server/tools/shared/observations.d.ts +87 -0
  101. package/dist/mcp-server/tools/shared/observations.d.ts.map +1 -0
  102. package/dist/mcp-server/tools/shared/observations.js +285 -0
  103. package/dist/mcp-server/tools/shared/observations.js.map +1 -0
  104. package/manifest.json +1 -1
  105. package/package.json +6 -5
  106. 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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": "0.3",
3
3
  "name": "brapi-mcp-server",
4
- "version": "0.6.3",
4
+ "version": "0.7.0",
5
5
  "description": "Collaborative BrAPI v2.1 MCP workspace — studies, germplasm, genotypes across Breedbase, T3, more.",
6
6
  "author": {
7
7
  "name": "cyanheads"
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.6.3",
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 --unsafe .",
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.13",
83
- "@duckdb/node-api": "^1.5.3-r.2",
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.7"
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.6.3",
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.6.3",
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.6.3",
52
+ "version": "0.7.0",
53
53
  "packageArguments": [
54
54
  { "type": "positional", "value": "run" },
55
55
  { "type": "positional", "value": "start:http" }