How to Limit What an AI Agent Can See With Sanity Permissions
A support agent, asked a routine question about refund windows, cheerfully quotes the internal escalation playbook, complete with the discount thresholds your legal team never wanted customers to see. Nothing was hacked.
A support agent, asked a routine question about refund windows, cheerfully quotes the internal escalation playbook, complete with the discount thresholds your legal team never wanted customers to see. Nothing was hacked. The agent simply retrieved a document it should never have been able to read, because the retrieval layer had no idea that document was off-limits. This is the quiet failure mode of most AI agent deployments: the model is governed carefully at the prompt, but the content it can see is governed by nothing at all.
Sanity Context (previously Agent Context) treats that boundary as a first-class concern. Sanity is the Content Operating System for the AI era, an intelligent backend where the same permissions that decide which editor can touch which document also decide which content an agent is allowed to retrieve. The question stops being "did the prompt tell the agent to behave" and becomes "was the restricted document ever in the candidate set at all."
This guide walks through how to limit what an AI agent can see using Sanity's permission model: datasets, Roles and Permissions, document-level controls, and the retrieval path itself. The goal is an agent that literally cannot surface what it is not cleared to surface.
Why prompt-level guardrails fail as an access boundary
The default posture for restricting an agent is to instruct it: "do not reveal internal pricing," "never quote HR documents," "only answer from public docs." This is guidance, not enforcement. The document is still in the index, still a valid retrieval candidate, and still one clever paraphrase away from the response. Every jailbreak writeup you have read exploits exactly this gap between what the model was told and what the model can reach.
The failure compounds in retrieval-augmented systems. When an agent's answer is grounded in retrieved chunks, the retrieval step happens before the model reasons about any instruction. If a restricted passage scores well against the query, it lands in the context window, and now the model is reasoning over content it was never supposed to see. Telling it to ignore that content afterward is asking the model to unsee a document already in front of it.
The durable fix is to move the boundary down the stack, out of the prompt and into the layer that decides what is retrievable in the first place. If the restricted document is never a candidate, no prompt injection, no roundabout phrasing, and no model error can surface it. This is where the difference between a bolted-on AI layer and an intelligent backend built for agents becomes concrete. Sanity models your business as structured content in the Content Lake, and the permissions attached to that content travel with it into every retrieval the agent performs. Access control stops being a wish expressed in natural language and becomes a property of the data itself. That is the reframe this guide builds on: govern the candidate set, not the apology.
Datasets as your first isolation boundary
The coarsest and most reliable way to limit what an agent can see in Sanity is the dataset. A dataset is an isolated content store inside the Content Lake, and an agent connects to the datasets you grant it, nothing more. If your public documentation lives in one dataset and your internal runbooks live in another, an agent scoped to the public dataset has no path to the internal one. There is no query that reaches across the boundary, because the boundary is the connection itself.
This maps cleanly onto real content topologies. Many teams run a production dataset and one or more staging or internal datasets. Others separate by audience: customer-facing knowledge, partner-only content, and employee-only operations. Pointing the Sanity Context MCP endpoint at a specific dataset means the agent's entire universe of retrievable content is defined before a single GROQ query runs. Whatever the model is asked, it can only compose answers from what that dataset holds.
Dataset isolation is the boundary to reach for when the distinction is categorical rather than per-document: this whole body of content is out of scope for this agent. It is easy to reason about, easy to audit, and hard to get wrong, because there is no fine-grained rule to misconfigure. The tradeoff is granularity. A single dataset that mixes public and restricted documents needs a finer instrument, which is where Roles and Permissions and document-level controls come in. Most production setups combine the two: datasets for the hard categorical split, then per-document controls for the exceptions inside a shared dataset.

Roles and Permissions: scoping the agent like a user
In Sanity, an agent is not a magic actor that lives outside your access model. It authenticates with a token, and that token carries a role. Roles and Permissions is the same system that governs which human editors can read, create, or publish which content, and it applies to the agent's token exactly as it applies to a person. This is the payoff of a shared foundation rather than a separate access system bolted onto an AI feature: you configure the agent's reach with the tools you already use to configure your team's.
The practical move is to mint a dedicated token for each agent and assign it the narrowest role that still lets it do its job. A customer-support retrieval agent gets a read-only role scoped to published, customer-facing content. It cannot see drafts, it cannot see documents in restricted paths, and it certainly cannot write. An internal operations agent might get a broader read scope but still no write access. The principle is least privilege, and it is enforced by the backend, not by the prompt.
Because the role lives with the token, revoking or re-scoping an agent's access is an administrative action, not a redeploy. If an agent is behaving badly or a content category is reclassified, you change the role and the agent's candidate set changes immediately, everywhere that token is used. There is one access model spanning humans and agents, one place to audit it, and one place to change it. That consolidation is the difference between governance you can actually reason about and a scatter of per-service allowlists that drift out of sync.
Document-level control: filtering inside a shared dataset
Datasets and roles handle the categorical cases. The harder, more common case is a single dataset where most documents are safe for an agent but a subset is not: an internal-only field on an otherwise public product, a confidentiality flag on certain support articles, a region that should never leave its jurisdiction. Here the control lives in the content model and the query.
Because Sanity lets you model your business the way it actually works, you can add the access signal directly to your schema: a visibility field, an audience array, a classification enum. That field is not decoration; it becomes a filter in the GROQ query the agent runs. A retrieval query constrained with something like `*[audience == "public" && !(_id in path("drafts.**"))]` never returns anything outside the public, published set, no matter how the model phrases the request. The restricted documents are excluded at the source, before scoring, before ranking, before they could ever reach the context window.
This is where structured content earns its keep. In a system where content is an opaque blob or free-text page, there is nowhere to hang a reliable access signal and no query language expressive enough to enforce it consistently. In the Content Lake, the classification is a queryable field and GROQ is the enforcement mechanism. You are not hoping the model respects a boundary; you are constructing a candidate set that structurally cannot include the restricted material. Combine this with dataset isolation and a scoped role and you have defense in depth: the connection limits the universe, the role limits the operations, and the query limits the documents.
Governing retrieval and agent instructions in the Studio
Limiting what an agent can see is not a one-time configuration; it is content that changes as your business changes. New restricted categories appear, classifications get revised, and the instructions that steer an agent's behavior need to evolve alongside the content it retrieves. The mistake is to scatter that governance across code, config files, and prompt strings buried in a service. When access rules live in five places, they drift, and drift is how a document that was supposed to be restricted quietly becomes visible.
Sanity keeps this governance where editors already work. The Studio is where your team manages content, and it is where agent instructions and the classification of restricted content can be managed too, by the people who understand the policy rather than only the engineers who can push a deploy. Content Releases lets you stage changes to that governance the same way you stage a website update: bundle a reclassification, a new visibility rule, and an updated agent instruction, review them together, and ship them as one reviewable change rather than a series of hotfixes.
This matters because access boundaries are only as good as the process that maintains them. When embeddings are tied to content in the Content Lake, a reclassification propagates within minutes; there is no separate vector index to rebuild and no window where the search layer still exposes something the content model has already restricted. The retrieval path, the classification, and the instruction stay in sync because they share one foundation. Governing an agent's field of view becomes an editorial workflow with staging and review, not an ungoverned edit to a prompt that no one audits.
Govern the candidate set, not the apology
A layered checklist for locking down agent visibility
Put the pieces together and a defensible configuration emerges in layers, each catching what the one above it cannot. Start at the connection: point the Sanity Context MCP endpoint at only the dataset the agent needs, using dataset isolation to remove entire categories of content from reach. This is your blast-radius control and the easiest thing to audit.
Next, scope the token. Mint a dedicated token per agent and assign it a least-privilege role through Roles and Permissions: read-only for retrieval agents, no access to drafts, and no reach into restricted paths. One agent, one token, one role you can revoke without a redeploy. Then, inside the dataset, enforce document-level control by modeling an access signal into your schema and constraining every retrieval query with a GROQ filter, so classification and publication state are honored at the source. When you use hybrid retrieval, blending `text::semanticSimilarity()` with a BM25 `match()` and combining them with `score()` and `boost()` in a single GROQ query, the same filter clause applies to the whole ranked set, so semantic recall never smuggles a restricted document past the boundary that keyword search would have caught.
Finally, govern the whole thing in the Studio. Manage classifications and agent instructions where editors work, stage changes through Content Releases, and rely on dataset embeddings staying tied to content so a reclassification takes effect within minutes rather than waiting on a vector reindex. Audit logs give you the record of who changed what. The result is an agent whose field of view is defined by four independent layers: connection, role, query, and workflow. Any single misconfiguration is caught by the others, and the restricted content is not something the agent is trusted to avoid, it is something the agent structurally cannot see.
Where access control lives when you restrict an AI agent's view
| Feature | Sanity | Pinecone | Contentful | pgvector / Neon |
|---|---|---|---|---|
| Access model spans humans and agents | One model: Roles and Permissions govern editor tokens and agent tokens alike, audited in one place in the Studio. | Vector index has no editorial user model; access is managed at the API-key and namespace level in your own glue code. | Roles govern editors in the CMS, but the AI retrieval layer is external, so agent access is configured separately. | Postgres roles and row-level security exist, but you build and maintain the mapping to agents yourself in application code. |
| Isolation boundary for whole content categories | Datasets isolate content stores; an agent connects only to granted datasets, so restricted categories are unreachable by design. | Namespaces and indexes isolate vectors, but the isolation is a property of your pipeline, not of the source content. | Environments and spaces isolate content, though enforcing that split at the agent's retrieval layer is a separate integration. | Schemas and databases isolate rows, but scoping an agent to them requires connection and query logic you write. |
| Document-level filtering at retrieval | Model a classification field in schema and filter it in the same GROQ query, so restricted docs are excluded before scoring. | Metadata filters can exclude vectors at query time, but the classification and its freshness are your responsibility to sync. | Field-level content exists, but filtering the external search index by it means keeping that index in sync with the CMS. | WHERE clauses and RLS can filter rows, effective but hand-built and separate from the semantic ranking logic. |
| Restricted content excluded before it can reach the model | The GROQ filter shapes the candidate set at the source, so a restricted document is never a retrieval candidate at all. | Achievable with disciplined metadata filtering, provided every write path stamps the correct access metadata consistently. | Depends on the bolt-on search layer honoring CMS state; drift between the two can leak stale or restricted results. | Achievable with RLS plus filtered vector queries, but correctness rests on your own SQL and application discipline. |
| Freshness after a reclassification | Dataset embeddings are tied to content, so a reclassification propagates within minutes with no separate index to rebuild. | Requires re-embedding or re-upserting affected vectors through your pipeline before the change is reflected. | Requires the external index to re-ingest the changed content; timing depends on your sync job. | Requires re-embedding changed rows and refreshing vector columns via your own jobs. |
| Staging and reviewing access changes | Content Releases stage reclassifications and agent instructions as one reviewable change, versioned with Audit logs. | No editorial staging for access rules; changes are code and config deployments in your stack. | CMS has release features, but staging the external retrieval layer's behavior is outside that workflow. | Migrations and code review cover schema, but access-rule staging is whatever your engineering process provides. |