datasette-mcp 0.2 Switches Row Format to Fix Weak-Model Column Drift

September 2, 2026news
Open Weights

Simon Willison shipped datasette-mcp 0.2 on 1 September 2026, marking the plugin's first non-alpha release after enough personal production use to call it stable. The update adds a /-/mcp endpoint to any Datasette instance, exposing it as a Model Context Protocol server. The headline change is a deliberate restructuring of how SQL query results get serialised before they reach an LLM.

The core fix is deceptively small in description but significant in practice: the rows field returned by execute_sql switched from an array of arrays to an array of objects. That single schema decision determines whether a model receiving query output has to maintain an implicit column-index mapping in its context or can resolve column identity from the data itself.

Why array-of-arrays was a problem

In the previous format, a result set arrived as positionally ordered values — [[1, "Alice", "eng"], [2, "Bob", "mkt"]] — with column names carried separately. A model interpreting that payload must hold the column list and the row list simultaneously and keep their positional relationship intact across however many tokens the response spans. For capable frontier models this is manageable, but it compounds quickly when result sets are wide, when prompts are already long, or when a smaller model is in the loop. The failure mode is silent: the model maps a value to the wrong column and the downstream reasoning is subtly wrong without any error surfacing. This is exactly the class of context-management fragility that pipeline architecture rather than raw model capability has proven better at addressing systematically.

The object format and what it costs

The 0.2 format encodes each row as a JSON object: [{"id": 1, "name": "Alice", "dept": "eng"}, ...]. Column identity is now co-located with every value, so a model reading row three doesn't need to remember what was declared at the top of the payload. The tradeoff is token overhead — every key name is repeated for every row — which matters when the result set is large. Willison's release note frames the change explicitly as a concession to "weaker models," meaning the design optimises for broad compatibility rather than minimal token count. The serialisation contract between tool and model is a concrete architectural lever worth controlling at the tool layer rather than at the prompt layer.

Dependency floor and release status

The release pins mcp>=2.1.1 as a hard dependency, moving the plugin off whatever earlier MCP versions it previously tolerated. Any Datasette deployment incorporating datasette-mcp 0.2 needs to audit its MCP stack version before upgrading. Willison's confidence statement — that he has been using it in his own workflows — is the signal that the plugin has crossed from experimental to supportable, which changes how teams should treat it for production MCP pipelines.

Format comparison: pre-0.2 vs 0.2 row encoding

Attribute datasette-mcp <0.2 (array of arrays) datasette-mcp 0.2 (array of objects)
Row encoding [[val1, val2, val3], ...] [{"col1": val1, "col2": val2, ...}, ...]
Column identity Carried separately; positional mapping required Embedded per-value; no positional mapping needed
Token overhead per row Lower (no repeated key names) Higher (key name repeated each row)
Failure mode under weak models Silent column misattribution Structurally mitigated
MCP dependency Pre-2.1.1 compatible Requires mcp>=2.1.1
Release status Alpha First stable release

MCP tool authors are now actively designing payload schemas around model capability variance rather than assuming a capable frontier model on every call. As smaller and more specialised language models become common in cost-sensitive agentic stacks, serialisation format becomes a first-class engineering decision. datasette-mcp 0.2's schema change is a reproducible pattern: when positional context is fragile, embed identity in the payload and accept the token cost as the price of reliability.

Related Reading