---
summary: Error contracts catch up to wire reality — obsidian://vault, obsidian_append_to_note, obsidian_write_note declare failure reasons (path_forbidden, note_missing, no_active_file, periodic_*, section_target_missing) the service already throws.
breaking: false
---

# 3.1.4 — 2026-05-05

A discovery-side polish release: every failure mode the service already throws is now declared on the calling tool/resource, so the recovery hints reach the agent at `tools/list` and `resources/read` time rather than only after a failed call. New `tool-defs-analysis` skill bundles the audit pattern that surfaced the gaps. Adopts `@cyanheads/mcp-ts-core` 0.8.15.

## Added

- **`obsidian://vault/{+path}` resource — `errors[]` contract.** Declares `path_forbidden` (Forbidden) and `note_missing` (NotFound) so the resource's documented failure surface matches what `ObsidianService.getNote` already throws. Recovery hints flow to clients at `resources/list` time.
- **`obsidian_append_to_note` and `obsidian_write_note` — five additional declared reasons each.** `note_missing`, `no_active_file`, `periodic_not_found`, `periodic_disabled`, and `section_target_missing` were already raised by the service (and carried recovery hints via `ctx.recoveryFor`) but weren't part of the tool's published `errors[]` array. Now the agent sees the same recovery guidance at discovery time as at failure time.
- **`tool-defs-analysis` skill** — read-only audit of MCP definition language across tools, resources, and prompts. Walks every definition and checks 10 categories the LLM reads to decide whether and how to call: voice & tense, internal leaks, audience leaks, defaults, recovery hints, output descriptions, cross-references, sparsity, examples, structure. Complements `field-test` (behavior testing) and `security-pass` (security audit).

## Changed

- **`@cyanheads/mcp-ts-core` bumped from 0.8.13 to 0.8.15.** `disabledTool` is now re-exported from the package root, so `src/index.ts` imports it from `@cyanheads/mcp-ts-core` directly instead of the `/tools` subpath.
- **Heading-section nesting clarified.** `obsidian_get_note`, `obsidian_append_to_note`, and `obsidian_write_note` descriptions now spell out the `Parent::Child` syntax for nested heading targets and point at `format: "document-map"` for discovery.
- **`obsidian://vault/{+path}` description** drops the internal `NoteJson` type name in favor of "parsed note", and the `frontmatter` field's `.describe()` notes that values are strings, numbers, booleans, arrays, or nested objects rather than just calling them "an object".
- **Skills synced from upstream framework**: `api-canvas` adds the `spillover()` helper documentation; `add-tool`, `design-mcp-server`, and `api-errors` pick up the inline-contract-per-tool guidance; `api-workers` clarifies storage provider whitelist behavior. CLAUDE.md gains the matching note that error contracts stay inline on each tool rather than being extracted to a shared module.

## Fixed

- **`obsidian://status` description** swaps the path-revealing "authenticated probe to /vault/" wording for "authenticated request to the vault listing" — strips an internal endpoint leak from the LLM-facing surface.
