Sanity MCP

Live

API KEY

CONTENT MANAGEMENT

Design

Structured content documents, dataset queries, and schema-validated fields your agent needs to read and write live in Sanity. Sanity MCP gives your content agent per-user API access, credentials vaulted, scoped, never in the prompt.

  • Acts as the user: Each editor's Sanity token is scoped to their dataset permissions and project roles.
  • Credentials stay vaulted: AES-256, resolved at request time, never in LLM context.
  • Scoped before every call: User permissions enforced. 90-day audit trail on every document read and write.
Sanity MCP
agent · Acme Q3
Run
Find all blog posts published in the last 30 days missing an SEO description and add a generated one.
S
sanity_query
87ms
Content agent
14 posts found missing SEO descriptions. Generated and patched all 14 — descriptions written from post body, avg 148 characters, within 160-char limit.
Sources: Sanity blog dataset, post body text
sanitymcp
14
18:29
Message Claude...

Tools your content agent reaches for on Sanity, scoped per editor.

CALL ANY TOOL
API token scoped per editor. Every GROQ query and document mutation attributed to the authorizing user.
sanitymcp__get_ui_context
Get Ui Context
Get the current UI context including the active document and workspace in Sanity Studio.
Parameters
Name
Type
Required
Description
resource
object
Required
No description.
documentId
string
Optional
Sanity document ID (e.g. drafts.abc123 for drafts, or bare ID for published).
documentType
string
Optional
Schema type name of the document.
maxRefDepth
integer
Optional
Maximum reference depth to follow when resolving document references.
workspaceName
string
Optional
Sanity workspace name. Defaults to the default workspace.
sanitymcp_add_cors_origin
Add Cors Origin
sanitymcp_create_dataset
Create Dataset
sanitymcp_create_documents_from_json
Create Documents From Json
sanitymcp_create_documents_from_markdown
Create Documents From Markdown
sanitymcp_create_project
Create Project
sanitymcp_create_release
Create Release
sanitymcp_create_version
Create Version
sanitymcp_deploy_schema
Deploy Schema
sanitymcp_deploy_studio
Deploy Studio
sanitymcp_discard_drafts
Discard Drafts
sanitymcp_generate_image
Generate Image
sanitymcp_get_document
Get Document
sanitymcp_get_project_studios
Get Project Studios
sanitymcp_get_sanity_rules
Get Sanity Rules
sanitymcp_get_schema
Get Schema
sanitymcp_list_datasets
List Datasets
sanitymcp_list_embeddings_indices
List Embeddings Indices
sanitymcp_list_organizations
List Organizations
sanitymcp_list_projects
List Projects
sanitymcp_list_releases
List Releases
sanitymcp_list_sanity_rules
List Sanity Rules
sanitymcp_list_workspace_schemas
List Workspace Schemas
sanitymcp_migration_guide
Migration Guide
sanitymcp_patch_document_from_json
Patch Document From Json
sanitymcp_patch_document_from_markdown
Patch Document From Markdown
sanitymcp_publish_documents
Publish Documents
sanitymcp_query_documents
Query Documents
sanitymcp_read_docs
Read Docs
sanitymcp_search_docs
Search Docs

For more tools, view docs.

Build your Agent
Drop the toolkit in, point it at the authorized editor, and your agent can query and update structured content in Sanity from the first run.
Python · LlamaIndex
import { ScalekitClient } from "@scalekit-sdk/node";
import { DynamicStructuredTool } from "@langchain/core/tools";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { z } from "zod";

const sk = new ScalekitClient(envUrl, clientId, clientSecret);

const { tools } = await sk.tools.listScopedTools("user_123", {
filter: { connectionNames: ["contentfulmcp"], toolNames: ["contentful_entries_search", "contentful_entry_get", "contentful_entry_publish"] },
pageSize: 100,
});

const lcTools = tools.map((t) => new DynamicStructuredTool({
name: t.tool.definition.name,
description: t.tool.definition.description,
schema: z.object({}).passthrough(),
func: async (args) => {
const { data } = await sk.tools.executeTool({
toolName: t.tool.definition.name,
identifier: "user_123",
params: args,
});
return JSON.stringify(data);
},
}));

const agent = createReactAgent({ llm, tools: lcTools });
import { ScalekitClient } from "@scalekit-sdk/node";
import OpenAI from "openai";

const sk = new ScalekitClient(envUrl, clientId, clientSecret);
const openai = new OpenAI();

const { tools } = await sk.tools.listScopedTools("user_123", {
filter: { connectionNames: ["contentfulmcp"], toolNames: ["contentful_entries_search", "contentful_entry_get", "contentful_entry_publish"] },
pageSize: 100,
});

const llmTools = tools.map((t) => ({
type: "function",
function: {
name: t.tool.definition.name,
description: t.tool.definition.description,
parameters: t.tool.definition.input_schema,
},
}));

const resp = await openai.responses.create({
model: "gpt-4o", input: prompt, tools: llmTools,
});
import { ScalekitClient } from "@scalekit-sdk/node";
import Anthropic from "@anthropic-ai/sdk";

const sk = new ScalekitClient(envUrl, clientId, clientSecret);
const anthropic = new Anthropic();

const { tools } = await sk.tools.listScopedTools("user_123", {
filter: { connectionNames: ["contentfulmcp"], toolNames: ["contentful_entries_search", "contentful_entry_get", "contentful_entry_publish"] },
pageSize: 100,
});

const llmTools = tools.map((t) => ({
name: t.tool.definition.name,
description: t.tool.definition.description,
input_schema: t.tool.definition.input_schema,
}));

const msg = await anthropic.messages.create({
model: "claude-sonnet-4-6", max_tokens: 1024,
tools: llmTools,
messages: [{ role: "user", content: prompt }],
});
import { Agent } from "@google/adk/agents";
import {
MCPToolset, StreamableHTTPConnectionParams,
} from "@google/adk/tools/mcp";

const toolset = new MCPToolset({
connectionParams: new StreamableHTTPConnectionParams({
url: "https://mcp.scalekit.com/contentfulmcp",
headers: { Authorization: `Bearer ${userScopedToken}` },
}),
});

const agent = new Agent({
name: "agent", model: "gemini-2.0-flash",
tools: await toolset.getTools(),
});
Try these prompts
Paste any prompt into your content agent to start querying and updating structured content in Sanity MCP.
Search & recall
Copy the prompt
Copied
List all published blog posts in the [dataset] dataset from the last 30 days.
Copy the prompt
Copied
Get the full document for [document ID] including all references.
Copy the prompt
Copied
Run GROQ query: *[_type == "product" && inStock == true]
Create & update
Copy the prompt
Copied
Create a new blog post draft with title: [title] and body: [content].
Copy the prompt
Copied
Update the [field name] field on document [ID] to [new value].
Copy the prompt
Copied
Publish all documents of type [type] that are in draft status.
SEE HOW AUTH WORKS
Editors authorize Sanity once. Their API token stays vaulted, every mutation runs under their identity, and every change is logged.
1
Authorize
Your user connects
Sanity MCP
once. We tie it to their identity and the meetings they approved — no shared bot account, no org-wide access
Who:
user ‘A’
when:
Once per user
access:
Limited to user
2
Store
Their
Sanity MCP
token lives in a vault scoped to them. User A's meetings are never reachable by an agent acting for user B, even on the same connection
vault:
encrypted
scope:
per-user
tokens:
auto-refreshed
3
Resolve
When your agent calls a
Sanity MCP
tool, we fetch the right token server-side. It never touches your agent, never appears in the LLM context, never shows up in your logs
speed:
~40ms
check:
before every call
seen by:
nobody
4
Audit
Every
Sanity MCP
tool call is logged — who triggered it, which meeting was fetched, what came back. 90 days of history, tied to the user who authorized it
history:
90 days
export:
SIEM-ready
logged:
every call
Test other agents
Same per-user auth pattern across other CMS and content management connectors.
SALES
Outbound prospecting agent
Build targeted prospect lists with Apollo, enrich with firmographic data, and draft personalised outreach. Runs on a schedule.
SALES
Sales call prep agent
Pull Granola notes and Attio contact history to draft a pre-call brief before every sales meeting. Zero rep input.
ENGINEERING
Slack workflow agent (LangGraph)
LangGraph agent that drives multi-step Slack workflows: triggers, approvals, and follow-up actions per user identity.
Why Scalekit
Secure your agent's access. Connectors ship in minutes
One vault for every CMS connector. Sanity today, Webflow and Notion tomorrow.
01.
Shared tokens break per-user analytics
A shared token looks fine in a demo. In production every call looks like a service account. Scalekit resolves the real user credential so attribution, audit, and scope stay accurate.
// shared token
 audit → bot_service_account
 user_filter → broken

 // scalekit
 audit → user_abc
 scope → enforced ✓
02.
Authentication is not authorization
03.
Multi-tenancy is architectural
04.
Sanity MCP today. Others tomorrow.
“Our agents act across Salesforce, Gong, Google Drive, and more, on behalf of every customer. Scalekit behind the scenes meant we can keep adding tools without ever rebuilding how credentials or tool calling work.”
Venu Madhav Kattagoni
Head of Engineering / Von
FAQs
Frequently Asked Questions
Does the agent use a shared Sanity token or per-editor tokens?
Per-editor tokens. Each team member provisions their own Sanity API token and Scalekit vaults it under their identity. Document mutations are attributed to that editor, not a shared bot credential.
Where is the Sanity API token stored?
In Scalekit's AES-256 vault, namespaced per tenant. Keys resolve at request time and never appear in prompts, logs, or LLM completions.
Can I limit the agent to read-only Sanity access?
Yes. Use a read-only Sanity API token and/or listScopedTools to allow GROQ queries and document retrieval but block mutations for users who should not write to the dataset.
What happens when an editor's Sanity token is revoked?
The next tool call fails closed for that user. Other editors in the team remain unaffected. Revocation is logged with a timestamp.
Can the agent publish to Sanity and trigger a Webflow or Vercel deploy in one workflow?
Yes. A single agent can patch Sanity documents and trigger a Webflow CMS publish or a Vercel deploy hook in one workflow. Each connector resolves under the same editor identity with its own vaulted credential.
Start in your coding agent
Up and running in one command
Install the Scalekit skill in your editor of choice. Connector, auth, tools, prompt, all wired up
Claude Code REPL
/plugin marketplace add scalekit-inc/claude-code-authstack
/plugin install agentkit@scalekit-auth-stack
Cursor Code REPL
# ~/.cursor/mcp.json
{
""mcpServers"": {
""sanitymcp"": {
""url"": ""https://mcp.scalekit.com/sanitymcp"",
""headers"": { ""Authorization"": ""Bearer $SCALEKIT_TOKEN"" }
}
}
}
Codex Code REPL
# ~/.codex/config.toml
[mcp_servers.sanitymcp]
url = ""https://mcp.scalekit.com/sanitymcp""
auth_env = ""SCALEKIT_TOKEN""
Copilot Code REPL
# .vscode/mcp.json
{
""servers"": {
""sanitymcp"": {
""url"": ""https://mcp.scalekit.com/sanitymcp"",
""type"": ""http""
}
}
}