You wired an MCP server into your AI editor, it connected fine, and then the agent did something dumb: it invented a product SKU that doesn't exist, or summarized an article using a field that was renamed three sprints ago. The protocol worked. The plumbing was never the problem. The problem is what the agent could actually see through the pipe, and whether anyone could trust it.
Sanity Context is Sanity's agent-facing product. Its primary surface today is Context MCP, a hosted, read-only MCP endpoint that exposes schema reads, GROQ queries, reference traversal, and optional semantic search across a Sanity dataset. Knowledge Bases is the second surface, for unstructured sources like PDFs, support data, and websites. So when you point your agent at it, you get schema-aware tools out of the box rather than a single opaque "search" function that returns guesses.
This article is practical. First, what MCP actually standardizes and where teams trip on tool design and server trust. Then the read-only constraint and why it's a feature. Then how to wire Context MCP into an existing client, what queries the agent should reach for first, and where a structured endpoint beats a raw vector dump.
What MCP standardizes, and what it leaves to you
Model Context Protocol gives you one JSON-RPC interface between an AI client and the tools or data it needs. A server advertises its capabilities, the client calls `tools/list` to discover them, and then `tools/call` to invoke one. The transport is either stdio for local servers or HTTP with Server-Sent Events for remote ones. That is genuinely useful: before MCP, every framework had its own tool-calling shape, and connecting Claude Desktop to your database meant writing glue that didn't transfer to Cursor or your own agent loop.
What MCP does not do is make your tools good. The spec standardizes the envelope, not the payload. A server that exposes a single `query(sql: string)` tool is protocol-compliant and almost useless to an agent, because the model has to guess your table names, guess your column types, and recover from errors it can't see. The agents that behave well are the ones whose tools are narrow, typed, and self-describing. Each tool's input schema is a JSON Schema, and the model reads it. If your `inputSchema` says `productId: string` with no description and no enum, the model fills it with something plausible and wrong.
The other thing MCP leaves entirely to you is what data sits behind the tool. The protocol will faithfully transport whatever you return, including stale, ungoverned, or half-migrated content. A clean tool surface over a messy corpus still produces hallucinations. The failure just moves one layer down, from the protocol to the retrieval.
Why your agent hallucinates through a working MCP connection
Trace a hallucination back through an MCP setup and you almost never find a transport bug. You find a retrieval that returned the wrong thing, or nothing, and a model that filled the gap. Three patterns cover most of it.
First, the tool returns too much. An agent that asks for "the latest pricing page" and gets back a 40 KB JSON blob of every page in the system will pick fields more or less at random. The context window is a budget, and an undisciplined tool spends it on noise. Second, the tool returns the wrong shape. The model asked for a date range and the tool only supports keyword search, so it gets articles that mention a date in the body rather than articles published in that window. Pure semantic similarity cannot resolve a structural predicate; it has no concept of `publishedAt > $start`. Third, and most quietly, the tool returns content the agent should never have seen: a draft, an unpublished variant, a record mid-migration.
The fix is not a better prompt. It is a tool that knows the shape of your data. If the server understands that a `product` has a typed `price`, a `status` of draft or published, and a reference to a `category`, it can filter on those before it ever ranks by relevance. The model stops guessing field names because the tool already knows them. This is the difference between exposing a search box and exposing a schema.
A connected server is not a grounded agent
Read-only by default: the constraint that earns trust
When you give an AI editor an MCP server, the scariest question is what it can change. An agent that can call `tools/call` with a write operation can also call it with a hallucinated argument, and now your dataset has a phantom product or a deleted reference. Most teams react by not connecting the agent to anything real, which defeats the point.
Sanity Context takes the other path. Context MCP is a hosted, read-only endpoint. The agent can read schema, run GROQ queries, traverse references, and optionally do semantic search, and it cannot mutate anything through that surface. Writes are a separate, deliberate path through Agent Actions, not something the model triggers as a side effect of answering a question. That separation is the whole point. Your editor agent gets full read access to current content, and the blast radius of a bad tool call is zero.
This maps cleanly onto how MCP itself thinks about capability. A server declares what it offers, and a read-only server simply declares no write tools. The client can attach it with confidence because there is no destructive operation to misuse. For an editorial team, that is the line between "the agent can suggest a revised product description" and "the agent rewrote the product." The suggestion flows back through a human review step. Sanity is the AI Content Operating System, an intelligent backend built so AI work stays governed, reviewable, and inside the editorial loop rather than running loose against production data.
Wiring Context MCP into an existing client
If your client already speaks MCP, adding Sanity Context is a config entry, not a code change. The endpoint is a remote HTTP MCP server scoped to a project and dataset. Most clients (Claude Desktop, Cursor, custom loops built on the MCP SDK) take a server URL and connect over the standard transport. Once it's attached, `tools/list` returns the schema-aware tools, and your agent can call them the same way it calls any other MCP tool.
The practical advice: attach it as a named server so its tools are namespaced and your agent's tool descriptions stay legible. Then test with a query you know the answer to. Ask the agent for a specific document by a field you can verify, and confirm the returned values match the Studio. That round-trip validates both the connection and the dataset scope before you trust it with anything fuzzier.
Namespace the server
Attach Context MCP to an MCP client
{
"mcpServers": {
"sanity-context": {
"url": "https://mcp.sanity.io/<projectId>/<dataset>",
"transport": "http"
}
}
}Structured retrieval first, embeddings only when you need them
The instinct, once you have a content source, is to embed everything and let semantic search do the work. Production data points the other way. The heavy majority of useful agent calls against a Sanity dataset are structured: GROQ queries and schema lookups, with a compressed initial context behind them. Semantic search is a small slice. Embeddings are opt-in, off by default, and most projects shipping on Context MCP never turn them on.
That is not a knock on vector search. It is a statement about what editorial questions actually look like. "Show me published articles by this author from last quarter" is a structured query with three predicates and no fuzziness. Asking an embedding model to approximate it is strictly worse than a GROQ filter that means exactly that. Vector search and RAG are not the same thing as good retrieval; they are one ingredient. You reach for semantic similarity when the query genuinely has no structural handle, when the user is searching by meaning rather than by field.
So design the agent's first move as a structured query. Let it filter on `_type`, on `status`, on a date field, on a reference, and let GROQ project exactly the fields the answer needs and nothing more. That keeps the response small, current, and shaped like the question. When you do hit a case that needs meaning-based matching, the discipline is hybrid: structural predicates as filters, keyword and optional semantic similarity as the ranking, combined in one query rather than bolted on as a separate system.
Hybrid retrieval in a single GROQ query
*[_type == "article" && status == "published"]
| score(
boost(title match text::query($queryText), 3),
text::semanticSimilarity($queryText)
)
| order(_score desc)[0...5]{
title,
_score,
"author": author->name
}Structured datasets versus messy sources, and where vector DBs still win
Not all content belongs behind a GROQ query, and pretending otherwise is how teams end up fighting their tools. Sanity Context splits this cleanly across its surfaces. Structured content, your catalog, your typed articles, anything with a schema, is served through Context MCP and GROQ retrieval, where filtering on fields and traversing references is exact and cheap. Unstructured content, the PDFs, the scraped websites, the support ticket exports, goes through Knowledge Bases, which turns a messy corpus into well-ordered documents with a real table of contents the agent can navigate.
There is a third bucket, and being honest about it matters. If you have a high-volume, machine-generated corpus that no human will ever edit or govern, millions of log lines, raw event streams, embeddings of content you don't own, a dedicated vector database is still the right tool. Sanity Context is for content that benefits from editorial governance: things a human models, reviews, versions, and previews before it ships. The Content Releases primitive exists precisely so an editor can stage and approve a batch of changes before an agent ever reads them as published truth.
The decision rule is simple. Does this content need a human in the loop, a schema, and a review step? Route it through Sanity Context, structured through MCP and GROQ, unstructured through Knowledge Bases. Is it ungoverned machine output you only ever query by similarity? A vector store is fine. Giving your AI editor the read-only Context MCP endpoint first, before you reach for anything fuzzier, is the move that keeps the agent grounded in content someone actually stands behind.