v2.3.1
Mesh-LLM/mesh-llmv2.3.1Sep 14, 2026by krinart
Upgrading?
AI Summary
Patch release that improves performance for filtered Vortex scans, cache eviction, and query planning, while enhancing installation reliability and Cayenne capabilities. The release also updates support for the Model Context Protocol (MCP) to the 2026-07-28 revision.
Key Highlights
- Ordered Full Refreshes with sort_columns applied during table replacement
- CTE Materialization for multi-reference Common Table Expressions
- Faster Vortex Scans with selective filters and zone statistics
- Reusable Parameterized Plans to reduce logical plan cache overhead
- Cayenne Memory-Mode DML (INSERT, UPDATE, DELETE) support
New Features
- MCP 2026-07-28 support for server and client
- Installer and container reliability improvements
- Connector fixes for SMB, DuckDB regexp_match, and S3 warnings
- Asynchronous filesystem calls for Cayenne
- DuckDB and SQLite subquery fallback improvements
Full Release Notes
Spice v2.3.1 is a patch release that improves performance of filtered Vortex scans, cache eviction, and query planning. It also improves installation reliability, subquery fallback, numeric conversion, and Cayenne writes. The MCP server and client support the 2026-07-28 revision of the Model Context Protocol.
Highlights in v2.3.1 include:
- [**Ordered Full Refreshes**](#cayenne-applies-sort-columns-during-full-refreshes) — Cayenne applies `sort_columns` when a full refresh replaces a table
- [**CTE Materialization**](#cayenne-materializes-multi-reference-ctes) — Cayenne computes a multi-reference CTE once under `runtime.query.cte_materialization: auto`
- [**Faster Vortex Scans**](#faster-vortex-scans) — selective filters read fewer bytes, and zones skip unrelated scan splits
- [**Faster `IN` Lists**](#large-in-lists-run-faster) — Vortex uses a set probe for large constant lists
- [**Reusable Parameterized Plans**](#parameterized-queries-reuse-logical-plans) — all parameter values use the same cached plan without sharing results
- [**Reliable Cache Eviction**](#caching-accelerations-evict-reliably) — declared keys and timestamp ties do not prevent eviction
- [**Installer and Container Reliability**](#installer-and-container-reliability) — the installer validates each archive, and scratch images provide a writable temporary directory
- [**Cayenne Memory-Mode DML**](#cayenne-memory-mode-dml-works-as-expected) — `DELETE`, `UPDATE`, and `INSERT` is now supported when the acceleration uses the default in-memory mode
- [**MCP 2026-07-28**](#mcp-specification-2026-07-28) — the MCP server and client speak the new revision, and legacy `initialize` clients keep working
- [**Connector Fixes**](#connector-bug-fixes) — SMB share roots load on Samba, `regexp_match` answers faithfully on DuckDB, and S3 retry warnings read as one line
## What's New in v2.3.1
### Cayenne Applies `sort_columns` During Full Refreshes
Cayenne previously applied `sort_columns` only during compaction. A dataset with `refresh_mode: full` replaces its table, so it did not run that compaction. Cayenne now sorts the replacement stream before it writes the refreshed table, and the ordered files let min/max statistics exclude unrelated files from selective queries.
In the reported TPC-H SF1 benchmark, point-lookup p50 latency improved by 16%, and the same query used 37% less CPU. The sort adds work to each full refresh, so only datasets that configure `sort_columns` pay this cost. See [Full Refresh](https://spiceai.org/docs/features/data-acceleration/refresh-modes/full) for full-refresh configuration.
### Cayenne Materializes Multi-Reference CTEs
DataFusion inlines a `WITH` body when it binds a query, so it plans and executes a twice-referenced CTE twice. This release adds the setting `runtime.query.cte_materialization`, which accepts `disabled` and `auto`. The default is `disabled`, and it keeps the previous behavior.
Under `auto`, Cayenne computes an expensive multi-reference CTE once into a memory-accounted buffer, and each reference reads that buffer. A CTE body counts as expensive when it contains an aggregation, a join, a window function, a distinct, a sort, an unnest, or a union. A pass-through CTE stays inlined, so projection pushdown can still prune its columns. The setting has no effect on a query that does not scan a Cayenne-accelerated dataset.
```yaml
runtime:
query:
cte_materialization: auto # disabled (default) | auto
```
See the [Runtime reference](https://spiceai.org/docs/reference/spicepod/runtime) for the other query settings.
### Faster Vortex Scans
Vortex now waits for a pushed-down filter before it prepares projected column readers, so a split with no matching rows does not prepare those readers. Selective queries in the reported benchmarks read 45% to 58% fewer bytes, and bandwidth-constrained storage showed the largest latency improvements.
Vortex also checks each scan split against its zone statistics, and it returns an empty stream when the split cannot contain a matching row. In the reported TPC-H SF1 benchmark, point-lookup p50 latency improved by 12%, and CPU use per query fell by 53%. Nonselective scans do not use these shortcuts and remain unchanged within the reported run-to-run variation.
### Large `IN` Lists Run Faster
Vortex previously evaluated a constant `IN` list once for every row and every list item, so runtime grew with both dimensions. Vortex now builds a set once and probes it in one pass, and it uses value intervals to reject files before a scan.
A 32,768-value list over 1,048,576 rows fell from 44.57 seconds to 225 milliseconds in the reported benchmark. Small lists keep their existing evaluation path.
### Parameterized Queries Reuse Logical Plans
The logical-plan cache previously included each bound parameter value in its key, so each value tuple created another entry for the same SQL text. The cache now stores an unbound plan for each SQL statement, and Spice binds the current values after it reads the plan. The SQL results cache still includes parameter values in its keys, so a result only matches a query with the same values.
The reported point-lookup benchmark improved p50 latency by 12.4% and p99 latency by 31.7%, and parameterized traffic creates fewer duplicate entries. Indexed primary-key lookups now report an exact row count to the query planner, so DataFusion can avoid an unnecessary repartition for a single-row probe.
### Caching Accelerations Evict Reliably
A [`refresh_mode: caching`](https://spiceai.org/docs/features/data-acceleration/refresh-modes/caching) dataset can use size and row-count limits, and two defects could let the cache exceed these limits. A declared `primary_key` could expand the eviction key to the entire stored row, and an index marked as unique had the same fault. A `Map` column in the expanded key then made every sweep fail. Spice now keeps the group key limited to the request columns, and a refreshed entry also removes rows that disappeared from its latest response.
Many responses can share the same one-second fetch timestamp. The previous eviction predicate could then remove only 512 entries when this tie crossed the survivor boundary. The eviction sweep now combines a timestamp range with named boundary entries. The range removes the older bulk, so eviction converges under sustained ingestion.
### Federation and Runtime Improvements
- DuckDB and SQLite accelerations now handle more subqueries with `on_zero_results: use_source`. This includes mixed fallback policies and `ANY` or `ALL` comparisons.
- Decimal-to-float casts now preserve the expected rounded value after a federated aggregate. For example, a PostgreSQL average of `47.5` returns `47.5`.
- Cayenne uses asynchronous filesystem calls from its asynchronous code. A slow data directory does not block the Tokio runtime at these call sites.
### Installer and Container Reliability
The runtime installer now retries unsuccessful HTTP responses and invalid archives, and it accepts an archive only when it contains a regular `spiced` executable. The installer sends a configured token to GitHub release requests, and it does not send that token to runtime download hosts. On native Windows, `spice upgrade` now rejects the unsupported runtime upgrade instead of continuing with a partial upgrade.
The scratch-based standard and release images now provide a writable temporary directory at `/app/tmp`, which supports operations such as acceleration snapshot uploads.
### Cayenne Memory-Mode DML Works as Expected
A [Cayenne](https://spiceai.org/docs/components/data-accelerators/cayenne) acceleration uses `mode: memory` by default. Before this release, filtered DML statements did not change its in-memory rows as requested.
- A filtered `DELETE` reported zero affected rows and left each matching row in place.
- An `UPDATE` could create a duplicate live row for the same primary key.
- An `INSERT` over an existing primary key could keep both versions of the row.
`DELETE`, `UPDATE`, and `INSERT` now update the in-memory tier. The append path also enforces each declared `primary_key` according to its `on_conflict` setting. The [SQL DML reference](https://spiceai.org/docs/reference/sql/dml) lists the supported statements.
### Cayenne Uses Less Memory for Primary-Key Work
Cayenne now releases the keyset bytes that an abandoned primary-key checkout counted. A cancelled validation stream, or an apply that fails before the store, previously left a phantom reservation behind. That reservation counted against the table's memory account and the keyset ceiling until the next publish replaced the figure. Sibling tables then received a smaller keyset budget than their real use warranted. Cayenne always discarded the keys themselves, so no query returned wrong rows.
Cayenne also allocates less during primary-key validation and deletion filtering:
- The membership set for incoming keys holds only the precomputed digests.
- Conflict and bloom checks borrow the encoded row. Cayenne copies the key bytes only for a row that it retains.
- A scan reuses its row-encoding buffers between batches. The runtime charges each retained buffer to the query memory pool.
### MCP Specification 2026-07-28
Spice supports revision `2026-07-28` of the [Model Context Protocol](https://modelcontextprotocol.io/specification/2026-07-28/) on `/v1/mcp`. A modern client calls `server/discover` and sends per-request `_meta`, and it can call `tools/list` and `tools/call` without a prior `initialize` request. These requests carry no session, so the runtime mints no `Mcp-Session-Id`. A legacy client keeps its current behavior. It sends `initialize` and `notifications/initialized`, receives an `Mcp-Session-Id`, and sends that header on each later request. `GET /v1/mcp` and `DELETE /v1/mcp` stay legacy-only.
The runtime accepts every revision from `2024-11-05` to `2026-07-28`, and it advertises `2026-07-28`. An unknown version returns JSON-RPC error `-32022`, and `data.supported` lists the accepted revisions. A Streamable HTTP header that does not match the request body returns HTTP 400 and error `-32020`. The MCP client for stdio and Streamable HTTP catalogs prefers `server/discover` with `2026-07-28`. It falls back to `initialize` with `2025-03-26` when the server does not support the new revision.
`/v1/mcp` now checks the browser `Origin` header. Read [Breaking Changes](#breaking-changes) before you upgrade a remote browser client.
### Chunked Search Indexes Drop Superseded Chunks
A chunked search index stores one entry for each text chunk of a source row. A write upserts the chunks that the current text produces. Before this release, a row whose new text produced fewer chunks kept the extra chunks of the previous text. A search could return a row for text that the dataset no longer holds, and the runtime reported no error. An index that can enumerate its own entries now deletes the chunks that a shortened row no longer produces. A co-located index needs no cleanup, because its entries live in the accelerated row.
### Connector Bug Fixes
- **[SMB](https://spiceai.org/docs/components/data-connectors/smb)**: A dataset whose `from:` names the share root failed to load on Samba with `SMB error 0xC000000D`. The connector sent a CREATE request with an empty dynamic buffer, and Samba rejects a frame that declares dynamic bytes and carries none. The connector now pads an empty buffer with one zero byte, and the error message names `<share root>` for an empty path.
- **DuckDB**: A federated `regexp_match` call returned the whole match instead of the capture groups. A row with no match returned `['']` instead of NULL, so `IS NULL` answered `false`. The DuckDB dialect rewrote the call to `regexp_extract`, which answers a different question. Spice now evaluates `regexp_match` above the federated scan with the DataFusion implementation.
- **S3**: A connection that closed before the response headers arrived printed a multi-line OpenDAL diagnostic at `WARN` level. The runtime now prints one line that names the file, the scheduled retry, the cause, and the action to take. Set `opendal::layers::retry=debug` for the full diagnostic. The retry policy is unchanged.
### Dependency Updates
Compared with v2.3.0, this release changes the following versions:
| Dependency / Component | v2.3.0 | v2.3.1 |
| ---------------------- | ------ | ------ |
| **rmcp** | v1.5.0 | v3.3.0 |
DataFusion remains at v54.1.0, Arrow remains at v58.3.0, and Vortex remains at v0.79.0. Spice updates the Arrow and DataFusion fork revisions for the Decimal-to-Float rounding fix and the scan improvements described above.
## Contributors
- [@bjchambers](https://github.com/bjchambers)
- [@Jeadie](https://github.com/Jeadie)
- [@krinart](https://github.com/krinart)
- [@lukekim](https://github.com/lukekim)
- [@peasee](https://github.com/peasee)
- [@phillipleblanc](https://github.com/phillipleblanc)
## Breaking Changes
`/v1/mcp` now checks the browser `Origin` header against `runtime.cors.allowed_origins`. A request with an `Origin` that is not on the list receives HTTP 403. The default value `["*"]`, and an empty list, expand to the localhost origins. A client that sends no `Origin` header, or a localhost `Origin`, still passes, so most desktop MCP clients are unaffected.
If a remote browser client calls `/v1/mcp`, set an explicit list before you upgrade:
```yaml
runtime:
cors:
enabled: true
allowed_origins:
- 'https://app.example.com'
```
CORS on the other HTTP endpoints is unchanged. `runtime.mcp.allowed_hosts: ["*"]` still disables the `Host` check.
A modern MCP client must send the headers `MCP-Protocol-Version`, `Mcp-Method`, and, for `tools/call`, `Mcp-Name`. It must also send per-request `_meta`. An argument that carries the `x-mcp-header` annotation needs a matching `Mcp-Param-*` header. These requirements come from the 2026-07-28 specification. A legacy client that sends `initialize` needs no change.
## Cookbook Updates
No new cookbook recipes.
The [Spice Cookbook](https://spiceai.org/cookbook) includes more than 104 recipes to help you get started with Spice quickly and easily.
## Upgrading
To upgrade to v2.3.1, use one of the following methods:
**CLI**:
```console
spice upgrade
```
**Homebrew**:
```console
brew upgrade spiceai/spiceai/spice
```
**Docker**:
Pull the `spiceai/spiceai:2.3.1` image:
```console
docker pull spiceai/spiceai:2.3.1
```
For available tags, see [DockerHub](https://hub.docker.com/r/spiceai/spiceai/tags).
**Helm**:
```console
helm repo update
helm upgrade spiceai spiceai/spiceai --version 2.3.1
```
**AWS Marketplace**:
Spice is available in the [AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-jmf6jskjvnq7i).
## What's Changed
### Changelog
- fix(duckdb): deny the regexp built-ins DuckDB cannot answer faithfully (fixes #13809) by [@claudespice](https://github.com/claudespice) in [#13871](https://github.com/spiceai/spiceai/pull/13871)
- feat(cayenne): materialize multi-reference CTEs on the query path by [@lukekim](https://github.com/lukekim) in [#13918](https://github.com/spiceai/spiceai/pull/13918)
- fix(cayenne): release the keyset bytes an abandoned PK checkout accounted (fixes #13668) by [@grokspice](https://github.com/grokspice) in [#13925](https://github.com/spiceai/spiceai/pull/13925)
- fix(search): drop the chunks a shortened row no longer produces from a chunked index (refs #13717) by [@claudespice](https://github.com/claudespice) in [#13960](https://github.com/spiceai/spiceai/pull/13960)
- fix(arrow): report an exact row count from the indexed point-lookup scan by [@krinart](https://github.com/krinart) in [#13972](https://github.com/spiceai/spiceai/pull/13972)
- fix(caching): keep a declared key from disabling eviction and stranding stale rows (fixes #13976) by [@bjchambers](https://github.com/bjchambers) in [#13992](https://github.com/spiceai/spiceai/pull/13992)
- Reduce Cayenne allocations during primary-key validation and filtering by [@lukekim](https://github.com/lukekim) in [#14009](https://github.com/spiceai/spiceai/pull/14009)
- fix(deps): bump arrow-rs to correctly-rounded Decimal→Float cast (closes #13978) by [@Jeadie](https://github.com/Jeadie) in [#14012](https://github.com/spiceai/spiceai/pull/14012)
- fix(caching): partition doomed entries at the survivor cutoff so eviction converges (closes #13994) by [@Jeadie](https://github.com/Jeadie) in [#14021](https://github.com/spiceai/spiceai/pull/14021)
- Fix subqueries with use_source acceleration by [@phillipleblanc](https://github.com/phillipleblanc) in [#14022](https://github.com/spiceai/spiceai/pull/14022)
- fix: restore OSS installer, CLI and test workflow coverage by [@phillipleblanc](https://github.com/phillipleblanc) in [#14025](https://github.com/spiceai/spiceai/pull/14025)
- perf(vortex): defer projection setup on filtered scans until the filter resolves by [@bjchambers](https://github.com/bjchambers) in [#14035](https://github.com/spiceai/spiceai/pull/14035)
- fix: clarify OpenDAL S3 retry warnings by [@lukekim](https://github.com/lukekim) in [#14040](https://github.com/spiceai/spiceai/pull/14040)
- feat(mcp): support MCP specification 2026-07-28 (dual-era) by [@lukekim](https://github.com/lukekim) in [#14043](https://github.com/spiceai/spiceai/pull/14043)
- fix(cayenne): make DELETE, UPDATE and INSERT work on a `mode: memory` acceleration (fixes #12008) by [@bjchambers](https://github.com/bjchambers) in [#14047](https://github.com/spiceai/spiceai/pull/14047)
- fix(deps): bump arrow-rs fork pin for Decimal->Float rounding fix by [@Jeadie](https://github.com/Jeadie) in [#14049](https://github.com/spiceai/spiceai/pull/14049)
- fix(smb): pad an empty CREATE buffer so Samba lists the share root (fixes #13293) by [@grokspice](https://github.com/grokspice) in [#14050](https://github.com/spiceai/spiceai/pull/14050)
- perf(vortex): answer a constant IN list by probing a set, and falsify it by interval by [@bjchambers](https://github.com/bjchambers) in [#14061](https://github.com/spiceai/spiceai/pull/14061)
- fix(cayenne): apply `sort_columns` with `refresh_mode: full` by [@peasee](https://github.com/peasee) in [#14063](https://github.com/spiceai/spiceai/pull/14063)
- perf(vortex): skip a scan split whose zones cannot satisfy the filter by [@peasee](https://github.com/peasee) in [#14064](https://github.com/spiceai/spiceai/pull/14064)
- perf(cache): key the logical-plan cache on SQL text, not parameter values by [@bjchambers](https://github.com/bjchambers) in [#14069](https://github.com/spiceai/spiceai/pull/14069)
- fix(cayenne): move accelerator filesystem I/O off Tokio workers by [@lukekim](https://github.com/lukekim) in [#14073](https://github.com/spiceai/spiceai/pull/14073)
- fix: Provide temporary directory in docker images by [@Jeadie](https://github.com/Jeadie) in [#14089](https://github.com/spiceai/spiceai/pull/14089)
**Full Changelog**: [https://github.com/spiceai/spiceai/compare/v2.3.0...v2.3.1](https://github.com/spiceai/spiceai/compare/v2.3.0...v2.3.1)
Related Repositories
A cross-platform desktop All-in-One assistant for Claude Code, Codex, OpenCode, OpenClaw, Grok Build & Hermes Agent. Only official website: ccswitch.io
An open-source remote desktop application designed for self-hosting, as an alternative to TeamViewer.
Build smaller, faster, and more secure desktop and mobile applications with a web frontend.
π RuView turns commodity WiFi signals into real-time spatial intelligence, vital sign monitoring, and presence detection — all without a single pixel of video.
Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one