---
summary: "Source-agnostic `spillover()` helper exported from /canvas (#113); engine-agnostic `inferSchemaFromRows` lifted into canvas/core; api-canvas, add-tool, design-mcp-server skills updated."
breaking: false
---

# 0.8.15 — 2026-05-05

`spillover()` closes the most common pattern for canvas-coupled tools: pair an inline preview with a `DataCanvas` table holding the full source, so an agent gets a sized slice plus a SQL handle for the rest in one call. Source-agnostic — anything `registerTable` accepts (arrays, sync/async iterables, paginated upstream APIs wrapped as generators) flows through.

## Added

- **`spillover()`** ([#113](https://github.com/cyanheads/mcp-ts-core/issues/113)) — exported from `@cyanheads/mcp-ts-core/canvas`. Drains a tabular source up to a character budget for the inline preview, then — if the source exceeds the budget — registers the full source (preview rows + overflow sentinel + remaining tail) to a canvas table and returns a handle. Returns a discriminated union: `{ spilled: false, previewRows }` when the source fit, `{ spilled: true, previewRows, handle, truncated }` when the source overflowed. Caller controls the canvas (acquire once, spill many sources). `caps.maxRows` enforces a hard upper bound during the spill drain; `truncated` reports whether the source had more rows than the cap allowed. Best-effort `canvas.drop()` on register failure so aborted runs don't leave half-appended tables. `previewChars: 0` rejected — spillover always implies a visible preview; call `canvas.registerTable` directly for headless spills.
- **`inferSchemaFromRows(rows)`** — exported from `@cyanheads/mcp-ts-core/canvas`. Engine-agnostic schema inference over an in-memory row buffer. Powers `spillover()`'s default schema path; available standalone for callers that want to derive a `ColumnSchema[]` without going through `sniffSchema`'s iterable contract.
- **`SpilloverOptions`, `SpilloverResult`, `SpilloverFitResult`, `SpilloverSpillResult`** — types exported from `/canvas` for callers that wrap or compose the helper.

## Changed

- **`schemaSniffer` lifted from `services/canvas/providers/duckdb/` to `services/canvas/core/`.** `sniffSchema` (the iterable-buffering variant) now delegates to the new `inferSchemaFromRows` for the actual type inference. `DuckdbProvider` import path updated; engine-agnostic so future providers don't need to re-implement schema sniffing. Internal `unionToDuckdbType` renamed to `unionToColumnType` to match the engine-agnostic role.
- **`skills/api-canvas` (v1.1→1.2)** — new "Pattern: spillover" section with API surface, character-budget sizing table (10k tokens ≈ 40k chars, 25k ≈ 100k, 50k ≈ 200k), drain/overflow flow, schema-handling table, cancellation behavior, and "when not to use" notes.
- **`skills/add-tool` (v2.7→2.8)** — "Tabular working sets" bullet expanded to name `spillover()` explicitly, mention auto-register-on-overflow and the full-result distribution pattern.
- **`skills/design-mcp-server` (v2.9→2.10)** — new bullet under Output design ("Spill big tabular results to a queryable surface") pointing at `spillover()` for paginated APIs, streamed exports, and large query results. Reinforces computing aggregate signal across the full result, not the preview.
- **`AGENTS.md`/`CLAUDE.md` `/canvas` exports row** updated to list `spillover` and `inferSchemaFromRows`. `templates/AGENTS.md` and `templates/CLAUDE.md` skill table gained an `api-canvas` row so newly scaffolded servers see the helper alongside the rest of the canvas surface.
