@cyanheads/brapi-mcp-server 0.3.5

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 (238) hide show
  1. package/CLAUDE.md +391 -0
  2. package/Dockerfile +99 -0
  3. package/LICENSE +201 -0
  4. package/README.md +590 -0
  5. package/changelog/0.1.x/0.1.0.md +19 -0
  6. package/changelog/0.1.x/0.1.1.md +27 -0
  7. package/changelog/0.1.x/0.1.2.md +22 -0
  8. package/changelog/0.2.x/0.2.0.md +35 -0
  9. package/changelog/0.2.x/0.2.1.md +36 -0
  10. package/changelog/0.3.x/0.3.0.md +38 -0
  11. package/changelog/0.3.x/0.3.1.md +40 -0
  12. package/changelog/0.3.x/0.3.2.md +29 -0
  13. package/changelog/0.3.x/0.3.3.md +19 -0
  14. package/changelog/0.3.x/0.3.4.md +33 -0
  15. package/changelog/0.3.x/0.3.5.md +24 -0
  16. package/changelog/template.md +51 -0
  17. package/dist/config/alias-credentials.d.ts +82 -0
  18. package/dist/config/alias-credentials.d.ts.map +1 -0
  19. package/dist/config/alias-credentials.js +159 -0
  20. package/dist/config/alias-credentials.js.map +1 -0
  21. package/dist/config/server-config.d.ts +39 -0
  22. package/dist/config/server-config.d.ts.map +1 -0
  23. package/dist/config/server-config.js +128 -0
  24. package/dist/config/server-config.js.map +1 -0
  25. package/dist/index.d.ts +10 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +85 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/mcp-server/prompts/definitions/brapi-eda-study.prompt.d.ts +14 -0
  30. package/dist/mcp-server/prompts/definitions/brapi-eda-study.prompt.d.ts.map +1 -0
  31. package/dist/mcp-server/prompts/definitions/brapi-eda-study.prompt.js +75 -0
  32. package/dist/mcp-server/prompts/definitions/brapi-eda-study.prompt.js.map +1 -0
  33. package/dist/mcp-server/prompts/definitions/brapi-meta-analysis.prompt.d.ts +16 -0
  34. package/dist/mcp-server/prompts/definitions/brapi-meta-analysis.prompt.d.ts.map +1 -0
  35. package/dist/mcp-server/prompts/definitions/brapi-meta-analysis.prompt.js +109 -0
  36. package/dist/mcp-server/prompts/definitions/brapi-meta-analysis.prompt.js.map +1 -0
  37. package/dist/mcp-server/resources/definitions/brapi-calls.resource.d.ts +11 -0
  38. package/dist/mcp-server/resources/definitions/brapi-calls.resource.d.ts.map +1 -0
  39. package/dist/mcp-server/resources/definitions/brapi-calls.resource.js +46 -0
  40. package/dist/mcp-server/resources/definitions/brapi-calls.resource.js.map +1 -0
  41. package/dist/mcp-server/resources/definitions/brapi-dataset.resource.d.ts +13 -0
  42. package/dist/mcp-server/resources/definitions/brapi-dataset.resource.d.ts.map +1 -0
  43. package/dist/mcp-server/resources/definitions/brapi-dataset.resource.js +26 -0
  44. package/dist/mcp-server/resources/definitions/brapi-dataset.resource.js.map +1 -0
  45. package/dist/mcp-server/resources/definitions/brapi-filters.resource.d.ts +19 -0
  46. package/dist/mcp-server/resources/definitions/brapi-filters.resource.d.ts.map +1 -0
  47. package/dist/mcp-server/resources/definitions/brapi-filters.resource.js +45 -0
  48. package/dist/mcp-server/resources/definitions/brapi-filters.resource.js.map +1 -0
  49. package/dist/mcp-server/resources/definitions/brapi-germplasm.resource.d.ts +18 -0
  50. package/dist/mcp-server/resources/definitions/brapi-germplasm.resource.d.ts.map +1 -0
  51. package/dist/mcp-server/resources/definitions/brapi-germplasm.resource.js +34 -0
  52. package/dist/mcp-server/resources/definitions/brapi-germplasm.resource.js.map +1 -0
  53. package/dist/mcp-server/resources/definitions/brapi-server-info.resource.d.ts +11 -0
  54. package/dist/mcp-server/resources/definitions/brapi-server-info.resource.d.ts.map +1 -0
  55. package/dist/mcp-server/resources/definitions/brapi-server-info.resource.js +28 -0
  56. package/dist/mcp-server/resources/definitions/brapi-server-info.resource.js.map +1 -0
  57. package/dist/mcp-server/resources/definitions/brapi-study.resource.d.ts +18 -0
  58. package/dist/mcp-server/resources/definitions/brapi-study.resource.d.ts.map +1 -0
  59. package/dist/mcp-server/resources/definitions/brapi-study.resource.js +34 -0
  60. package/dist/mcp-server/resources/definitions/brapi-study.resource.js.map +1 -0
  61. package/dist/mcp-server/tools/definitions/brapi-connect.tool.d.ts +82 -0
  62. package/dist/mcp-server/tools/definitions/brapi-connect.tool.d.ts.map +1 -0
  63. package/dist/mcp-server/tools/definitions/brapi-connect.tool.js +106 -0
  64. package/dist/mcp-server/tools/definitions/brapi-connect.tool.js.map +1 -0
  65. package/dist/mcp-server/tools/definitions/brapi-describe-filters.tool.d.ts +41 -0
  66. package/dist/mcp-server/tools/definitions/brapi-describe-filters.tool.d.ts.map +1 -0
  67. package/dist/mcp-server/tools/definitions/brapi-describe-filters.tool.js +88 -0
  68. package/dist/mcp-server/tools/definitions/brapi-describe-filters.tool.js.map +1 -0
  69. package/dist/mcp-server/tools/definitions/brapi-find-genotype-calls.tool.d.ts +74 -0
  70. package/dist/mcp-server/tools/definitions/brapi-find-genotype-calls.tool.d.ts.map +1 -0
  71. package/dist/mcp-server/tools/definitions/brapi-find-genotype-calls.tool.js +386 -0
  72. package/dist/mcp-server/tools/definitions/brapi-find-genotype-calls.tool.js.map +1 -0
  73. package/dist/mcp-server/tools/definitions/brapi-find-germplasm.tool.d.ts +72 -0
  74. package/dist/mcp-server/tools/definitions/brapi-find-germplasm.tool.d.ts.map +1 -0
  75. package/dist/mcp-server/tools/definitions/brapi-find-germplasm.tool.js +290 -0
  76. package/dist/mcp-server/tools/definitions/brapi-find-germplasm.tool.js.map +1 -0
  77. package/dist/mcp-server/tools/definitions/brapi-find-images.tool.d.ts +65 -0
  78. package/dist/mcp-server/tools/definitions/brapi-find-images.tool.d.ts.map +1 -0
  79. package/dist/mcp-server/tools/definitions/brapi-find-images.tool.js +243 -0
  80. package/dist/mcp-server/tools/definitions/brapi-find-images.tool.js.map +1 -0
  81. package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.d.ts +63 -0
  82. package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.d.ts.map +1 -0
  83. package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.js +278 -0
  84. package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.js.map +1 -0
  85. package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.d.ts +74 -0
  86. package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.d.ts.map +1 -0
  87. package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.js +288 -0
  88. package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.js.map +1 -0
  89. package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.d.ts +69 -0
  90. package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.d.ts.map +1 -0
  91. package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.js +243 -0
  92. package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.js.map +1 -0
  93. package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.d.ts +86 -0
  94. package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.d.ts.map +1 -0
  95. package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.js +337 -0
  96. package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.js.map +1 -0
  97. package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.d.ts +59 -0
  98. package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.d.ts.map +1 -0
  99. package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.js +248 -0
  100. package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.js.map +1 -0
  101. package/dist/mcp-server/tools/definitions/brapi-get-germplasm.tool.d.ts +59 -0
  102. package/dist/mcp-server/tools/definitions/brapi-get-germplasm.tool.d.ts.map +1 -0
  103. package/dist/mcp-server/tools/definitions/brapi-get-germplasm.tool.js +306 -0
  104. package/dist/mcp-server/tools/definitions/brapi-get-germplasm.tool.js.map +1 -0
  105. package/dist/mcp-server/tools/definitions/brapi-get-image.tool.d.ts +57 -0
  106. package/dist/mcp-server/tools/definitions/brapi-get-image.tool.d.ts.map +1 -0
  107. package/dist/mcp-server/tools/definitions/brapi-get-image.tool.js +291 -0
  108. package/dist/mcp-server/tools/definitions/brapi-get-image.tool.js.map +1 -0
  109. package/dist/mcp-server/tools/definitions/brapi-get-study.tool.d.ts +75 -0
  110. package/dist/mcp-server/tools/definitions/brapi-get-study.tool.d.ts.map +1 -0
  111. package/dist/mcp-server/tools/definitions/brapi-get-study.tool.js +323 -0
  112. package/dist/mcp-server/tools/definitions/brapi-get-study.tool.js.map +1 -0
  113. package/dist/mcp-server/tools/definitions/brapi-manage-dataset.tool.d.ts +87 -0
  114. package/dist/mcp-server/tools/definitions/brapi-manage-dataset.tool.d.ts.map +1 -0
  115. package/dist/mcp-server/tools/definitions/brapi-manage-dataset.tool.js +296 -0
  116. package/dist/mcp-server/tools/definitions/brapi-manage-dataset.tool.js.map +1 -0
  117. package/dist/mcp-server/tools/definitions/brapi-raw-get.tool.d.ts +35 -0
  118. package/dist/mcp-server/tools/definitions/brapi-raw-get.tool.d.ts.map +1 -0
  119. package/dist/mcp-server/tools/definitions/brapi-raw-get.tool.js +148 -0
  120. package/dist/mcp-server/tools/definitions/brapi-raw-get.tool.js.map +1 -0
  121. package/dist/mcp-server/tools/definitions/brapi-raw-search.tool.d.ts +33 -0
  122. package/dist/mcp-server/tools/definitions/brapi-raw-search.tool.d.ts.map +1 -0
  123. package/dist/mcp-server/tools/definitions/brapi-raw-search.tool.js +126 -0
  124. package/dist/mcp-server/tools/definitions/brapi-raw-search.tool.js.map +1 -0
  125. package/dist/mcp-server/tools/definitions/brapi-server-info.tool.d.ts +51 -0
  126. package/dist/mcp-server/tools/definitions/brapi-server-info.tool.d.ts.map +1 -0
  127. package/dist/mcp-server/tools/definitions/brapi-server-info.tool.js +41 -0
  128. package/dist/mcp-server/tools/definitions/brapi-server-info.tool.js.map +1 -0
  129. package/dist/mcp-server/tools/definitions/brapi-submit-observations.tool.d.ts +117 -0
  130. package/dist/mcp-server/tools/definitions/brapi-submit-observations.tool.d.ts.map +1 -0
  131. package/dist/mcp-server/tools/definitions/brapi-submit-observations.tool.js +574 -0
  132. package/dist/mcp-server/tools/definitions/brapi-submit-observations.tool.js.map +1 -0
  133. package/dist/mcp-server/tools/definitions/brapi-walk-pedigree.tool.d.ts +52 -0
  134. package/dist/mcp-server/tools/definitions/brapi-walk-pedigree.tool.d.ts.map +1 -0
  135. package/dist/mcp-server/tools/definitions/brapi-walk-pedigree.tool.js +420 -0
  136. package/dist/mcp-server/tools/definitions/brapi-walk-pedigree.tool.js.map +1 -0
  137. package/dist/mcp-server/tools/shared/connect-auth-schema.d.ts +29 -0
  138. package/dist/mcp-server/tools/shared/connect-auth-schema.d.ts.map +1 -0
  139. package/dist/mcp-server/tools/shared/connect-auth-schema.js +55 -0
  140. package/dist/mcp-server/tools/shared/connect-auth-schema.js.map +1 -0
  141. package/dist/mcp-server/tools/shared/find-helpers.d.ts +143 -0
  142. package/dist/mcp-server/tools/shared/find-helpers.d.ts.map +1 -0
  143. package/dist/mcp-server/tools/shared/find-helpers.js +319 -0
  144. package/dist/mcp-server/tools/shared/find-helpers.js.map +1 -0
  145. package/dist/mcp-server/tools/shared/orientation-envelope.d.ts +97 -0
  146. package/dist/mcp-server/tools/shared/orientation-envelope.d.ts.map +1 -0
  147. package/dist/mcp-server/tools/shared/orientation-envelope.js +254 -0
  148. package/dist/mcp-server/tools/shared/orientation-envelope.js.map +1 -0
  149. package/dist/mcp-server/tools/shared/raw-routing-hints.d.ts +10 -0
  150. package/dist/mcp-server/tools/shared/raw-routing-hints.d.ts.map +1 -0
  151. package/dist/mcp-server/tools/shared/raw-routing-hints.js +46 -0
  152. package/dist/mcp-server/tools/shared/raw-routing-hints.js.map +1 -0
  153. package/dist/services/brapi-client/brapi-client.d.ts +76 -0
  154. package/dist/services/brapi-client/brapi-client.d.ts.map +1 -0
  155. package/dist/services/brapi-client/brapi-client.js +320 -0
  156. package/dist/services/brapi-client/brapi-client.js.map +1 -0
  157. package/dist/services/brapi-client/index.d.ts +9 -0
  158. package/dist/services/brapi-client/index.d.ts.map +1 -0
  159. package/dist/services/brapi-client/index.js +7 -0
  160. package/dist/services/brapi-client/index.js.map +1 -0
  161. package/dist/services/brapi-client/types.d.ts +82 -0
  162. package/dist/services/brapi-client/types.d.ts.map +1 -0
  163. package/dist/services/brapi-client/types.js +8 -0
  164. package/dist/services/brapi-client/types.js.map +1 -0
  165. package/dist/services/brapi-filters/catalog.d.ts +14 -0
  166. package/dist/services/brapi-filters/catalog.d.ts.map +1 -0
  167. package/dist/services/brapi-filters/catalog.js +490 -0
  168. package/dist/services/brapi-filters/catalog.js.map +1 -0
  169. package/dist/services/brapi-filters/index.d.ts +8 -0
  170. package/dist/services/brapi-filters/index.d.ts.map +1 -0
  171. package/dist/services/brapi-filters/index.js +7 -0
  172. package/dist/services/brapi-filters/index.js.map +1 -0
  173. package/dist/services/brapi-filters/types.d.ts +23 -0
  174. package/dist/services/brapi-filters/types.d.ts.map +1 -0
  175. package/dist/services/brapi-filters/types.js +9 -0
  176. package/dist/services/brapi-filters/types.js.map +1 -0
  177. package/dist/services/capability-registry/capability-registry.d.ts +51 -0
  178. package/dist/services/capability-registry/capability-registry.d.ts.map +1 -0
  179. package/dist/services/capability-registry/capability-registry.js +234 -0
  180. package/dist/services/capability-registry/capability-registry.js.map +1 -0
  181. package/dist/services/capability-registry/index.d.ts +9 -0
  182. package/dist/services/capability-registry/index.d.ts.map +1 -0
  183. package/dist/services/capability-registry/index.js +7 -0
  184. package/dist/services/capability-registry/index.js.map +1 -0
  185. package/dist/services/capability-registry/types.d.ts +67 -0
  186. package/dist/services/capability-registry/types.d.ts.map +1 -0
  187. package/dist/services/capability-registry/types.js +9 -0
  188. package/dist/services/capability-registry/types.js.map +1 -0
  189. package/dist/services/dataset-store/dataset-store.d.ts +35 -0
  190. package/dist/services/dataset-store/dataset-store.d.ts.map +1 -0
  191. package/dist/services/dataset-store/dataset-store.js +190 -0
  192. package/dist/services/dataset-store/dataset-store.js.map +1 -0
  193. package/dist/services/dataset-store/index.d.ts +8 -0
  194. package/dist/services/dataset-store/index.d.ts.map +1 -0
  195. package/dist/services/dataset-store/index.js +7 -0
  196. package/dist/services/dataset-store/index.js.map +1 -0
  197. package/dist/services/dataset-store/types.d.ts +65 -0
  198. package/dist/services/dataset-store/types.d.ts.map +1 -0
  199. package/dist/services/dataset-store/types.js +8 -0
  200. package/dist/services/dataset-store/types.js.map +1 -0
  201. package/dist/services/ontology-resolver/index.d.ts +8 -0
  202. package/dist/services/ontology-resolver/index.d.ts.map +1 -0
  203. package/dist/services/ontology-resolver/index.js +7 -0
  204. package/dist/services/ontology-resolver/index.js.map +1 -0
  205. package/dist/services/ontology-resolver/ontology-resolver.d.ts +49 -0
  206. package/dist/services/ontology-resolver/ontology-resolver.d.ts.map +1 -0
  207. package/dist/services/ontology-resolver/ontology-resolver.js +99 -0
  208. package/dist/services/ontology-resolver/ontology-resolver.js.map +1 -0
  209. package/dist/services/ontology-resolver/types.d.ts +38 -0
  210. package/dist/services/ontology-resolver/types.d.ts.map +1 -0
  211. package/dist/services/ontology-resolver/types.js +8 -0
  212. package/dist/services/ontology-resolver/types.js.map +1 -0
  213. package/dist/services/reference-data-cache/index.d.ts +9 -0
  214. package/dist/services/reference-data-cache/index.d.ts.map +1 -0
  215. package/dist/services/reference-data-cache/index.js +7 -0
  216. package/dist/services/reference-data-cache/index.js.map +1 -0
  217. package/dist/services/reference-data-cache/reference-data-cache.d.ts +31 -0
  218. package/dist/services/reference-data-cache/reference-data-cache.d.ts.map +1 -0
  219. package/dist/services/reference-data-cache/reference-data-cache.js +131 -0
  220. package/dist/services/reference-data-cache/reference-data-cache.js.map +1 -0
  221. package/dist/services/reference-data-cache/types.d.ts +42 -0
  222. package/dist/services/reference-data-cache/types.d.ts.map +1 -0
  223. package/dist/services/reference-data-cache/types.js +9 -0
  224. package/dist/services/reference-data-cache/types.js.map +1 -0
  225. package/dist/services/server-registry/index.d.ts +9 -0
  226. package/dist/services/server-registry/index.d.ts.map +1 -0
  227. package/dist/services/server-registry/index.js +7 -0
  228. package/dist/services/server-registry/index.js.map +1 -0
  229. package/dist/services/server-registry/server-registry.d.ts +57 -0
  230. package/dist/services/server-registry/server-registry.d.ts.map +1 -0
  231. package/dist/services/server-registry/server-registry.js +210 -0
  232. package/dist/services/server-registry/server-registry.js.map +1 -0
  233. package/dist/services/server-registry/types.d.ts +43 -0
  234. package/dist/services/server-registry/types.d.ts.map +1 -0
  235. package/dist/services/server-registry/types.js +10 -0
  236. package/dist/services/server-registry/types.js.map +1 -0
  237. package/package.json +86 -0
  238. package/server.json +99 -0
@@ -0,0 +1,35 @@
1
+ ---
2
+ summary: "Read-side surface expansion — 11 new tools (variables, observations, images, genotype calls, locations, variants, pedigree walks, dataset lifecycle, raw passthrough) plus OntologyResolver and binary image fetch."
3
+ breaking: false
4
+ ---
5
+
6
+ # 0.2.0 — 2026-04-23
7
+
8
+ Fills out the read-side BrAPI surface. The orient + studies/germplasm trio from 0.1.1 is joined by variables, observations, images, locations, variants, genotype calls, pedigree DAG walks, a dataset lifecycle tool, and raw-endpoint escape hatches. Tool count: 7 → 18.
9
+
10
+ ## Added
11
+
12
+ - **Pedigree traversal** — `brapi_walk_pedigree` BFS-walks germplasm ancestry or descendancy as a deduplicated DAG. BrAPI only exposes one generation per call; this tool handles cycle detection, depth limits (default 3, cap 10), and a 1000-node safety cap. Up to 20 roots per call, each walked concurrently. Returns nodes + edges plus traversal stats (`depthReached`, `leafCount`, `cycleCount`, `deadEndCount`, `truncated`).
13
+ - **Observation variables** — `brapi_find_variables` with free-text ranking via the new `OntologyResolver` service (`src/services/ontology-resolver/`). Scores variable records against a query across PUI / name / synonym / trait-class / trait description and surfaces the top 10 candidates alongside the full result set. Falls back to substring matching when the server doesn't expose `/ontologies`.
14
+ - **Observations** — `brapi_find_observations` with filters for study, germplasm, variable, season, observation unit, observation level, timestamp range, program, and trial. Distributions across variable / study / germplasm / level / season; dataset spillover on the standard `find_*` pattern.
15
+ - **Images** — `brapi_find_images` (metadata only, distributions across MIME type / study / observation unit / ontology terms) and `brapi_get_image` (fetch bytes inline as `type: image` content blocks, up to 5 images per call, 20 MB per image). `get_image` prefers `/images/{id}/imagecontent` and falls back to the `imageURL` metadata field; relative URLs resolve against the registered base URL, absolute URLs pass through without auth.
16
+ - **Locations** — `brapi_find_locations` with filters for country / type / abbreviation / ID and an optional client-side bounding-box filter (BrAPI has no spec-level bbox). All four corners required to activate; mismatched ranges produce a warning and the filter is skipped.
17
+ - **Variants** — `brapi_find_variants` with genomic-region filters (1-based inclusive `start` / exclusive `end` per the BrAPI spec). Distributions across `variantType`, `referenceName`, `variantSetDbId`.
18
+ - **Genotype calls** — `brapi_find_genotype_calls` handles the async-search pattern (`POST /search/calls` → `GET /search/calls/{id}` with 202-retry) transparently. Default cap of 100k calls per call, hard cap of 500k. Rows beyond `loadLimit` spill to `DatasetStore` for export. Server-reported genotype-encoding hints (`expandHomozygotes`, `unknownString`, `sepPhased`, `sepUnphased`) are echoed so the agent can interpret the values. Requires at least one filter — unfiltered pulls are rejected at the input boundary.
19
+ - **Dataset lifecycle** — `brapi_manage_dataset` consolidates list / summary / load / delete operations against `DatasetStore`. `mode: load` supports paged rows (up to 1000 per page) and optional column projection. Export (CSV / Parquet) remains deferred until a storage write path lands.
20
+ - **Raw passthroughs** — `brapi_raw_get` (any BrAPI GET) and `brapi_raw_search` (any BrAPI `POST /search/{noun}` with async polling) for niche endpoints the curated tools don't cover. Both emit a `suggestion` field routing to the goal-shaped tool when one exists — e.g. `raw_get /studies` nudges to `brapi_find_studies`. `raw_get` blocks absolute URLs in `path` to prevent cross-origin smuggling via the registered base URL.
21
+ - **`OntologyResolver` service** (`src/services/ontology-resolver/`) — stateless scorer ranking `VariableLike` records against a query. PUI exact match (100) > name exact (90) > name substring (60) > variable synonym (50) > trait synonym (40) > trait name (35) > trait class exact (30) > trait description (20). Swappable backend — an embedding-backed implementation can replace this one without changing the tool surface.
22
+ - **Binary fetch** on `BrapiClient` — new `getBinary()` honors the same retry policy as `get()` and threads the private-IP guard; `fetchBinaryUrl()` pulls arbitrary URLs (e.g. CDN-backed `imageURL`) without attaching auth. Both return a `BinaryResponse` (`{ bytes, contentType }`).
23
+ - **Shared routing hints** (`src/mcp-server/tools/shared/raw-routing-hints.ts`) — centralized map from path segment / search noun to the curated-tool suggestion, powering `raw_get` / `raw_search`.
24
+
25
+ ## Changed
26
+
27
+ - **`find-helpers.ts`** — extracted shared utilities previously duplicated between `brapi_find_studies` and `brapi_find_germplasm`:
28
+ - `DatasetHandleSchema` + `toDatasetHandle()` — the in-context dataset handle shape (drops provenance fields available via `brapi_manage_dataset summary`).
29
+ - `maybeSpill()` — wraps `spillToDataset()` with the "only spill when `hasMore` and `totalCount > loadLimit`" guard. All nine `find_*` tools use it.
30
+ - `asString()`, `asStringArray()`, `buildRefinementHint()` — scalar / array coercion and the refinement-hint heuristic (pick the highest-cardinality non-empty distribution).
31
+ - `brapi_find_studies` and `brapi_find_germplasm` slimmed down to use the extracted helpers; the two files dropped ~100 lines of duplication.
32
+ - **`src/index.ts`** — registers 11 new tools and calls `initOntologyResolver()` in `setup()`.
33
+ - **`README.md`** — tool count 7 → 18, tool tables regrouped by axis (orient / retrieve / orchestrate / escape hatches), per-tool sections added for every new tool, BrAPI-specific feature bullets expanded (async-search transparency, pedigree DAG walks, image content, free-text variable ranking, last-resort escape hatches), project-structure table gains `ontology-resolver`.
34
+ - **`CLAUDE.md`** — structure section updated with all 18 tool files and the `ontology-resolver` service.
35
+ - **`docs/tree.md`** — regenerated to reflect the new files.
@@ -0,0 +1,36 @@
1
+ ---
2
+ summary: "Framework bump to @cyanheads/mcp-ts-core 0.6.16 — recursive describe-on-fields linting, synced skills, and .describe() coverage on every nested field, array element, and union variant across the 18-tool surface."
3
+ breaking: false
4
+ ---
5
+
6
+ # 0.2.1 — 2026-04-24
7
+
8
+ Housekeeping release. Tracks the `@cyanheads/mcp-ts-core` 0.6.16 linter upgrade, syncs the external skills bundle, and resolves every new `describe-on-fields` warning surfaced by the recursive schema walk. No runtime behavior changes; no new tools.
9
+
10
+ ## Changed
11
+
12
+ - **`@cyanheads/mcp-ts-core`** bumped to `^0.6.16`. Notable upstream changes the server adopts:
13
+ - `describe-on-fields` linter now recurses into nested objects, array element types, and union variants (previously top-level only). Diagnostic path syntax: `.key`, `[]`, `|<i>`.
14
+ - Resource `output` schemas now lint through `checkFieldDescriptions` (no-op here — no resources yet).
15
+ - `maintenance` skill gains Phase C (content-hash sync of framework scripts into `scripts/`).
16
+ - `add-tool` + `design-mcp-server` skills add "seed orientation context" guidance (the `brapi_connect` envelope already implements this pattern).
17
+ - `setup` + `polish-docs-meta` skills add guardrails against editing synced SKILL.md files directly.
18
+ - **Synced skills** (`skills/add-tool`, `skills/design-mcp-server`, `skills/maintenance`, `skills/polish-docs-meta`, `skills/setup`) — overwritten by content since the package did not bump the embedded `metadata.version` fields. `.claude/skills/` mirror refreshed via Phase B.
19
+ - **Schema `.describe()` coverage** — resolved every new warning (360 → 0) across the 18-tool surface:
20
+ - Row schemas for every `find_*` tool carry a container describe plus field describes — Study, Germplasm, Observation, Image, Location, Variant, GenotypeCall, Variable rows.
21
+ - Nested sub-objects in variables (trait / scale / method), genotype calls (`genotype.values` + `callFormatting`), images, synonyms, and related records.
22
+ - `brapi_manage_dataset` discriminated-union variants (ListResult / SummaryResult / LoadResult / DeleteResult) each carry their own describe.
23
+ - `DatasetHandleSchema` fields in `find-helpers.ts`.
24
+ - `ConnectAuthSchema` union variants (none / bearer / api_key / sgn / oauth2); oauth2 gains `clientId` / `clientSecret` / `tokenUrl` describes.
25
+ - `OrientationEnvelope` array element describes (supported services, notable gaps, crops, notes).
26
+ - `brapi_find_locations.bbox.{minLat,maxLat,minLon,maxLon}` describes.
27
+ - `brapi_raw_get` / `brapi_raw_search` `PaginationSchema` field describes.
28
+ - `brapi_get_germplasm` Parent / Attribute and `brapi_get_image` Payload / Error container describes.
29
+ - Distribution maps (`z.record`) on every `find_*` tool describe what the count represents.
30
+ - `brapi_describe_filters` `FilterDescriptorSchema` container describe.
31
+ - Biome auto-formatted 7 files during the lint pass (line wrapping); no semantic changes.
32
+
33
+ ## Verified
34
+
35
+ - `bun run lint:mcp` — `All definitions valid.` (was 360 warnings pre-bump)
36
+ - `bun run test` — 101/101 pass
@@ -0,0 +1,38 @@
1
+ ---
2
+ summary: "Surface complete — adds brapi_submit_observations write tool, six brapi:// resources, two workflow prompts (eda_study, meta_analysis), POST/PUT methods on BrapiClient, and full test backfill (101 → 191 tests)."
3
+ breaking: false
4
+ ---
5
+
6
+ # 0.3.0 — 2026-04-25
7
+
8
+ Closes the MVP design surface from `docs/design.md`. The read side (18 tools, 6 services) shipped in 0.1.x / 0.2.x; this release adds the only write tool, the additive resource layer for URI-addressable clients, two workflow prompts that package the canonical multi-step BrAPI flows, and backfills tests for every untested handler.
9
+
10
+ ## Added
11
+
12
+ - **`brapi_submit_observations` tool** — additive write surface for observation rows. Two-phase: `mode: preview` (default) validates rows against the study's `observationvariables` and reports a `routing` breakdown (POST for new, PUT for rows carrying `observationDbId`); `mode: apply` elicits user confirmation via `ctx.elicit`, fans the rows out to POST + PUT in parallel, then re-probes `studies/{id}/observations?pageSize=0` to return the post-write total. Apply requires `ctx.elicit` support OR an explicit `force: true` flag (rejected with `Forbidden` otherwise). Per-row warnings flag unknown variables, empty values, and missing FK fields. Auth scope: `brapi:write:observations`.
13
+ - **Six MCP resources** under the `brapi://` scheme — additive layer for URI-addressable clients. Each mirrors an existing tool's payload through the default connection:
14
+ - `brapi://server/info` — orientation envelope (mirror of `brapi_server_info`).
15
+ - `brapi://calls` — raw capability profile from `CapabilityRegistry`.
16
+ - `brapi://study/{studyDbId}` — single study with FKs resolved (mirror of `brapi_get_study`).
17
+ - `brapi://germplasm/{germplasmDbId}` — single germplasm with attributes + parents (mirror of `brapi_get_germplasm`).
18
+ - `brapi://dataset/{datasetId}` — dataset metadata + provenance (rows via `brapi_manage_dataset`).
19
+ - `brapi://filters/{endpoint}` — static filter catalog (mirror of `brapi_describe_filters`).
20
+ - **Two MCP prompts** — multi-step workflow templates that package the canonical BrAPI flows into ready-to-fire user messages:
21
+ - `brapi_eda_study(studyDbId, alias?)` — exploratory-data-analysis playbook for one study (orient → variables → coverage → missing → outliers → pedigree → report).
22
+ - `brapi_meta_analysis(germplasmDbIds, traitName, alias?)` — cross-study trait meta-analysis (resolve trait → discover studies → harmonize → per-germplasm × per-study summary → across-study summary → pedigree side-quest → report).
23
+ - **`BrapiClient.post()` / `BrapiClient.put()`** — generic JSON request helpers shared by the new write tool. Same retry policy, HTTP error reclassification, and SSRF guard as `get()`.
24
+
25
+ ## Changed
26
+
27
+ - **`brapi_raw_get` cross-origin guard** — `rejectCrossOrigin()` now runs against the raw `input.path` before `normalizePath` adds the leading `/`. Previous order let `https://evil/leak` slip past the regex (the leading slash blocked the `/^https?:\/\//` match) and instead concatenate into the registered baseUrl path, where the URL constructor escaped it benignly. Defense-in-depth restored.
28
+ - **README** — version badge bumped, surface counters updated to `19 Tools · 6 Resources · 2 Prompts`, new sections for resources and prompts, write-tool entry in the tools table, project structure expanded with `mcp-server/resources/` and `mcp-server/prompts/`.
29
+
30
+ ## Fixed
31
+
32
+ - **State key validation** — switched every service prefix from colon-separated to slash-separated (`brapi:conn:` → `brapi/conn/`, `brapi:capability:` → `brapi/capability/`, `brapi:ds:meta:` / `brapi:ds:rows:` → `brapi/ds/meta/` / `brapi/ds/rows/`, `brapi:ref:…` → `brapi/ref/…`). `@cyanheads/mcp-ts-core` 0.7.x storage rejects keys outside `/^[a-zA-Z0-9_.\-/]+$/`, which broke every state-using tool at runtime in HTTP mode (`ServerRegistry.register`, `CapabilityRegistry`, `DatasetStore`, `ReferenceDataCache`). Unit tests passed because `createMockContext` skips key validation; field-testing the HTTP transport surfaced the regression. Also updated the matching state-key assertions in `tests/tools/brapi-connect.tool.test.ts`.
33
+
34
+ ## Verified
35
+
36
+ - `bun run devcheck` — all 11 sub-checks pass (lint, format, typecheck, MCP definitions, framework antipatterns, docs/skills/changelog sync, secrets, deps audit, outdated).
37
+ - `bun run test` — 191/191 pass across 32 test files (+90 new tests covering the 12 previously untested tools, 6 resources, and 2 prompts).
38
+ - `bun run lint:mcp` — `All definitions valid.` (19 tools, 6 resources, 2 prompts).
@@ -0,0 +1,40 @@
1
+ ---
2
+ summary: "Per-alias env-var fallback for brapi_connect — credentials live in BRAPI_<ALIAS>_* and never enter the LLM context. Plus a Breedbase /token form-encoding fix that unblocks SGN auth against Cassavabase et al."
3
+ breaking: false
4
+ ---
5
+
6
+ # 0.3.1 — 2026-04-29
7
+
8
+ Connect-time credentials no longer have to ride through agent input. `brapi_connect` now fills `baseUrl` and `auth` from `BRAPI_<ALIAS>_*` env vars (then `BRAPI_DEFAULT_*`) when the agent omits them, so the LLM can call `brapi_connect({ alias: 'cassava' })` with nothing else and the server resolves the rest from `.env`. This is meaningful for security: passwords stop being prompt-engineered into every session.
9
+
10
+ Also fixes a real bug — the SGN/Breedbase token-exchange request was sending JSON, but Breedbase's `/token` follows the OAuth2 spec and requires `application/x-www-form-urlencoded`. SGN auth now works against Cassavabase, Sweetpotatobase, Yambase, Musabase, Solgenomics, etc.
11
+
12
+ ## Added
13
+
14
+ - **Env-var fallback for `brapi_connect`** — `baseUrl` and `auth` are now optional inputs. When omitted, the handler resolves them via the new `resolveConnectInput` helper with three layers of precedence:
15
+ 1. Explicit agent input (always wins).
16
+ 2. Per-alias env vars: `BRAPI_<ALIAS>_*` where the alias name is uppercased and hyphens become underscores (`my-server` → `BRAPI_MY_SERVER_*`).
17
+ 3. Default env vars: `BRAPI_DEFAULT_*` — only consulted when alias differs from `default`.
18
+ - **Per-alias credential families.** Each alias's env vars can carry one credential family — the auth mode is derived from which fields are set:
19
+ - `_USERNAME` + `_PASSWORD` → `mode: 'sgn'` (Breedbase token exchange)
20
+ - `_BEARER_TOKEN` → `mode: 'bearer'`
21
+ - `_API_KEY` (+ optional `_API_KEY_HEADER`) → `mode: 'api_key'`
22
+ - `_OAUTH_CLIENT_ID` + `_OAUTH_CLIENT_SECRET` (+ optional `_OAUTH_TOKEN_URL`) → `mode: 'oauth2'`
23
+ - None → `mode: 'none'`
24
+
25
+ Setting multiple families within a single alias raises a `ValidationError` naming the conflict.
26
+ - **`src/config/alias-credentials.ts`** module exposes `aliasEnvPrefix`, `readAliasCredentials`, `deriveAuthFromCredentials`, and `resolveConnectInput` — all unit-tested with 28 cases covering the resolution matrix.
27
+ - **`.env.example`** documents the per-alias pattern with examples for `default`, `cassava`, `sweetpotato`, `t3wheat`, `gigwa`, plus an API-key example (`prod`) and a bearer-token example (`cgiar`).
28
+
29
+ ## Changed
30
+
31
+ - **`brapi_connect.input` schema** — `baseUrl` and `auth` are now `optional()` rather than required / defaulted. Tool description and field descriptions updated to point at the env-var fallback. Existing callers that pass both fields explicitly continue to work unchanged.
32
+ - **`brapi_connect.handler`** logs the auth source (`'agent'` vs `'env'`) alongside the existing alias / baseUrl / authMode fields.
33
+
34
+ ## Fixed
35
+
36
+ - **SGN token exchange against Breedbase servers.** `defaultTokenFetcher` now POSTs `application/x-www-form-urlencoded` with `URLSearchParams`-encoded credentials instead of JSON. Breedbase's `/token` endpoint follows the OAuth2 password-grant spec and rejects JSON bodies (`HTTP 400 "JSON array body required"`); the JSON form would silently fail against every SGN-family server. Verified end-to-end against Cassavabase (`access_token` issued, `/serverinfo` returns 118 supported calls).
37
+
38
+ ## Removed
39
+
40
+ - **`dev:stdio` and `dev:http` package.json scripts.** Both wrapped `tsx --watch` — a long-running foreground process that's incompatible with agent-driven validation (an agent invoking it either blocks indefinitely or backgrounds the process and mistakes silence for success). Finite verification paths remain: `bun run test`, `bun run devcheck`, and JSON-RPC against a one-shot built binary via the `field-test` skill. Humans who want hot-reload can run `tsx --watch src/index.ts` directly.
@@ -0,0 +1,29 @@
1
+ ---
2
+ summary: "Adopt mcp-ts-core 0.8.6 typed-error contracts across the BrAPI surface, fix a germplasmOrigin schema bug that broke output validation against spec-compliant servers."
3
+ breaking: false
4
+ ---
5
+
6
+ # 0.3.2 — 2026-04-29
7
+
8
+ Tools and resources now declare typed `errors: [...]` contracts and throw via `ctx.fail(reason, …)` instead of `notFound()`/`validationError()` factories. Each declared failure carries a stable `data.reason`, an auto-mirrored `recovery.hint` on the wire, and a `code` that matches its contract entry — so agent clients can route errors deterministically. Verified end-to-end against `test-server.brapi.org`. Also ships a real bug fix: `germplasmOrigin` was schema-typed as a string, but BrAPI v2 spec defines it as an array of collection-site objects, so `find_germplasm` and `get_germplasm` were throwing on every call against a compliant server.
9
+
10
+ ## Added
11
+
12
+ - **Typed error contracts on tools & resources.** New `errors: [{ reason, code, when, recovery }]` blocks declared on:
13
+ - Tools: `brapi_connect` (auth_token_exchange_failed, auth_no_access_token), `brapi_describe_filters` (unknown_endpoint), `brapi_find_genotype_calls` (no_filters), `brapi_get_germplasm` (germplasm_not_found), `brapi_get_image` (images_unsupported), `brapi_get_study` (study_not_found), `brapi_manage_dataset` (datasetid_required, dataset_not_found, dataset_rows_missing), `brapi_raw_get` (cross_origin_path), `brapi_submit_observations` (observations_unsupported, post_unsupported, put_unsupported, elicit_unavailable, user_declined).
14
+ - Resources: `brapi://filters/{endpoint}` (unknown_endpoint), `brapi://germplasm/{germplasmDbId}` (germplasm_not_found), `brapi://study/{studyDbId}` (study_not_found).
15
+ - **`bun run start` script** — defers transport selection to `MCP_TRANSPORT_TYPE` (stdio default) so consumers can wire one entry point and switch transports via env.
16
+
17
+ ## Changed
18
+
19
+ - **Bumped `@cyanheads/mcp-ts-core` from `^0.7.4` → `^0.8.6`** to pick up the typed-`HandlerContext<R>` API, `ctx.fail` / `ctx.recoveryFor`, and the `error-contract-conformance` lint.
20
+ - **Tool & resource handlers** swapped ad-hoc `notFound()` / `validationError()` / `forbidden()` throws for `ctx.fail('reason', msg, { ...ctx.recoveryFor('reason') })` against their declared contracts. Recovery hints now ride on `data.recovery.hint` and are auto-mirrored into `content[].text`.
21
+ - **`brapi_submit_observations` confirmApply** typed against `HandlerContext<reason>` via a new `SubmitCtx` alias so all five declared failure modes are catchable at compile time.
22
+ - **Service-layer errors** (`brapi-client`, `dataset-store`, `server-registry`) now stamp a stable `reason` field into `error.data` for upstream-classified failures (`upstream_not_found`, `upstream_unauthorized`, `upstream_rate_limited`, `auth_no_access_token`, `dataset_rows_missing`, `unknown_alias`, etc.) — observable even though they're thrown outside a contract.
23
+ - **Skills synced to framework 0.8.6** — `add-tool`, `add-resource`, `add-service`, `api-context`, `api-errors`, `api-linter`, `api-testing`, `field-test`, and others updated with the typed-contract guidance and current API surface.
24
+ - **Project `CLAUDE.md`** updated with the typed-contract pattern and the `bun run start` script.
25
+
26
+ ## Fixed
27
+
28
+ - **`germplasmOrigin` schema** in `brapi_find_germplasm` and `brapi_get_germplasm` was declared as `z.string()`, but BrAPI v2 spec defines the field as an array of `GermplasmOrigin` objects (collection coordinates + uncertainty). Output validation was failing on every response from a spec-compliant server — confirmed end-to-end against `test-server.brapi.org`. Schema is now `z.array(z.object({}).passthrough())` and the format renderers report `germplasmOrigin=<count> record(s)` instead of stringifying objects.
29
+ - **`brapi_raw_get` cross-origin guard** is now thrown via `ctx.fail('cross_origin_path', …)` rather than the bare `validationError` factory so the recovery hint surfaces on the wire.
@@ -0,0 +1,19 @@
1
+ ---
2
+ summary: "Tighten recovery hints across BrAPI tool/resource error contracts and wire service-layer throws so declared hints actually reach the wire."
3
+ breaking: false
4
+ ---
5
+
6
+ # 0.3.3 — 2026-04-29
7
+
8
+ Audit pass on every declared `recovery` field added in 0.3.2. Six hints were either restating the failure (`brapi_get_image`, `brapi_submit_observations` `observations_unsupported`, two `unknown_endpoint` variants) or actively misleading (`brapi_submit_observations` `post_unsupported` had inverted POST/PUT routing logic; `put_unsupported` suggested dropping `observationDbId` without flagging the duplicate hazard). Four more were declared on tool contracts but thrown from service code that never spread `ctx.recoveryFor`, so the hints were documentation-only — `auth_token_exchange_failed`, `auth_no_access_token`, `dataset_not_found`, `dataset_rows_missing` now carry their hints to clients.
9
+
10
+ ## Changed
11
+
12
+ - **`brapi_submit_observations` recovery hints** — `observations_unsupported`, `post_unsupported`, `put_unsupported` rewritten to point at `brapi_server_info` for capability discovery, and to warn that dropping `observationDbId` to bypass `put_unsupported` would create duplicates rather than update existing rows.
13
+ - **`brapi_get_image` `images_unsupported`** rewritten from a restate-the-problem hint to a concrete recovery: run `brapi_server_info` or reconnect under a different alias.
14
+ - **`brapi_describe_filters` and `brapi://filters/{endpoint}` `unknown_endpoint`** hints now name the supported endpoints inline and reference `data.availableEndpoints` instead of the opaque "previous response" / "list field" wording.
15
+
16
+ ## Fixed
17
+
18
+ - **Service-layer throws now spread `ctx.recoveryFor(reason)`** so contract hints surface on the wire. `dataset-store.ts` (`dataset_not_found`, `dataset_rows_missing`) and `server-registry.ts` (`auth_token_exchange_failed`, `auth_no_access_token`) previously declared hints on the parent tool's contract but never wired them through their `notFound()` / `forbidden()` factory throws — clients saw `data.reason` but no `data.recovery.hint`.
19
+ - **`brapi_submit_observations` `post_unsupported` hint** corrected: the prior text said "remove observationDbId so all rows route to PUT" but removing the ID actually routes a row to POST (the failing path). Now correctly advises dropping the new rows or switching servers.
@@ -0,0 +1,33 @@
1
+ ---
2
+ summary: "Field-test pass against real BrAPI servers — capability-gated companion lookups, top-level study FK probes, structured-season + multi-set variant tolerance, and a multi-tenant HTTP deployment notice."
3
+ breaking: false
4
+ ---
5
+
6
+ # 0.3.4 — 2026-04-29
7
+
8
+ A robustness pass shaped by exercising every tool against live BrAPI servers (Breedbase, T3, Sweetpotatobase, and the test server). The headline change is that companion lookups in `brapi_get_germplasm` and `brapi_get_study` now consult the cached capability profile before firing — saving a 404 round-trip per unsupported endpoint. Several `find_*` tools learned to surface upstream sparsity (free-text filters that aren't honored, bbox windows that exclude all rows, named filters that return zero calls) instead of silently returning empty results.
9
+
10
+ ## Added
11
+
12
+ - **`brapi_connect` multi-tenant deployment notice** — when a connection authenticates over HTTP without per-client auth (`MCP_TRANSPORT_TYPE=http` and `MCP_AUTH_MODE=none|unset`), the tool now emits a `notice`-level log warning that the resolved bearer token persists under the shared `default` tenant and is reachable to any other connected client. README gains a "Multi-user HTTP deployments" section documenting tenant resolution and the cross-user state caveat.
13
+ - **Typed `study_not_found` / `germplasm_not_found` errors** — `brapi_get_study`, `brapi_get_germplasm`, and `brapi_submit_observations` now translate upstream 404s into a typed contract entry with a routing recovery hint, replacing the generic `serviceUnavailable` bubble-up.
14
+ - **`brapi_walk_pedigree` root-existence pre-flight** — when `germplasm/{germplasmDbId}/pedigree` is supported, each requested root is probed before the BFS so missing roots surface as warnings up front. Handles servers (e.g. `test-server.brapi.org`) that reply 200 + `result: null` for unknown germplasm rather than 404.
15
+ - **Dataset handle truncation flags** — `DatasetHandle` now carries `truncated` and `maxRows` fields when the producing tool capped rows before exhausting upstream. `brapi_find_genotype_calls` and the shared `spillToDataset` helper plumb these through so consumers can detect partial datasets.
16
+
17
+ ## Changed
18
+
19
+ - **`brapi_get_study` / `brapi_get_germplasm` companion lookups gate on capability profile** — pedigree, attribute, progeny, observation, observation-unit, and variable count probes now skip when the cached `/serverinfo` profile doesn't advertise the corresponding service. This avoids the per-request 404 storm against servers that only expose a subset of the spec.
20
+ - **Study FK count probes use top-level endpoints** — `brapi_get_study` and `brapi_submit_observations` now call `/observations?studyDbIds=…`, `/observationunits?studyDbIds=…`, and `/variables?studyDbId=…` (BrAPI v2.1 canonical routes) instead of `/studies/{id}/observations`, `/studies/{id}/observationunits`, `/studies/{id}/observationvariables`. `pageSize=0` was replaced with `pageSize=1` so servers that reject zero-page sizes still return a `totalCount`.
21
+ - **`brapi_find_observations` accepts structured `season` payloads** — output schema and distribution rendering now handle the BrAPI v2.1 `{seasonDbId, year, season, seasonName}` form alongside the legacy flat-string variant.
22
+ - **`brapi_find_variants` tolerates multi-set membership** — `variantSetDbId` accepts string OR string[] OR a separate `variantSetDbIds` plural form per spec drift across servers; `variantType`, `referenceBases`, `alternateBases`, `referenceName`, `start`, `end`, and the filter fields are now `nullish()` to accept upstream nulls without dropping rows.
23
+ - **`brapi_get_image` requires GET on `images/{imageDbId}/imagecontent`** — many servers expose the endpoint for PUT-only upload; the tool now only routes to `/imagecontent` when the capability profile lists GET, otherwise falls back to the metadata `imageURL`.
24
+ - **`brapi_submit_observations` study existence check** — `fetchStudyName` is now `fetchStudyExistence`, throwing `study_not_found` for upstream 404s and propagating transient errors (timeouts, 5xx) rather than swallowing them into a missing study name.
25
+ - **Tool descriptions tightened** — `brapi_connect`, `brapi_find_genotype_calls`, `brapi_find_locations`, `brapi_find_variables`, `brapi_get_image`, `brapi_raw_get`, and `brapi_submit_observations` rewrote their headline lines to drop redundant or prescriptive copy.
26
+
27
+ ## Fixed
28
+
29
+ - **`brapi_find_germplasm` free-text heuristic warning** — when the requested `text` filter would have narrowed the result and the server returned every row that matches the other filters, the tool now warns that the free-text filter may not be honored. Catches servers that silently ignore unrecognized query parameters.
30
+ - **`brapi_find_locations` bbox warnings** — surfaces a warning when a bbox window excludes every upstream row (likely a coordinate-convention mismatch) or some-but-not-all rows, instead of returning a quietly-empty list.
31
+ - **`brapi_find_genotype_calls` empty-result warning** — when a named filter (variant set, germplasm, callset, variant) was provided and upstream returned zero calls, surfaces a warning that the filter combination may not match data on this server.
32
+ - **`renderDatasetHandle` consolidation** — six `find_*` tools that duplicated the dataset-handle render block now share one helper, so `truncated` / `maxRows` lines surface consistently.
33
+ - **Test fixtures aligned with new routing** — `brapi_get_study`, `brapi_get_germplasm`, and `brapi_submit_observations` test mocks now advertise the capabilities the production code consults and respond on the top-level routes the tools actually call.
@@ -0,0 +1,24 @@
1
+ ---
2
+ summary: "Cassavabase null-tolerance pass — schemas accept upstream nulls without dropping rows, walk-pedigree no longer false-flags inverse-edge backtracks as cycles, get-image flags broken upstream URLs. Plus npm scope rename to @cyanheads/brapi-mcp-server."
3
+ breaking: false
4
+ ---
5
+
6
+ # 0.3.5 — 2026-04-30
7
+
8
+ A robustness pass driven by exercising the read tools against Cassavabase, which returns explicit `null` on most optional fields rather than omitting them. The headline change is that every read-tool schema now uses `nullish()` for nullable fields so sparse upstream payloads validate cleanly. Two real bugs surfaced along the way — `brapi_walk_pedigree` was double-counting inverse-edge backtracks as cycles in `direction='both'`, and `brapi_get_image` had no signal when the `imageURL` fallback returned an HTML error page instead of bytes.
9
+
10
+ ## Added
11
+
12
+ - **`brapi_get_image` per-image suspect-content warnings** — when the `imageURL` fallback returns content with a non-`image/*` MIME type (typically an HTML error page from a broken CDN URL), the tool now emits a per-image `warnings[]` entry advising that the bytes were loaded but may not render. The payload is still returned so the caller can inspect it; the warning just flags that something is wrong upstream.
13
+ - **`brapi_connect` configured-alias hint in tool description** — the connect tool now scans `BRAPI_<ALIAS>_BASE_URL` env vars at module load and appends a sentence listing the pre-configured aliases to its description, so agents see the alias inventory on `tools/list` without anyone enumerating them in the prompt. The phrasing emphasizes that any other BrAPI v2 server is still reachable by passing `baseUrl` directly. Restart the server to refresh.
14
+ - **`brapi_manage_dataset` truncation column** — list-mode table now includes a `truncated` column (`yes (cap=N)` or `no`); summary mode adds a `truncated: true (cap=N rows)` line when applicable. Surfaces the producer-side cap state through both `structuredContent` and `content[]`.
15
+
16
+ ## Changed
17
+
18
+ - **Optional schema fields use `nullish()` instead of `optional()`** — `brapi_find_germplasm`, `brapi_get_germplasm`, `brapi_find_studies`, `brapi_get_study`, `brapi_find_locations`, `brapi_find_variables` now accept upstream `null` on every optional field. Cassavabase serializes `subtaxa: null`, `studyCode: null`, nested `trait.traitClass: null`, etc. instead of omitting the keys; `nullish()` is `optional + nullable` so both shapes validate. Format functions correctly skip null-valued fields rather than rendering `lat=null` or `active=null`.
19
+ - **Package renamed to `@cyanheads/brapi-mcp-server`** — scoped under the `@cyanheads` npm org. README install instructions, package.json, and server.json identifiers all updated. Existing tag history retained.
20
+ - **Framework bump to `@cyanheads/mcp-ts-core ^0.8.7`** — picks up the latest framework patch.
21
+
22
+ ## Fixed
23
+
24
+ - **`brapi_walk_pedigree` no longer double-counts inverse-edge backtracks as cycles in `direction='both'`** — when walking ancestors and descendants concurrently, BFS naturally re-encounters nodes via the inverse relationship as the two expansion fronts meet (e.g. expanding `g-3`'s parents then re-finding `g-3` via the parent's progeny lookup). Previous logic counted these structural symmetries as cycles, inflating `cycleCount` for every multi-root walk. The walk now checks for the inverse edge before incrementing and only counts genuine re-discoveries via a non-symmetric path.
@@ -0,0 +1,51 @@
1
+ ---
2
+ # FORMAT REFERENCE — this file is never edited, never moved, never renamed.
3
+ #
4
+ # At release time, author a new per-version file at:
5
+ # changelog/<major.minor>.x/<version>.md (e.g. changelog/0.1.x/0.1.0.md)
6
+ # using this file's frontmatter and section layout as the starting point.
7
+ # Set that new file's H1 to `# <version> — YYYY-MM-DD` with a concrete date.
8
+
9
+ # Required. One-line headline describing the release. Max 250 chars. No markdown.
10
+ # This line is what the CHANGELOG.md rollup shows — write it like a GitHub Release title.
11
+ summary: ""
12
+
13
+ # Set to `true` only if this release has breaking changes (API removals, signature
14
+ # changes, config renames, anything that requires consumer code changes on update).
15
+ # Flagged as ⚠️ Breaking in the rollup.
16
+ breaking: false
17
+ ---
18
+
19
+ # <version> — YYYY-MM-DD
20
+
21
+ <!--
22
+ FORMAT REFERENCE — do not edit this file. It exists so the per-version file
23
+ structure has a single copy-source. Create new release notes at
24
+ `changelog/<major.minor>.x/<version>.md` using this layout.
25
+
26
+ Optional narrative intro — 1-3 sentences framing the release theme. Delete if not needed.
27
+
28
+ Linking issues/PRs: use full URLs so the link works everywhere (GitHub web UI,
29
+ npm/node_modules reads, local editors). GitHub's bare `#NN` auto-link only
30
+ resolves inside its own UI.
31
+
32
+ [#12](https://github.com/<owner>/<repo>/issues/12) ← issue
33
+ [#17](https://github.com/<owner>/<repo>/pull/17) ← PR
34
+
35
+ Only link numbers you've verified exist (via `gh issue view NN` or
36
+ `gh pr view NN`). Never speculate on a future number — `#17` for "my
37
+ upcoming PR" will quietly resolve to whatever real item already owns 17,
38
+ and GitHub timeline previews will pull in that unrelated item's title.
39
+ -->
40
+
41
+ ## Added
42
+
43
+ -
44
+
45
+ ## Changed
46
+
47
+ -
48
+
49
+ ## Fixed
50
+
51
+ -
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @fileoverview Per-alias env-var resolution for `brapi_connect`.
3
+ *
4
+ * Aliases map to env-var prefixes via `BRAPI_<ALIAS>_*` (uppercased, hyphens →
5
+ * underscores). Each alias can carry baseUrl + one credential family; the
6
+ * connect handler layers agent input over alias env over default env over the
7
+ * no-auth fallback. Credentials live in env, never in the LLM context.
8
+ *
9
+ * @module config/alias-credentials
10
+ */
11
+ import type { AuthMode, ConnectAuth } from '../services/server-registry/index.js';
12
+ /**
13
+ * Per-alias credential bundle read from env vars. All fields optional —
14
+ * presence is what determines which auth mode is derived.
15
+ */
16
+ export interface AliasCredentials {
17
+ apiKey?: string;
18
+ apiKeyHeader?: string;
19
+ baseUrl?: string;
20
+ bearerToken?: string;
21
+ oauthClientId?: string;
22
+ oauthClientSecret?: string;
23
+ oauthTokenUrl?: string;
24
+ password?: string;
25
+ username?: string;
26
+ }
27
+ /** Compute the env-var prefix for an alias. `my-server` → `BRAPI_MY_SERVER_`. */
28
+ export declare function aliasEnvPrefix(alias: string): string;
29
+ /** Read all `BRAPI_<ALIAS>_*` vars for an alias. Empty strings treated as unset. */
30
+ export declare function readAliasCredentials(alias: string, env?: NodeJS.ProcessEnv): AliasCredentials;
31
+ /**
32
+ * Pick a `ConnectAuth` from the credential bundle. Returns `undefined` when no
33
+ * credentials are present so callers can fall through to the next layer.
34
+ * Throws on intra-alias ambiguity (multiple credential families set).
35
+ */
36
+ export declare function deriveAuthFromCredentials(creds: AliasCredentials, alias: string): ConnectAuth | undefined;
37
+ export interface ResolvedConnectInput {
38
+ auth: ConnectAuth;
39
+ baseUrl: string;
40
+ }
41
+ /**
42
+ * Layer agent input over alias env over default env. Returns the resolved
43
+ * baseUrl + auth. Throws when no baseUrl is resolvable from any layer.
44
+ *
45
+ * Precedence:
46
+ * 1. Explicit agent input (`baseUrl`, `auth`) wins.
47
+ * 2. Per-alias env vars (`BRAPI_<ALIAS>_*`).
48
+ * 3. Default env vars (`BRAPI_DEFAULT_*`) — only when alias differs.
49
+ * 4. `auth` falls through to `{ mode: 'none' }`; `baseUrl` has no fallback.
50
+ */
51
+ export declare function resolveConnectInput(alias: string, agent: {
52
+ baseUrl?: string | undefined;
53
+ auth?: ConnectAuth | undefined;
54
+ }, env?: NodeJS.ProcessEnv): ResolvedConnectInput;
55
+ /**
56
+ * Summary of an alias discovered from env vars — what the operator pre-wired
57
+ * for this deployment. Surfaced to the LLM via the connect tool description so
58
+ * agents can pick a shortcut without the human having to enumerate them.
59
+ */
60
+ export interface DiscoveredAlias {
61
+ alias: string;
62
+ authMode: AuthMode;
63
+ baseUrl: string;
64
+ }
65
+ /**
66
+ * Scan env for `BRAPI_<X>_BASE_URL` keys and derive the alias inventory the
67
+ * operator has pre-configured. Each alias resolves its credential family via
68
+ * the same logic `resolveConnectInput` uses; ambiguous families fall back to
69
+ * `none` so the alias is still surfaced (the connect call will then raise the
70
+ * same `ValidationError` the agent would have hit anyway).
71
+ *
72
+ * Default-alias entries land first; the rest are alphabetical.
73
+ */
74
+ export declare function discoverConfiguredAliases(env?: NodeJS.ProcessEnv): DiscoveredAlias[];
75
+ /**
76
+ * Render the discovered alias list as a sentence appended to the connect
77
+ * tool's description. Empty when nothing is pre-configured — the original
78
+ * description stands alone in that case. Phrased so that absent aliases are
79
+ * never read as restricted servers: any BrAPI v2 URL stays connectable.
80
+ */
81
+ export declare function formatConfiguredAliasesHint(aliases: DiscoveredAlias[]): string;
82
+ //# sourceMappingURL=alias-credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alias-credentials.d.ts","sourceRoot":"","sources":["../../src/config/alias-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AAEjF;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAgBD,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,oFAAoF;AACpF,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,gBAAgB,CAQlB;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,MAAM,GACZ,WAAW,GAAG,SAAS,CAwCzB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;CAAE,EACvE,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,oBAAoB,CAsBtB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAID;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,eAAe,EAAE,CAuBjG;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,MAAM,CAI9E"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * @fileoverview Per-alias env-var resolution for `brapi_connect`.
3
+ *
4
+ * Aliases map to env-var prefixes via `BRAPI_<ALIAS>_*` (uppercased, hyphens →
5
+ * underscores). Each alias can carry baseUrl + one credential family; the
6
+ * connect handler layers agent input over alias env over default env over the
7
+ * no-auth fallback. Credentials live in env, never in the LLM context.
8
+ *
9
+ * @module config/alias-credentials
10
+ */
11
+ import { validationError } from '@cyanheads/mcp-ts-core/errors';
12
+ const FIELD_SUFFIXES = [
13
+ ['baseUrl', 'BASE_URL'],
14
+ ['username', 'USERNAME'],
15
+ ['password', 'PASSWORD'],
16
+ ['apiKey', 'API_KEY'],
17
+ ['apiKeyHeader', 'API_KEY_HEADER'],
18
+ ['bearerToken', 'BEARER_TOKEN'],
19
+ ['oauthClientId', 'OAUTH_CLIENT_ID'],
20
+ ['oauthClientSecret', 'OAUTH_CLIENT_SECRET'],
21
+ ['oauthTokenUrl', 'OAUTH_TOKEN_URL'],
22
+ ];
23
+ const DEFAULT_ALIAS = 'default';
24
+ /** Compute the env-var prefix for an alias. `my-server` → `BRAPI_MY_SERVER_`. */
25
+ export function aliasEnvPrefix(alias) {
26
+ return `BRAPI_${alias.replace(/-/g, '_').toUpperCase()}_`;
27
+ }
28
+ /** Read all `BRAPI_<ALIAS>_*` vars for an alias. Empty strings treated as unset. */
29
+ export function readAliasCredentials(alias, env = process.env) {
30
+ const prefix = aliasEnvPrefix(alias);
31
+ const result = {};
32
+ for (const [field, suffix] of FIELD_SUFFIXES) {
33
+ const value = env[`${prefix}${suffix}`];
34
+ if (value !== undefined && value !== '')
35
+ result[field] = value;
36
+ }
37
+ return result;
38
+ }
39
+ /**
40
+ * Pick a `ConnectAuth` from the credential bundle. Returns `undefined` when no
41
+ * credentials are present so callers can fall through to the next layer.
42
+ * Throws on intra-alias ambiguity (multiple credential families set).
43
+ */
44
+ export function deriveAuthFromCredentials(creds, alias) {
45
+ const families = [
46
+ { name: 'sgn (username+password)', present: !!(creds.username && creds.password) },
47
+ { name: 'bearer (bearerToken)', present: !!creds.bearerToken },
48
+ { name: 'api_key (apiKey)', present: !!creds.apiKey },
49
+ {
50
+ name: 'oauth2 (oauthClientId+oauthClientSecret)',
51
+ present: !!(creds.oauthClientId && creds.oauthClientSecret),
52
+ },
53
+ ];
54
+ const present = families.filter((f) => f.present).map((f) => f.name);
55
+ if (present.length === 0)
56
+ return;
57
+ if (present.length > 1) {
58
+ throw validationError(`Ambiguous auth config for alias '${alias}': multiple credential families set (${present.join(', ')}). Pick one — clear the env vars for the others.`, { alias, present });
59
+ }
60
+ if (creds.username && creds.password) {
61
+ return { mode: 'sgn', username: creds.username, password: creds.password };
62
+ }
63
+ if (creds.bearerToken) {
64
+ return { mode: 'bearer', token: creds.bearerToken };
65
+ }
66
+ if (creds.apiKey) {
67
+ return creds.apiKeyHeader
68
+ ? { mode: 'api_key', apiKey: creds.apiKey, headerName: creds.apiKeyHeader }
69
+ : { mode: 'api_key', apiKey: creds.apiKey };
70
+ }
71
+ if (creds.oauthClientId && creds.oauthClientSecret) {
72
+ return creds.oauthTokenUrl
73
+ ? {
74
+ mode: 'oauth2',
75
+ clientId: creds.oauthClientId,
76
+ clientSecret: creds.oauthClientSecret,
77
+ tokenUrl: creds.oauthTokenUrl,
78
+ }
79
+ : { mode: 'oauth2', clientId: creds.oauthClientId, clientSecret: creds.oauthClientSecret };
80
+ }
81
+ return;
82
+ }
83
+ /**
84
+ * Layer agent input over alias env over default env. Returns the resolved
85
+ * baseUrl + auth. Throws when no baseUrl is resolvable from any layer.
86
+ *
87
+ * Precedence:
88
+ * 1. Explicit agent input (`baseUrl`, `auth`) wins.
89
+ * 2. Per-alias env vars (`BRAPI_<ALIAS>_*`).
90
+ * 3. Default env vars (`BRAPI_DEFAULT_*`) — only when alias differs.
91
+ * 4. `auth` falls through to `{ mode: 'none' }`; `baseUrl` has no fallback.
92
+ */
93
+ export function resolveConnectInput(alias, agent, env = process.env) {
94
+ const aliasCreds = readAliasCredentials(alias, env);
95
+ const defaultCreds = alias === DEFAULT_ALIAS ? aliasCreds : readAliasCredentials(DEFAULT_ALIAS, env);
96
+ const baseUrl = agent.baseUrl ?? aliasCreds.baseUrl ?? defaultCreds.baseUrl;
97
+ if (!baseUrl) {
98
+ throw validationError(`No baseUrl provided. Pass \`baseUrl\` explicitly, or set ${aliasEnvPrefix(alias)}BASE_URL${alias === DEFAULT_ALIAS ? '' : ` or ${aliasEnvPrefix(DEFAULT_ALIAS)}BASE_URL`}.`, { alias });
99
+ }
100
+ const auth = agent.auth ??
101
+ deriveAuthFromCredentials(aliasCreds, alias) ??
102
+ (alias === DEFAULT_ALIAS
103
+ ? undefined
104
+ : deriveAuthFromCredentials(defaultCreds, DEFAULT_ALIAS)) ??
105
+ { mode: 'none' };
106
+ return { baseUrl, auth };
107
+ }
108
+ const ALIAS_BASE_URL_PATTERN = /^BRAPI_([A-Z0-9_]+)_BASE_URL$/;
109
+ /**
110
+ * Scan env for `BRAPI_<X>_BASE_URL` keys and derive the alias inventory the
111
+ * operator has pre-configured. Each alias resolves its credential family via
112
+ * the same logic `resolveConnectInput` uses; ambiguous families fall back to
113
+ * `none` so the alias is still surfaced (the connect call will then raise the
114
+ * same `ValidationError` the agent would have hit anyway).
115
+ *
116
+ * Default-alias entries land first; the rest are alphabetical.
117
+ */
118
+ export function discoverConfiguredAliases(env = process.env) {
119
+ const result = [];
120
+ for (const [key, value] of Object.entries(env)) {
121
+ const match = key.match(ALIAS_BASE_URL_PATTERN);
122
+ const captured = match?.[1];
123
+ if (!captured || !value)
124
+ continue;
125
+ const alias = captured.toLowerCase();
126
+ const creds = readAliasCredentials(alias, env);
127
+ let authMode = 'none';
128
+ try {
129
+ const auth = deriveAuthFromCredentials(creds, alias);
130
+ if (auth)
131
+ authMode = auth.mode;
132
+ }
133
+ catch {
134
+ // Ambiguous credential family — surface the alias anyway.
135
+ }
136
+ result.push({ alias, authMode, baseUrl: value });
137
+ }
138
+ result.sort((a, b) => {
139
+ if (a.alias === DEFAULT_ALIAS)
140
+ return -1;
141
+ if (b.alias === DEFAULT_ALIAS)
142
+ return 1;
143
+ return a.alias.localeCompare(b.alias);
144
+ });
145
+ return result;
146
+ }
147
+ /**
148
+ * Render the discovered alias list as a sentence appended to the connect
149
+ * tool's description. Empty when nothing is pre-configured — the original
150
+ * description stands alone in that case. Phrased so that absent aliases are
151
+ * never read as restricted servers: any BrAPI v2 URL stays connectable.
152
+ */
153
+ export function formatConfiguredAliasesHint(aliases) {
154
+ if (aliases.length === 0)
155
+ return '';
156
+ const names = aliases.map((a) => `\`${a.alias}\``).join(', ');
157
+ return `Pre-configured aliases on this deployment (callable without \`baseUrl\` or \`auth\` — credentials are read from server env vars): ${names}. Aliases are shortcuts only; any other BrAPI v2 server is reachable by passing \`baseUrl\` directly.`;
158
+ }
159
+ //# sourceMappingURL=alias-credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alias-credentials.js","sourceRoot":"","sources":["../../src/config/alias-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAmBhE,MAAM,cAAc,GAA6D;IAC/E,CAAC,SAAS,EAAE,UAAU,CAAC;IACvB,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,QAAQ,EAAE,SAAS,CAAC;IACrB,CAAC,cAAc,EAAE,gBAAgB,CAAC;IAClC,CAAC,aAAa,EAAE,cAAc,CAAC;IAC/B,CAAC,eAAe,EAAE,iBAAiB,CAAC;IACpC,CAAC,mBAAmB,EAAE,qBAAqB,CAAC;IAC5C,CAAC,eAAe,EAAE,iBAAiB,CAAC;CACrC,CAAC;AAEF,MAAM,aAAa,GAAG,SAAS,CAAC;AAEhC,iFAAiF;AACjF,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,OAAO,SAAS,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC;AAC5D,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,oBAAoB,CAClC,KAAa,EACb,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,MAAM,GAAqB,EAAE,CAAC;IACpC,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,cAAc,EAAE,CAAC;QAC7C,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;QACxC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;YAAE,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;IACjE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,KAAuB,EACvB,KAAa;IAEb,MAAM,QAAQ,GAA8C;QAC1D,EAAE,IAAI,EAAE,yBAAyB,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE;QAClF,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE;QAC9D,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE;QACrD;YACE,IAAI,EAAE,0CAA0C;YAChD,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,iBAAiB,CAAC;SAC5D;KACF,CAAC;IACF,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACrE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACjC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,eAAe,CACnB,oCAAoC,KAAK,wCAAwC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,kDAAkD,EACrJ,EAAE,KAAK,EAAE,OAAO,EAAE,CACnB,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACrC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC7E,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;QACtB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;IACtD,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC,YAAY;YACvB,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,YAAY,EAAE;YAC3E,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;IAChD,CAAC;IACD,IAAI,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;QACnD,OAAO,KAAK,CAAC,aAAa;YACxB,CAAC,CAAC;gBACE,IAAI,EAAE,QAAQ;gBACd,QAAQ,EAAE,KAAK,CAAC,aAAa;gBAC7B,YAAY,EAAE,KAAK,CAAC,iBAAiB;gBACrC,QAAQ,EAAE,KAAK,CAAC,aAAa;aAC9B;YACH,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,aAAa,EAAE,YAAY,EAAE,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC/F,CAAC;IACD,OAAO;AACT,CAAC;AAOD;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAa,EACb,KAAuE,EACvE,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,UAAU,GAAG,oBAAoB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACpD,MAAM,YAAY,GAChB,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,oBAAoB,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IAElF,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,UAAU,CAAC,OAAO,IAAI,YAAY,CAAC,OAAO,CAAC;IAC5E,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,eAAe,CACnB,4DAA4D,cAAc,CAAC,KAAK,CAAC,WAAW,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,cAAc,CAAC,aAAa,CAAC,UAAU,GAAG,EAC5K,EAAE,KAAK,EAAE,CACV,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GACR,KAAK,CAAC,IAAI;QACV,yBAAyB,CAAC,UAAU,EAAE,KAAK,CAAC;QAC5C,CAAC,KAAK,KAAK,aAAa;YACtB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,yBAAyB,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;QAC1D,EAAE,IAAI,EAAE,MAAM,EAAkB,CAAC;IAEpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC3B,CAAC;AAaD,MAAM,sBAAsB,GAAG,+BAA+B,CAAC;AAE/D;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC5E,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK;YAAE,SAAS;QAClC,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC/C,IAAI,QAAQ,GAAa,MAAM,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,yBAAyB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YACrD,IAAI,IAAI;gBAAE,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,0DAA0D;QAC5D,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACnB,IAAI,CAAC,CAAC,KAAK,KAAK,aAAa;YAAE,OAAO,CAAC,CAAC,CAAC;QACzC,IAAI,CAAC,CAAC,KAAK,KAAK,aAAa;YAAE,OAAO,CAAC,CAAC;QACxC,OAAO,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B,CAAC,OAA0B;IACpE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9D,OAAO,qIAAqI,KAAK,uGAAuG,CAAC;AAC3P,CAAC"}