@cyanheads/brapi-mcp-server 0.7.10 → 0.7.12

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 (121) hide show
  1. package/CLAUDE.md +32 -25
  2. package/Dockerfile +19 -5
  3. package/README.md +7 -4
  4. package/changelog/0.7.x/0.7.11.md +43 -0
  5. package/changelog/0.7.x/0.7.12.md +35 -0
  6. package/changelog/template.md +62 -40
  7. package/dist/config/alias-credentials.d.ts +1 -1
  8. package/dist/config/alias-credentials.d.ts.map +1 -1
  9. package/dist/config/alias-credentials.js +24 -8
  10. package/dist/config/alias-credentials.js.map +1 -1
  11. package/dist/config/builtin-aliases.js.map +1 -1
  12. package/dist/config/server-config.d.ts +1 -1
  13. package/dist/config/server-config.d.ts.map +1 -1
  14. package/dist/config/server-config.js +3 -1
  15. package/dist/config/server-config.js.map +1 -1
  16. package/dist/mcp-server/resources/definitions/brapi-calls.resource.d.ts +3 -3
  17. package/dist/mcp-server/resources/definitions/brapi-calls.resource.d.ts.map +1 -1
  18. package/dist/mcp-server/resources/definitions/brapi-calls.resource.js +1 -1
  19. package/dist/mcp-server/resources/definitions/brapi-filters.resource.d.ts +3 -3
  20. package/dist/mcp-server/resources/definitions/brapi-filters.resource.d.ts.map +1 -1
  21. package/dist/mcp-server/resources/definitions/brapi-filters.resource.js +1 -1
  22. package/dist/mcp-server/resources/definitions/brapi-germplasm.resource.d.ts +6 -6
  23. package/dist/mcp-server/resources/definitions/brapi-germplasm.resource.d.ts.map +1 -1
  24. package/dist/mcp-server/resources/definitions/brapi-germplasm.resource.js +1 -1
  25. package/dist/mcp-server/resources/definitions/brapi-server-info.resource.d.ts +3 -3
  26. package/dist/mcp-server/resources/definitions/brapi-server-info.resource.d.ts.map +1 -1
  27. package/dist/mcp-server/resources/definitions/brapi-server-info.resource.js +1 -1
  28. package/dist/mcp-server/resources/definitions/brapi-study.resource.d.ts +6 -6
  29. package/dist/mcp-server/resources/definitions/brapi-study.resource.d.ts.map +1 -1
  30. package/dist/mcp-server/resources/definitions/brapi-study.resource.js +1 -1
  31. package/dist/mcp-server/resources/definitions/brapi-variable.resource.d.ts +6 -6
  32. package/dist/mcp-server/resources/definitions/brapi-variable.resource.d.ts.map +1 -1
  33. package/dist/mcp-server/resources/definitions/brapi-variable.resource.js +1 -1
  34. package/dist/mcp-server/tools/definitions/brapi-build-phenotype-matrix.tool.d.ts +19 -14
  35. package/dist/mcp-server/tools/definitions/brapi-build-phenotype-matrix.tool.d.ts.map +1 -1
  36. package/dist/mcp-server/tools/definitions/brapi-build-phenotype-matrix.tool.js +34 -0
  37. package/dist/mcp-server/tools/definitions/brapi-build-phenotype-matrix.tool.js.map +1 -1
  38. package/dist/mcp-server/tools/definitions/brapi-connect.tool.d.ts +12 -12
  39. package/dist/mcp-server/tools/definitions/brapi-connect.tool.d.ts.map +1 -1
  40. package/dist/mcp-server/tools/definitions/brapi-dataframe-describe.tool.d.ts +2 -2
  41. package/dist/mcp-server/tools/definitions/brapi-dataframe-describe.tool.d.ts.map +1 -1
  42. package/dist/mcp-server/tools/definitions/brapi-dataframe-export.tool.d.ts +14 -14
  43. package/dist/mcp-server/tools/definitions/brapi-dataframe-export.tool.d.ts.map +1 -1
  44. package/dist/mcp-server/tools/definitions/brapi-dataframe-query.tool.d.ts +9 -4
  45. package/dist/mcp-server/tools/definitions/brapi-dataframe-query.tool.d.ts.map +1 -1
  46. package/dist/mcp-server/tools/definitions/brapi-dataframe-query.tool.js +31 -0
  47. package/dist/mcp-server/tools/definitions/brapi-dataframe-query.tool.js.map +1 -1
  48. package/dist/mcp-server/tools/definitions/brapi-describe-filters.tool.d.ts +6 -6
  49. package/dist/mcp-server/tools/definitions/brapi-describe-filters.tool.d.ts.map +1 -1
  50. package/dist/mcp-server/tools/definitions/brapi-export-genotype-matrix.tool.d.ts +11 -11
  51. package/dist/mcp-server/tools/definitions/brapi-export-genotype-matrix.tool.d.ts.map +1 -1
  52. package/dist/mcp-server/tools/definitions/brapi-find-genotype-calls.tool.d.ts +11 -11
  53. package/dist/mcp-server/tools/definitions/brapi-find-genotype-calls.tool.d.ts.map +1 -1
  54. package/dist/mcp-server/tools/definitions/brapi-find-germplasm.tool.d.ts +6 -6
  55. package/dist/mcp-server/tools/definitions/brapi-find-germplasm.tool.d.ts.map +1 -1
  56. package/dist/mcp-server/tools/definitions/brapi-find-images.tool.d.ts +6 -6
  57. package/dist/mcp-server/tools/definitions/brapi-find-images.tool.d.ts.map +1 -1
  58. package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.d.ts +6 -6
  59. package/dist/mcp-server/tools/definitions/brapi-find-locations.tool.d.ts.map +1 -1
  60. package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.d.ts +6 -6
  61. package/dist/mcp-server/tools/definitions/brapi-find-observations.tool.d.ts.map +1 -1
  62. package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.d.ts +6 -6
  63. package/dist/mcp-server/tools/definitions/brapi-find-studies.tool.d.ts.map +1 -1
  64. package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.d.ts +7 -7
  65. package/dist/mcp-server/tools/definitions/brapi-find-variables.tool.d.ts.map +1 -1
  66. package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.d.ts +6 -6
  67. package/dist/mcp-server/tools/definitions/brapi-find-variants.tool.d.ts.map +1 -1
  68. package/dist/mcp-server/tools/definitions/brapi-germplasm-performance.tool.d.ts +6 -6
  69. package/dist/mcp-server/tools/definitions/brapi-germplasm-performance.tool.d.ts.map +1 -1
  70. package/dist/mcp-server/tools/definitions/brapi-get-germplasm.tool.d.ts +6 -6
  71. package/dist/mcp-server/tools/definitions/brapi-get-germplasm.tool.d.ts.map +1 -1
  72. package/dist/mcp-server/tools/definitions/brapi-get-image.tool.d.ts +6 -6
  73. package/dist/mcp-server/tools/definitions/brapi-get-image.tool.d.ts.map +1 -1
  74. package/dist/mcp-server/tools/definitions/brapi-get-study.tool.d.ts +6 -6
  75. package/dist/mcp-server/tools/definitions/brapi-get-study.tool.d.ts.map +1 -1
  76. package/dist/mcp-server/tools/definitions/brapi-raw-get.tool.d.ts +6 -6
  77. package/dist/mcp-server/tools/definitions/brapi-raw-get.tool.d.ts.map +1 -1
  78. package/dist/mcp-server/tools/definitions/brapi-raw-search.tool.d.ts +7 -7
  79. package/dist/mcp-server/tools/definitions/brapi-raw-search.tool.d.ts.map +1 -1
  80. package/dist/mcp-server/tools/definitions/brapi-server-info.tool.d.ts +9 -9
  81. package/dist/mcp-server/tools/definitions/brapi-server-info.tool.d.ts.map +1 -1
  82. package/dist/mcp-server/tools/definitions/brapi-submit-observations.tool.d.ts +23 -27
  83. package/dist/mcp-server/tools/definitions/brapi-submit-observations.tool.d.ts.map +1 -1
  84. package/dist/mcp-server/tools/definitions/brapi-submit-observations.tool.js +41 -55
  85. package/dist/mcp-server/tools/definitions/brapi-submit-observations.tool.js.map +1 -1
  86. package/dist/mcp-server/tools/definitions/brapi-walk-pedigree.tool.d.ts +8 -8
  87. package/dist/mcp-server/tools/definitions/brapi-walk-pedigree.tool.d.ts.map +1 -1
  88. package/dist/mcp-server/tools/definitions/index.d.ts +829 -824
  89. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
  90. package/dist/mcp-server/tools/shared/find-helpers.js.map +1 -1
  91. package/dist/mcp-server/tools/shared/observations.js.map +1 -1
  92. package/dist/mcp-server/tools/shared/orientation-envelope.d.ts +12 -12
  93. package/dist/services/brapi-client/brapi-client.d.ts +2 -3
  94. package/dist/services/brapi-client/brapi-client.d.ts.map +1 -1
  95. package/dist/services/brapi-client/brapi-client.js +16 -19
  96. package/dist/services/brapi-client/brapi-client.js.map +1 -1
  97. package/dist/services/brapi-client/types.d.ts +2 -2
  98. package/dist/services/brapi-dialect/detect.d.ts +1 -1
  99. package/dist/services/brapi-dialect/detect.js.map +1 -1
  100. package/dist/services/brapi-dialect/index.d.ts +2 -2
  101. package/dist/services/brapi-dialect/index.js +1 -1
  102. package/dist/services/brapi-dialect/index.js.map +1 -1
  103. package/dist/services/canvas-bridge/canvas-bridge.d.ts +1 -1
  104. package/dist/services/canvas-bridge/canvas-bridge.d.ts.map +1 -1
  105. package/dist/services/canvas-bridge/canvas-bridge.js.map +1 -1
  106. package/dist/services/capability-registry/capability-registry.d.ts +1 -1
  107. package/dist/services/capability-registry/capability-registry.d.ts.map +1 -1
  108. package/dist/services/capability-registry/capability-registry.js +1 -1
  109. package/dist/services/capability-registry/capability-registry.js.map +1 -1
  110. package/dist/services/ontology-resolver/ontology-resolver.d.ts.map +1 -1
  111. package/dist/services/ontology-resolver/ontology-resolver.js.map +1 -1
  112. package/dist/services/reference-data-cache/reference-data-cache.d.ts +2 -2
  113. package/dist/services/reference-data-cache/reference-data-cache.d.ts.map +1 -1
  114. package/dist/services/reference-data-cache/reference-data-cache.js +1 -1
  115. package/dist/services/reference-data-cache/reference-data-cache.js.map +1 -1
  116. package/dist/services/server-registry/server-registry.d.ts.map +1 -1
  117. package/dist/services/server-registry/server-registry.js.map +1 -1
  118. package/dist/services/server-registry/types.d.ts +1 -1
  119. package/manifest.json +8 -6
  120. package/package.json +15 -14
  121. package/server.json +3 -3
package/CLAUDE.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # Agent Protocol
2
2
 
3
3
  **Server:** brapi-mcp-server
4
- **Version:** 0.7.10
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.10.14`
4
+ **Version:** 0.7.12
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.0`
6
+ **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
+ **MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revisions 2026-07-28 and 2025-*)
6
8
 
7
9
  > **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
8
10
 
@@ -32,7 +34,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
32
34
  - **Logic throws, framework catches.** Tool/resource handlers are pure — throw on failure, no `try/catch`. The framework catches, classifies, and formats. Default to typed contracts: declare `errors: [...]` and throw via `ctx.fail(reason, …)` so failures carry stable `data.reason` codes for agent-client routing. Fall back to error factories (`notFound()`, `validationError()`, etc.) only for services or when no contract entry fits.
33
35
  - **Use `ctx.log`** for request-scoped logging. No `console` calls.
34
36
  - **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
35
- - **Check `ctx.elicit` / `ctx.sample`** for presence before calling.
37
+ - **Need input the caller didn't supply?** `return ctx.requestInput(...)` and read `ctx.inputs` when the handler is re-entered. Never `await` for user input mid-handler.
36
38
  - **Secrets in env vars only** — never hardcoded.
37
39
  - **Close the loop on issues.** When implementing work tracked by a GitHub issue, comment on the issue with what landed and close it. Do both — a comment without a close leaves stale issues open; a close without a comment leaves no record of what shipped. The comment is for future readers — state the concrete changes, not the conversation that produced them.
38
40
 
@@ -161,14 +163,16 @@ Handlers receive a unified `ctx` object. Currently used surface:
161
163
 
162
164
  | Property | Description |
163
165
  |:---------|:------------|
164
- | `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
166
+ | `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. Dual-sink: Pino **and** `notifications/message` to the client, so treat it as client-visible. |
165
167
  | `ctx.state` | Tenant-scoped KV — used by `ServerRegistry` (connection aliases), `CanvasBridge` (default canvas pointer + per-table provenance), and `CapabilityRegistry` (cached profiles). Spilled `find_*` rows live on the canvas (DuckDB), not in `ctx.state`. |
166
- | `ctx.sessionId` | Mcp-Session-Id (HTTP stateful/auto); `undefined` for stdio and stateless HTTP unless `exposeStatelessSessionId` is opted in. Composed into `ServerRegistry.connKey` and `CanvasBridge.defaultCanvasKey` when `BRAPI_SESSION_ISOLATION=true` (default), so concurrent HTTP sessions in the same tenant don't share connection state or canvas. Discovery / scoping key on top of tenant-keyed state — not an authorization principal. |
168
+ | `ctx.sessionId` | Mcp-Session-Id (HTTP stateful/auto); `undefined` for stdio, stateless HTTP unless `exposeStatelessSessionId` is opted in, and every request on protocol revision 2026-07-28, which is session-less by design. Composed into `ServerRegistry.connKey` and `CanvasBridge.defaultCanvasKey` when `BRAPI_SESSION_ISOLATION=true` (default), so concurrent HTTP sessions in the same tenant don't share connection state or canvas. Discovery / scoping key on top of tenant-keyed state — not an authorization principal. |
167
169
  | `ctx.signal` | `AbortSignal` — threaded into every BrAPI HTTP call so client-side cancellation aborts the upstream request. |
168
170
  | `ctx.requestId` | Unique request ID — auto-attached to every `ctx.log` entry. |
169
171
  | `ctx.tenantId` | Tenant ID from JWT or `'default'` for stdio / HTTP+`auth=none` — outer scope on all `ctx.state` reads/writes. |
172
+ | `ctx.requestInput` / `ctx.inputs` | Multi-round-trip input. `brapi_submit_observations` gates apply-mode writes on a `confirm` elicitation: it reads `ctx.inputs.view('confirm')`, returns `ctx.requestInput({ inputRequests: … })` when the answer is missing, and treats a declined, cancelled, or unparseable answer as terminal (`user_declined`). `force: true` skips the round. |
173
+ | `ctx.enrich` | Success-path agent context. `brapi_dataframe_query` and `brapi_build_phenotype_matrix` disclose capped results with `ctx.enrich.truncated({ shown, cap, guidance })`. |
170
174
 
171
- `ctx.elicit` is used by `brapi_submit_observations` to gate apply-mode writes behind user confirmation (with explicit `force: true` as the bypass). `ctx.sample` and `ctx.progress` are not used yet — they'll show up when long-running workflows (pedigree traversal, genotype-call pulls) need progress reporting or LLM sampling. `ctx.fail(reason, …)` is the typed thrower keyed off declared `errors[]` contracts — used by 14 tools and 1 resource today. `ctx.recoveryFor(reason)` resolves the matching contract entry's recovery hint into `data.recovery.hint` so it surfaces on the wire.
175
+ `ctx.fail(reason, …)` is the typed thrower keyed off declared `errors[]` contracts — used by 14 tools and 1 resource today. `ctx.recoveryFor(reason)` resolves the matching contract entry's recovery hint into `data.recovery.hint` so it surfaces on the wire. `ctx.content` is unused — no tool emits media blocks outside `brapi_get_image`, which returns image bytes through its own output schema.
172
176
 
173
177
  ---
174
178
 
@@ -176,7 +180,7 @@ Handlers receive a unified `ctx` object. Currently used surface:
176
180
 
177
181
  Handlers throw — the framework catches, classifies, and formats.
178
182
 
179
- **Default for new tools: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` to receive a typed `ctx.fail(reason, …)` keyed by the declared reason union. TypeScript catches `ctx.fail('typo')` at compile time, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. The `recovery` field is required descriptive metadata (≥ 5 words, lint-validated); to surface it on the wire, spread `...ctx.recoveryFor('reason')` into `data` or pass an explicit `{ recovery: { hint: '...' } }` when runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring. Live across the BrAPI surface today: `brapi_build_phenotype_matrix`, `brapi_dataframe_describe`, `brapi_dataframe_export`, `brapi_dataframe_query`, `brapi_describe_filters`, `brapi_export_genotype_matrix`, `brapi_find_genotype_calls`, `brapi_germplasm_performance`, `brapi_get_germplasm`, `brapi_get_image`, `brapi_get_study`, `brapi_raw_get`, `brapi_raw_search`, `brapi_submit_observations`, plus the `brapi://variable/{observationVariableDbId}` resource.
183
+ **Default for new tools: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` to receive a typed `ctx.fail(reason, …)` keyed by the declared reason union. TypeScript catches `ctx.fail('typo')` at compile time, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. The `recovery` field is required descriptive metadata (≥ 5 words, lint-validated); to surface it on the wire, spread `...ctx.recoveryFor('reason')` into `data` or pass an explicit `{ recovery: { hint: '...' } }` when runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`, `RequestCancelled`) bubble freely and don't need declaring. Live across the BrAPI surface today: `brapi_build_phenotype_matrix`, `brapi_dataframe_describe`, `brapi_dataframe_export`, `brapi_dataframe_query`, `brapi_describe_filters`, `brapi_export_genotype_matrix`, `brapi_find_genotype_calls`, `brapi_germplasm_performance`, `brapi_get_germplasm`, `brapi_get_image`, `brapi_get_study`, `brapi_raw_get`, `brapi_raw_search`, `brapi_submit_observations`, plus the `brapi://variable/{observationVariableDbId}` resource.
180
184
 
181
185
  ```ts
182
186
  errors: [
@@ -257,7 +261,7 @@ src/
257
261
  brapi-build-phenotype-matrix.tool.ts # Germplasm × trait matrix from studies; materialized as canvas dataframe
258
262
  brapi-germplasm-performance.tool.ts # Per-variable aggregates (n, mean, median, sd) for a single germplasm
259
263
  brapi-export-genotype-matrix.tool.ts # Genotype calls → germplasm × variant dataframe + VCF-lite / PLINK serialization
260
- brapi-submit-observations.tool.ts # Two-phase observation write — preview / apply (POST + PUT) with elicit gate
264
+ brapi-submit-observations.tool.ts # Two-phase observation write — preview / apply (POST + PUT) behind a confirmation round trip
261
265
  brapi-raw-get.tool.ts # Last-resort GET passthrough with routing nudge
262
266
  brapi-raw-search.tool.ts # Last-resort POST /search passthrough with async polling
263
267
  shared/
@@ -297,9 +301,9 @@ src/
297
301
 
298
302
  ## Skills
299
303
 
300
- Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool.
304
+ Skills are modular instructions in `framework-skills/` at the project root. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. Keep development skills out of root `skills/`: plugin hosts load that directory for installing agents.
301
305
 
302
- **Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, Codex: `.codex/skills/`, shared: `.agents/skills/`, others: equivalent). This makes skills available as context without needing to reference `skills/` paths manually. After framework updates, run the `maintenance` skill — it re-syncs the agent directory automatically (Phase B).
306
+ **Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, Codex: `.codex/skills/`, shared: `.agents/skills/`, others: equivalent). This makes skills available as context without needing to reference `framework-skills/` paths manually. After framework updates, run the `maintenance` skill — it re-syncs the agent directory automatically (Phase B).
303
307
 
304
308
  Available skills:
305
309
 
@@ -317,10 +321,12 @@ Available skills:
317
321
  | `tool-defs-analysis` | Read-only audit of MCP definition language across the surface — voice, leaks, defaults, recovery hints, output descriptions |
318
322
  | `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
319
323
  | `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
324
+ | `techniques` | Catalog of response/data-shaping techniques — overflow handling, payload shaping, retrieval patterns |
320
325
  | `devcheck` | Lint, format, typecheck, audit |
321
326
  | `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
322
- | `git-wrapup` | Land working-tree changes as a versioned commit + annotated tag version bump, changelog, verify, tag. Local only. |
323
- | `release-and-publish` | Push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
327
+ | `git-wrapup` | Land working-tree changes as a versioned commit stack; opens a release PR when the project declares release PR mode. |
328
+ | `release-pr-review` | Review an open release PR, land fixups, and keep its body current. Release PR mode only. |
329
+ | `release-and-publish` | Tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup`. |
324
330
  | `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
325
331
  | `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
326
332
  | `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
@@ -329,7 +335,7 @@ Available skills:
329
335
  | `api-linter` | Definition lint rule reference (`format-parity`, `schema-*`, `name-*`, `server-json-*`, …) |
330
336
  | `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper for big result sets — Tier 3 opt-in |
331
337
  | `api-config` | AppConfig, parseConfig, env vars |
332
- | `api-context` | Context interface, logger, state, progress |
338
+ | `api-context` | Context interface, RequestContext, logger, state, multi-round-trip input |
333
339
  | `api-errors` | McpError, JsonRpcErrorCode, error patterns |
334
340
  | `api-mirror` | MirrorService: persistent SQLite-backed local mirror of a bulk upstream dataset with FTS5 — Tier 3 opt-in |
335
341
  | `api-services` | LLM, Speech, Graph services |
@@ -338,7 +344,7 @@ Available skills:
338
344
  | `api-utils` | Formatting, parsing, security, pagination, scheduling, telemetry helpers |
339
345
  | `api-workers` | Cloudflare Workers runtime |
340
346
 
341
- **Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
347
+ **Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `framework-skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
342
348
 
343
349
  When you complete a skill's checklist, check the boxes and add a completion timestamp at the end (e.g., `Completed: 2026-03-11`).
344
350
 
@@ -354,13 +360,14 @@ When you complete a skill's checklist, check the boxes and add a completion time
354
360
  | `bun run rebuild` | Clean + build |
355
361
  | `bun run clean` | Remove build artifacts |
356
362
  | `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
357
- | `bun run audit:refresh` | Delete `bun.lock`, reinstall, re-audit. Use when `devcheck` flags a transitive advisory Bun's `update` is sticky on transitive resolutions, so the advisory may be a stale-lockfile false positive. If it survives the refresh, it's real. |
363
+ | `bun run audit:fix` | Upgrade vulnerable packages within existing ranges with `bun audit fix`; try before `bun update <name>` and `bun dedupe`. |
364
+ | `bun run audit:refresh` | Delete `bun.lock`, reinstall, re-audit. Last resort after in-place fixes; re-resolves ranged dependencies, including the framework. |
358
365
  | `bun run list-skills` | Print the skill index for this project (name, version, description) |
359
366
  | `bun run tree` | Generate `docs/tree.md` |
360
367
  | `bun run format` | Auto-fix formatting via Biome |
361
368
  | `bun run lint:mcp` | Validate MCP tool / resource / prompt definitions against the spec |
362
369
  | `bun run lint:packaging` | Verify env var alignment between `manifest.json` and `server.json` |
363
- | `bun run bundle` | Build and pack as `.mcpb` for one-click Claude Desktop install |
370
+ | `bun run bundle` | Build, pack, and clean a `.mcpb` for one-click Claude Desktop install |
364
371
  | `bun run test` | Vitest suite |
365
372
  | `bun run start` | Production mode — defers transport selection to `MCP_TRANSPORT_TYPE` (stdio default) |
366
373
  | `bun run start:stdio` | Production mode (stdio) — requires prior `bun run build` |
@@ -372,7 +379,7 @@ When you complete a skill's checklist, check the boxes and add a completion time
372
379
 
373
380
  ## Bundling
374
381
 
375
- `bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. MCPB is stdio-only — HTTP deployments are unaffected. To opt out, delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly when `manifest.json` is absent.
382
+ `bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. The pack step is followed by `scripts/clean-mcpb.ts`, which prunes dev dependencies (`mcpb clean`) and strips two classes of `node_modules/**` content that root-anchored `.mcpbignore` patterns cannot reach: dependency-shipped agent docs (`framework-skills/`, `skills/`, `.claude/`, `.agents/`, `SKILL.md`) and platform-specific native bindings, which would otherwise lock the bundle to the platform it was packed on. The bundle therefore ships portable without the DuckDB native — `@duckdb/node-api` is loaded lazily, so canvas tools report an actionable install hint and every other tool works normally. MCPB is stdio-only — HTTP deployments are unaffected. To opt out, delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly when `manifest.json` is absent.
376
383
 
377
384
  **Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match.
378
385
 
@@ -388,20 +395,20 @@ Each per-version file opens with YAML frontmatter:
388
395
  ---
389
396
  summary: One-line headline, ≤350 chars # required — powers the rollup index
390
397
  breaking: false # optional — true flags breaking changes
391
- security: false # optional — true flags security fixes
398
+ security: false # optional — true ONLY for a source-code security fix, never a dependency CVE bump
392
399
  ---
393
400
 
394
401
  # 0.1.0 — YYYY-MM-DD
395
402
  ...
396
403
  ```
397
404
 
398
- `breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section. When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
405
+ `breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section — set it only for a security fix in this server's *own source code*, never for a routine dependency or transitive CVE bump (record those under `## Dependencies`). When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
399
406
 
400
407
  `agent-notes` is an optional free-form field for maintenance agents processing the release downstream. Content here won't appear in the rendered CHANGELOG — it's consumed by agents running the `maintenance` skill. Use it for adoption instructions that don't fit the human-facing sections: new files to create, fields to populate, one-time migration steps. Omit entirely when there's nothing to say.
401
408
 
402
- **Section order** (Keep a Changelog): Added, Changed, Deprecated, Removed, Fixed, Security. Include only sections with entries — don't ship empty headers.
409
+ **Section order** (Keep a Changelog): Added, Changed, Deprecated, Removed, Fixed, Security, then Dependencies. Include only sections with entries — don't ship empty headers.
403
410
 
404
- **Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject omits the version number (GitHub prepends it). See `changelog/template.md` for the full format reference.
411
+ **Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. Subject omits the version number (GitHub prepends it). Follow `framework-skills/release-and-publish/SKILL.md` for the tag format.
405
412
 
406
413
  ---
407
414
 
@@ -425,7 +432,7 @@ import { getMyService } from '@/services/my-domain/my-service.js';
425
432
  - [ ] JSDoc `@fileoverview` + `@module` on every file
426
433
  - [ ] `ctx.log` for logging, `ctx.state` for storage — no `console`, no direct persistence access
427
434
  - [ ] Handlers throw on failure — error factories or plain `Error`, no try/catch
428
- - [ ] `format()` renders all data the LLM needs — different clients forward different surfaces (Codex → `structuredContent`, Codex Desktop → `content[]`); both must carry the same data
435
+ - [ ] `format()` renders all data the LLM needs — different clients forward different surfaces (Claude Code → `structuredContent`, Claude Desktop → `content[]`); both must carry the same data
429
436
  - [ ] BrAPI tool: resolves connection via `ServerRegistry.get(ctx, alias ?? DEFAULT_ALIAS)` before touching the client
430
437
  - [ ] BrAPI tool: gates the call with `CapabilityRegistry.ensure(...)` — never fires against an endpoint the server didn't advertise
431
438
  - [ ] BrAPI tool: raw / domain / output schemas reviewed against real upstream sparsity (most `/germplasm` and `/studies` fields are optional in the wild)
@@ -434,7 +441,7 @@ import { getMyService } from '@/services/my-domain/my-service.js';
434
441
  - [ ] Tests include at least one sparse upstream payload (fields omitted) alongside the happy path
435
442
  - [ ] Registered in the `tools` array of `createApp()` in `src/index.ts`
436
443
  - [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
437
- - [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` = package name; `interface.shortDescription` from `package.json` description
438
- - [ ] `.codex-plugin/mcp.json` updated — server name key matches `package.json` name; env vars added for any required API keys
439
- - [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; inline `mcpServers` entry with server name key, env vars for any required API keys
444
+ - [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` = the unscoped repo name; `interface.shortDescription` from `package.json` description
445
+ - [ ] `.codex-plugin/mcp.json` updated — server name key is the unscoped repo name; user-supplied variables are listed in `env_vars`, never set to empty strings in `env`
446
+ - [ ] `.claude-plugin/plugin.json` populated — metadata from `package.json`; inline `mcpServers` entry keyed by the unscoped repo name; user-supplied variables declared under `userConfig` and referenced as `${user_config.<option>}`
440
447
  - [ ] `bun run devcheck` passes
package/Dockerfile CHANGED
@@ -3,8 +3,16 @@
3
3
  #
4
4
  # This stage installs all dependencies (including dev), builds the TypeScript
5
5
  # source code into JavaScript, and prepares the production assets.
6
+ #
7
+ # Pinned to BUILDPLATFORM, not the target platform. `bun run build` emits
8
+ # platform-independent JavaScript and only `dist/` is copied forward, so this
9
+ # stage has no reason to run under emulation — and under QEMU it does not run
10
+ # at all: Bun's JavaScriptCore aborts with a spurious MemoryExhaustion
11
+ # assertion (~21 MB peak), so a cross-arch build of this stage fails outright
12
+ # on an arm64 host. Building natively also removes emulation from the slowest
13
+ # stage of a multi-arch build.
6
14
  # ==============================================================================
7
- FROM oven/bun:1.3.14 AS build
15
+ FROM --platform=$BUILDPLATFORM oven/bun:1.4.0 AS build
8
16
 
9
17
  WORKDIR /usr/src/app
10
18
 
@@ -33,7 +41,7 @@ RUN bun run build
33
41
  # application. It uses a slim base image and only includes production
34
42
  # dependencies and build artifacts.
35
43
  # ==============================================================================
36
- FROM oven/bun:1.3.14-slim AS production
44
+ FROM oven/bun:1.4.0-slim AS production
37
45
 
38
46
  WORKDIR /usr/src/app
39
47
 
@@ -54,8 +62,14 @@ COPY package.json bun.lock ./
54
62
 
55
63
  # Install only production dependencies, ignoring any lifecycle scripts (like 'prepare')
56
64
  # that are not needed in the final production image.
65
+ # `--omit=peer` drops the framework's optional peer tiers (test runner, service
66
+ # SDKs, parsers) that Bun would otherwise auto-install. Anything this server
67
+ # actually imports belongs in its own `dependencies` — @duckdb/node-api among
68
+ # them — so nothing needed at runtime is lost. The two `bun add` steps below
69
+ # carry the same flag; without it, they re-resolve the graph and pull every
70
+ # optional peer back in.
57
71
  RUN --mount=type=cache,target=/root/.bun/install/cache \
58
- bun install --production --frozen-lockfile --ignore-scripts
72
+ bun install --production --omit=peer --frozen-lockfile --ignore-scripts
59
73
 
60
74
  # Conditionally install OpenTelemetry optional peer dependencies (Tier 3).
61
75
  # These are not bundled by default to keep the base image lean. Enable at build time
@@ -63,7 +77,7 @@ RUN --mount=type=cache,target=/root/.bun/install/cache \
63
77
  ARG OTEL_ENABLED=true
64
78
  RUN --mount=type=cache,target=/root/.bun/install/cache \
65
79
  if [ "$OTEL_ENABLED" = "true" ]; then \
66
- bun add @hono/otel \
80
+ bun add --omit=dev --omit=peer --ignore-scripts @hono/otel \
67
81
  @opentelemetry/instrumentation-http \
68
82
  @opentelemetry/exporter-metrics-otlp-http \
69
83
  @opentelemetry/exporter-trace-otlp-http \
@@ -82,7 +96,7 @@ RUN --mount=type=cache,target=/root/.bun/install/cache \
82
96
  ARG CANVAS_ENABLED=true
83
97
  RUN --mount=type=cache,target=/root/.bun/install/cache \
84
98
  if [ "$CANVAS_ENABLED" = "true" ]; then \
85
- bun add @duckdb/node-api; \
99
+ bun add --omit=dev --omit=peer @duckdb/node-api; \
86
100
  fi
87
101
 
88
102
  # Copy the compiled application code from the build stage
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![npm](https://img.shields.io/npm/v/@cyanheads/brapi-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/brapi-mcp-server) [![Version](https://img.shields.io/badge/Version-0.7.10-blue.svg?style=flat-square)](./CHANGELOG.md) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![TypeScript](https://img.shields.io/badge/TypeScript-^6.0.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.3.14-blueviolet.svg?style=flat-square)](https://bun.sh/) [![Status](https://img.shields.io/badge/Status-Beta-yellow.svg?style=flat-square)](./CHANGELOG.md)
10
+ [![npm](https://img.shields.io/npm/v/@cyanheads/brapi-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/brapi-mcp-server) [![Version](https://img.shields.io/badge/Version-0.7.12-blue.svg?style=flat-square)](./CHANGELOG.md) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.0-blueviolet.svg?style=flat-square)](https://bun.sh/) [![Status](https://img.shields.io/badge/Status-Beta-yellow.svg?style=flat-square)](./CHANGELOG.md)
11
11
 
12
12
  </div>
13
13
 
@@ -66,7 +66,7 @@
66
66
 
67
67
  | Tool | Description |
68
68
  |:-----|:------------|
69
- | `brapi_submit_observations` | Two-phase observation write — `mode: preview` validates; `mode: apply` elicits confirmation, then fans POST + PUT in parallel. Additive only — no destructive deletion. |
69
+ | `brapi_submit_observations` | Two-phase observation write — `mode: preview` validates; `mode: apply` asks the caller to confirm, then fans POST + PUT in parallel. Additive only — no destructive deletion. |
70
70
 
71
71
  ### Escape hatches
72
72
 
@@ -118,6 +118,8 @@ Within one (tenant, session), dataframes act as a self-cleaning shared notebook:
118
118
 
119
119
  **Default (isolated) shape.** Under `MCP_AUTH_MODE=none` + HTTP stateful (the default), each MCP session carves its own connection state and its own canvas. Two researchers connected to the same host don't see each other's `brapi_connect` aliases, exchanged SGN/OAuth tokens, or spilled `df_<uuid>` rows. Stdio always behaves as one session (single-process, no concurrency).
120
120
 
121
+ **Clients on MCP revision 2026-07-28.** That revision is session-less on every transport — requests carry no `Mcp-Session-Id` — so `ctx.sessionId` is undefined and a client negotiating it falls back to the shared tenant workspace even under `MCP_SESSION_MODE=stateful`. Session isolation applies to 2025-era clients; deployments that need a hard boundary for 2026-era clients should carve tenants with `MCP_AUTH_MODE=jwt`/`oauth`.
122
+
121
123
  **Shared-workspace shape.** Set `BRAPI_SESSION_ISOLATION=false` for cross-session collaboration in one tenant — multiple MCP sessions then share connection state and one default canvas, the way pre-0.5.3 deployments behaved. Useful when planning, analysis, and writeup agents run as separate MCP clients but operate as one researcher on shared upstream credentials.
122
124
 
123
125
  **On privileged data.** The `df_<uuid>` name is a capability token within a canvas — not row-level access control. Anyone holding the name within the same (tenant, session) bucket can read its rows. Under default isolation, that bucket is one MCP session. Under `BRAPI_SESSION_ISOLATION=false`, the bucket widens to the whole tenant (all callers under `auth=none`, or one user's sessions under `jwt`/`oauth`). Treat dataframe names like authenticated share links — pass within the bucket, not externally. The 24h TTL caps blast radius; the provenance trail (originating tool, baseUrl, query) supports audit. Belt-and-braces: `brapi_dataframe_describe` requires an explicit `dataframe` name on shared-trust HTTP (no list-all enumeration), and `brapi_dataframe_query` rejects system-catalog reads (`information_schema`, `pg_catalog`, `sqlite_master`, `duckdb_*`) — so a caller without a known `df_<uuid>` name can't fish through either surface.
@@ -192,7 +194,7 @@ MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
192
194
 
193
195
  No env vars are required — the six built-in aliases (`bti-cassava`, `bti-sweetpotato`, `bti-breedbase-demo`, `t3-wheat`, `t3-oat`, `t3-barley`) resolve out-of-the-box, and agents can connect to any other BrAPI v2 URL at runtime via `brapi_connect`. **For credentialed servers, prefer env vars over agent input** so passwords / tokens / API keys stay out of the LLM context — see [Per-alias credentials](#per-alias-credentials).
194
196
 
195
- **Prerequisites:** [Bun v1.3.11+](https://bun.sh/) or Node.js v24+. [`@duckdb/node-api`](https://www.npmjs.com/package/@duckdb/node-api) is a required dependency — supported on Linux/macOS/Windows × x64 plus Linux/macOS arm64 (no Windows arm64; no Cloudflare Workers).
197
+ **Prerequisites:** [Bun v1.4.0+](https://bun.sh/) or Node.js v24+. [`@duckdb/node-api`](https://www.npmjs.com/package/@duckdb/node-api) is a required dependency — supported on Linux/macOS/Windows × x64 plus Linux/macOS arm64 (no Windows arm64; no Cloudflare Workers).
196
198
 
197
199
  ---
198
200
 
@@ -223,7 +225,7 @@ Every variable is optional.
223
225
  | `BRAPI_CANVAS_DROP_ENABLED` | Opt-in for `brapi_dataframe_drop` registration. Off by default; dataframes expire via TTL when left unmanaged. | `false` |
224
226
  | `BRAPI_EXPORT_DIR` | Directory for `brapi_dataframe_export` output files. Setting a path is the opt-in (no separate enable flag); unset leaves the tool out of `tools/list`. Stdio-only — the tool stays disabled under HTTP transport regardless of this value. Bridged to the framework's `CANVAS_EXPORT_PATH` automatically. | — |
225
227
  | `BRAPI_CANVAS_MAX_ROWS` / `BRAPI_CANVAS_QUERY_TIMEOUT_MS` | Per-query response row cap and wall-clock timeout for `brapi_dataframe_query`. | `10000` / `30000` |
226
- | `MCP_TRANSPORT_TYPE` / `MCP_HTTP_PORT` / `MCP_SESSION_MODE` | Transport (`stdio` \| `http`), HTTP port, session mode (`stateful` \| `stateless` \| `auto`). | `stdio` / `3010` / `auto` |
228
+ | `MCP_TRANSPORT_TYPE` / `MCP_HTTP_PORT` / `MCP_SESSION_MODE` | Transport (`stdio` \| `http`), HTTP port, session mode: `stateful` retains 2025 client sessions for observation-write confirmation; `stateless` cannot perform that confirmation round; `auto` (framework default) resolves to stateful for HTTP. Docker and the env example pin `stateful`. | `stdio` / `3010` / `stateful` |
227
229
  | `MCP_AUTH_MODE` / `MCP_LOG_LEVEL` / `STORAGE_PROVIDER_TYPE` / `OTEL_ENABLED` | Auth mode (`none` \| `jwt` \| `oauth`), log level, storage backend, OpenTelemetry. | `none` / `info` / `in-memory` / `false` |
228
230
  | `BRAPI_SESSION_ISOLATION` | When `true`, scope ServerRegistry connection state and the CanvasBridge default canvas to `ctx.sessionId` (HTTP stateful/auto). Concurrent callers under `MCP_AUTH_MODE=none` operate in isolated workspaces. Set `false` for the shared-workspace collaboration model. No effect on stdio. | `true` |
229
231
 
@@ -326,6 +328,7 @@ Defaults to HTTP transport, stateful session mode (engages the `mcp-session-id`
326
328
  - **Multi-user with institutional SSO.** `MCP_AUTH_MODE=jwt` (HS256, `MCP_AUTH_SECRET_KEY`) or `oauth` (JWKS, `OAUTH_ISSUER_URL` + `OAUTH_AUDIENCE`). Each user's `tid` claim carves a tenant — the outer scope. `BRAPI_SESSION_ISOLATION=true` (default) then sub-scopes inside each tenant for users running parallel sessions, and JWT/OAuth identity binding gives real session-hijack protection on top.
327
329
  - **One researcher, parallel agents.** If multiple agents (planner, analyst, writeup) connect as separate MCP clients but should share one workspace, set `BRAPI_SESSION_ISOLATION=false` and rely on shared trust. This is the shared-workspace shape.
328
330
  - **Stdio.** Always one session; isolation is moot. The flag has no effect.
331
+ - **Clients on MCP revision 2026-07-28.** Session-less by protocol, so they land in the shared tenant workspace whatever `BRAPI_SESSION_ISOLATION` says. Only the per-user-credentials shape isolates them.
329
332
 
330
333
  **Belt-and-braces under shared trust.** Even with `BRAPI_SESSION_ISOLATION=false`, `brapi_dataframe_describe` requires an explicit `dataframe` name on HTTP (no list-all enumeration), and `brapi_dataframe_query` rejects system-catalog reads (`information_schema`, `pg_catalog`, `sqlite_master`, `duckdb_*`). The dataframe name is the capability token; possession proves it.
331
334
 
@@ -0,0 +1,43 @@
1
+ ---
2
+ summary: "Adopts @cyanheads/mcp-ts-core ^0.12.3 (MCP SDK v2): every HTTP endpoint serves protocol revision 2026-07-28 alongside the 2025 era, undeclared tool arguments are rejected instead of stripped, and brapi_submit_observations' apply-mode confirmation becomes a multi-round-trip input_required result."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.7.11 — 2026-08-22
8
+
9
+ The wire-level changes below arrive with the framework and apply across the whole tool surface; this server changes no contract of its own.
10
+
11
+ ## Added
12
+
13
+ - **`brapi_dataframe_query` and `brapi_build_phenotype_matrix` disclose truncation** — `truncated` / `shown` / `cap` / `notice` in `structuredContent` and the `content[]` trailer. `dataframe_query`'s `cap` is the ceiling that actually bound, and the notice names which lever produced it (`preview` or `rowLimit`).
14
+
15
+ ## Changed
16
+
17
+ - **Undeclared top-level tool arguments are rejected by name** instead of silently stripped, and every `inputSchema` advertises `additionalProperties: false`. A nested `z.object()` still strips.
18
+ - **Advertised schemas are JSON Schema 2020-12**, and every `outputSchema` declares the error envelope — clients that validate `structuredContent` without checking `isError` stop rejecting error results.
19
+ - **Protocol revision 2026-07-28 is served on every HTTP endpoint**, alongside the 2025 era.
20
+ - **`brapi_submit_observations` `mode: apply` confirmation is a multi-round-trip `input_required` result.** A 2026-07-28 client re-invokes the tool with the answer; a 2025-era client is fulfilled through the SDK's legacy shim on its live session. `force: true` is unchanged.
21
+ - **Per-session isolation does not apply to 2026-07-28 clients** — that revision is session-less, so `ctx.sessionId` is undefined and those callers share the tenant workspace whatever `BRAPI_SESSION_ISOLATION` says. `MCP_SESSION_MODE` stays pinned `stateful`; `.env.example` and the README env row now carry the reason.
22
+ - **`ctx.log` lines also reach the client** as `notifications/message` at their RFC 5424 level — treat log text as client-visible.
23
+ - **Docker** — Bun 1.4.0 in both stages, the build stage pinned to `$BUILDPLATFORM` (Bun's JavaScriptCore aborts under emulated amd64), and the production install omits the framework's optional peer tiers. `bundle` packs via `npx -y @anthropic-ai/mcpb`.
24
+
25
+ ## Removed
26
+
27
+ - **`elicit_unavailable`** — apply mode no longer probes for client elicitation support, so the reason is gone from `brapi_submit_observations`' error contract. A declined, cancelled, or malformed answer returns `user_declined`.
28
+
29
+ ## Fixed
30
+
31
+ - **Singleton `GET` 404s log at debug**, not as incidents — every caller catches them and reports an absent record. Upstream status now reads from the canonical `error.data.status`.
32
+
33
+ ## Dependencies
34
+
35
+ - `@cyanheads/mcp-ts-core` ^0.10.14 → ^0.12.3
36
+ - `@duckdb/node-api` ^1.5.4-r.1 → ^1.5.5-r.4
37
+ - `@biomejs/biome` ^2.5.0 → ^2.5.9
38
+ - `@types/node` ^26.1.1 → ^26.2.0
39
+ - `tsc-alias` ^1.9.1 → ^1.9.2
40
+ - `typescript` ^6.0.3 → ^7.0.2
41
+ - `vitest` ^4.1.10 → ^4.1.11
42
+ - `packageManager` bun@1.3.14 → bun@1.4.0
43
+ - Skills and `scripts/` resynced from the framework.
@@ -0,0 +1,35 @@
1
+ ---
2
+ summary: "Adopts @cyanheads/mcp-ts-core ^0.13.0: empty and unsubstituted ${…} env values read as unset across every BRAPI_* path, async-search cancellation and poll deadlines classify as RequestCancelled and Timeout, and the framework skill tree moves out of the plugin-loaded skills/ directory."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.7.12 — 2026-09-13
8
+
9
+ A framework-adoption release; this server changes no tool contract of its own.
10
+
11
+ ## Changed
12
+
13
+ - **Plugin manifests declare their user config** — `.claude-plugin/plugin.json` gains a `userConfig` block, and it and `manifest.json` reference every option as `${user_config.<key>}`; `.codex-plugin/mcp.json` forwards `BRAPI_DEFAULT_BASE_URL` through `env_vars` rather than setting it to an empty string, which would replace a value the operator exported (mcp-ts-core 0.13.0).
14
+ - **Tool argument rejections classify as `InvalidParams` (-32602)** instead of `ValidationError`, carrying the same structured error envelope and message text (mcp-ts-core 0.12.7).
15
+ - **HTTP transport** — a trailing slash on `MCP_PUBLIC_URL` no longer doubles in advertised links (mcp-ts-core 0.12.8); the landing page's curl snippet sends a handshake `initialize` accepts, and a cancelled request's SSE stream closes immediately rather than at keep-alive (mcp-ts-core 0.12.9).
16
+ - **`MCP_RESPONSE_VERBOSITY` and `OAUTH_PROXY_*` are no longer recognized** by framework config (mcp-ts-core 0.12.8). Neither is set by this server's Docker image or `.env.example`.
17
+ - **Records logged from `setup()` survive startup** — buffered and replayed once the logger initializes, or drained to stderr when startup fails, instead of dropped (mcp-ts-core 0.12.5).
18
+ - **The framework skill tree moved from `skills/` to `framework-skills/`** — plugin hosts auto-load a root `skills/`, which handed this server's development skills to every installing agent. `.mcpbignore`, the agent protocol, and `docs/tree.md` follow the path; no runtime surface changed (mcp-ts-core 0.13.0).
19
+ - **Bun engines floor raised to `>=1.4.0`** (mcp-ts-core 0.12.9), and `audit:fix` runs `bun audit fix` — the previous script ran a read-only audit and changed nothing (mcp-ts-core 0.13.0).
20
+
21
+ ## Fixed
22
+
23
+ - **An unsubstituted `${…}` env value no longer registers a phantom alias or fails URL validation** — `BRAPI_<ALIAS>_*` credentials and the `BRAPI_*` server config both read empty strings and whole-value placeholders as unset through `parseEnvConfig`, so a builtin alias is no longer shadowed by its own unfilled placeholder. A URL containing a literal `${…}` segment is still preserved (mcp-ts-core 0.13.0).
24
+ - **A cancelled async search throws `RequestCancelled` (-32011) and an exhausted poll deadline throws `Timeout`** — they were `InternalError` and `ServiceUnavailable`. Neither issues a further poll, and both the `structuredContent.error` envelope and the `content[]` trailer carry the same message.
25
+ - **An upstream 500 reaches the client's reclassifier again** — no HTTP status maps to `InternalError` now, so a singleton `GET` that an upstream answers 500 for an unknown DbId maps to `NotFound` without retrying, an ordinary 500 retries, and a 501 carries `data.retryable: false` so an unimplemented method is not retried (mcp-ts-core 0.12.4).
26
+
27
+ ## Dependencies
28
+
29
+ - `@cyanheads/mcp-ts-core` ^0.12.3 → ^0.13.0
30
+ - `zod` ^4.4.3 → ^4.6.1
31
+ - `@biomejs/biome` ^2.5.9 → ^2.5.13
32
+ - `@types/node` ^26.2.0 → ^26.5.1
33
+ - `ignore` ^7.0.6 → ^7.0.9
34
+ - `tsc-alias` ^1.9.2 → ^1.9.4
35
+ - `vitest` ^4.1.11 → ^5.0.0
@@ -4,10 +4,11 @@
4
4
  # to author a new release. Set that file's H1 to `# <version> — YYYY-MM-DD`
5
5
  # with a concrete date.
6
6
 
7
- # Required. One-line GitHub Release-style headline. 350 character cap.
8
- # Default short and scannable. Don't pad, don't stitch unrelated changes with
9
- # semicolons pick the headline. Quotes required: unquoted YAML treats `: `
10
- # inside the value as a key separator and fails GitHub's strict parser.
7
+ # Required. One-line GitHub Release-style headline. 350 character cap — a
8
+ # ceiling, not a target. Default short and scannable. Don't pad, don't stitch
9
+ # unrelated changes with commas/semicolons into an inventory pick the
10
+ # headline, like a tag's theme line. Quotes required: unquoted YAML treats
11
+ # `: ` inside the value as a key separator and fails GitHub's strict parser.
11
12
  summary: ""
12
13
 
13
14
  # Set `true` when consumers must change code to upgrade: API removals,
@@ -24,9 +25,10 @@ security: false
24
25
 
25
26
  # Optional free-form notes for maintenance agents processing this release.
26
27
  # Not rendered in CHANGELOG — consumed by agents running `maintenance` on
27
- # downstream servers. Use for adoption instructions that don't fit the
28
- # human-facing sections: new files to create, fields to populate, one-time
29
- # migration steps. Omit the field entirely when there's nothing to say.
28
+ # downstream servers. ADOPTION STEPS ONLY new files to create, fields to
29
+ # populate, one-time migration steps. Never a second rendering of the body:
30
+ # if a body bullet already says it, name the bullet's symbol instead of
31
+ # re-explaining. Omit the field entirely when there's nothing to say.
30
32
  # agent-notes: |
31
33
  # <instructions for downstream maintenance agents>
32
34
  ---
@@ -41,17 +43,54 @@ security: false
41
43
  each bullet with the symbol or concept name in **bold** so they can skip
42
44
  what's irrelevant and zoom in on what's not.
43
45
 
44
- Tone: terse, fact-dense, not verbose. Default to one sentence per bullet —
45
- name the symbol, state what changed, stop. Use a second sentence only when
46
- it carries weight. If a bullet feels long, it is.
47
-
48
- Cut: mechanism walkthroughs (those belong in JSDoc, CLAUDE.md/AGENTS.md, or the
49
- relevant skill), ceremonial framings ("This release introduces…",
50
- backwards-compat paragraphs), file-by-file test enumerations, internal
51
- implementation notes. Prefer code/symbol names over English re-explanations.
46
+ Tone: terse, fact-dense, not verbose. Bullet shape: **symbol** + what
47
+ changed + at most one consumer-facing caveat. One sentence by default, two
48
+ when the second carries weight a bullet past ~40 words or three sentences
49
+ is wrong. The depth lives one hop away: the linked issue carries the why,
50
+ the commit diff carries the how. The changelog names what changed and what
51
+ a consumer does about it; a reader who wants mechanism opens the link.
52
+
53
+ Model length on THIS guide, never on the previous entry — entries modeled
54
+ on entries compound.
55
+
56
+ Cut (each has shipped as a wall of text; these are the cruft):
57
+ - History/justification narration — how the bug worked, why the old
58
+ behavior was wrong. One short clause at most; the issue carries the story.
59
+ - Design-rationale defense — "chosen over Y because…", "guarding the
60
+ getter is not enough…". That is the author arguing with a reviewer;
61
+ reviewers read the PR, not the changelog.
62
+ - Defensive unchanged-clauses — "X is unchanged", "byte-identical to
63
+ <prev>". Keep one only where its absence would cause a real misread,
64
+ as a short parenthetical.
65
+ - Edge-case inventories — marker lists, not-flagged lists, escape tables.
66
+ Tests and the issue carry those.
67
+ - Mechanism walkthroughs (JSDoc, CLAUDE.md/AGENTS.md, or the relevant
68
+ skill own those), ceremonial framings ("This release introduces…"),
69
+ backwards-compat paragraphs, file-by-file test enumerations. Prefer
70
+ code/symbol names over English re-explanations.
71
+
72
+ Verified ≠ included: the every-claim-verified-from-the-diff rule bounds
73
+ the TRUTH of what you write, never the AMOUNT.
74
+
75
+ Example — same fact, right size:
76
+
77
+ TOO LONG: **`fetchWithTimeout`'s `timeoutMs` bounds the whole exchange**
78
+ (#341). `fetch` resolves once headers arrive and the deadline was
79
+ cleared as the helper returned, so a peer that answered promptly and
80
+ then stalled the stream held the request open indefinitely. A 2xx
81
+ carrying a body now comes back as a passthrough wrapper that disarms
82
+ the deadline when the body closes, errors, or is cancelled; …
83
+ [+90 more words of mechanism and edge cases]
84
+
85
+ RIGHT: **`fetchWithTimeout`'s `timeoutMs` now bounds the whole
86
+ exchange, not just the headers** (#341). A stalled body aborts with
87
+ the same `Timeout` error; the returned `Response` is a wrapper, so
88
+ identity assertions (`toBe(response)`) no longer hold.
52
89
 
53
90
  Narrative intro: skip by default. Add one short sentence only when the
54
- release theme genuinely needs framing the bullets can't carry.
91
+ release theme genuinely needs framing the bullets can't carry. When many
92
+ bullets share one upgrade consequence, state it ONCE — intro line or
93
+ agent-notes — never per bullet.
55
94
 
56
95
  Sections: Keep a Changelog order — Added, Changed, Deprecated, Removed,
57
96
  Fixed, Security. Include only sections with entries; delete the rest
@@ -78,30 +117,13 @@ security: false
78
117
  in that unrelated item's metadata.
79
118
 
80
119
  TAG ANNOTATIONS — the annotated tag body renders as the GitHub Release body
81
- via `gh release create --notes-from-tag`. The tag is a derivative of this
82
- changelog entry a condensed, scannable version, not a copy. Format:
83
-
84
- <theme omit version number, GitHub prepends it>
85
- blank line
86
- <1-2 sentence context: what this release does>
87
- blank line
88
- Dependency bumps: ← section header
89
- ← blank line
90
- - `@cyanheads/mcp-ts-core` ^0.9.1 → ^0.9.6 ← bullet
91
- ← blank line
92
- Changed: ← only sections with entries
93
- ← blank line
94
- - `format()` output includes `query` in text mode
95
- ← blank line
96
- Added:
97
- ← blank line
98
- - `manifest.json` scaffolded for MCPB bundle support
99
- - Install badges (Claude Desktop, Cursor, VS Code)
100
- ← blank line
101
- <N> tests pass; `bun run devcheck` clean. ← footer
102
-
103
- Never a flat comma-separated string. Always structured markdown with
104
- sections. The tag must scan well as a rendered GitHub Release page.
120
+ via `gh release create --notes-from-tag`. It is a condensed digest of this
121
+ entry, never a copy, and its format is owned by the `release-and-publish`
122
+ skill (step 4, "Create the annotated tag"): the entry's `summary:` as the
123
+ theme line without the version, flat headline bullets — no Keep-a-Changelog
124
+ section headers, no gates line — at most one deps line, issue backlinks,
125
+ and the changelog link last. In release-PR mode the `git-wrapup` skill
126
+ authors that digest as the PR body's `## Changes` and the tag copies it.
105
127
  -->
106
128
 
107
129
  ## Added
@@ -26,7 +26,7 @@ export interface AliasCredentials {
26
26
  }
27
27
  /** Compute the env-var prefix for an alias. `my-server` → `BRAPI_MY_SERVER_`. */
28
28
  export declare function aliasEnvPrefix(alias: string): string;
29
- /** Read all `BRAPI_<ALIAS>_*` vars for an alias. Empty strings treated as unset. */
29
+ /** Read alias env vars; empty strings and whole-value host placeholders are unset. */
30
30
  export declare function readAliasCredentials(alias: string, env?: NodeJS.ProcessEnv): AliasCredentials;
31
31
  /**
32
32
  * Pick a `ConnectAuth` from the credential bundle. Returns `undefined` when no
@@ -1 +1 @@
1
- {"version":3,"file":"alias-credentials.d.ts","sourceRoot":"","sources":["../../src/config/alias-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,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;AAiBD,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;;;;;;;;;;;;;;;;GAgBG;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,CAwBtB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,KAAK,GAAG,SAAS,CAAC;CAC3B;AAID;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,eAAe,EAAE,CA2CjG;AAaD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,MAAM,CAmB9E"}
1
+ {"version":3,"file":"alias-credentials.d.ts","sourceRoot":"","sources":["../../src/config/alias-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,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;AA6BD,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,sFAAsF;AACtF,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,gBAAgB,CAalB;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;;;;;;;;;;;;;;;;GAgBG;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,CAwBtB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,KAAK,GAAG,SAAS,CAAC;CAC3B;AAID;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,eAAe,EAAE,CA4CjG;AAaD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,MAAM,CAmB9E"}