Skip to main content
Glama

šŸ”® Sanity MCP Server

The self-hosted Sanity MCP that Sanity deprecated. Full CRUD, atomic transactions, reference tracking, and tools the official server doesn't have.

npm version License: MIT


Why This Exists

Sanity's official MCP server (@sanity/mcp-server) is archived. They want you to use their hosted solution at mcp.sanity.io with OAuth.

That's fine if you want:

  • OAuth flows for every AI tool

  • Dependency on Sanity's infrastructure

  • No offline or air-gapped usage

  • Whatever tools they decide to expose

This MCP gives you:

  • šŸ”‘ Simple token auth — one env var, done

  • šŸ  Self-hosted — runs anywhere, no external dependencies

  • šŸ› ļø More tools — reference tracking, diff, history, bulk ops

  • ⚔ Works offline — no OAuth dance, no hosted service required


Related MCP server: Agentic CMS

Quick Start

npx @purple-horizons/sanity-mcp

Or install globally:

npm install -g @purple-horizons/sanity-mcp

MCP Configuration

Add to your Claude Desktop, Cursor, or VS Code config:

{
  "mcpServers": {
    "sanity": {
      "command": "npx",
      "args": ["@purple-horizons/sanity-mcp"],
      "env": {
        "SANITY_PROJECT_ID": "your-project-id",
        "SANITY_DATASET": "production",
        "SANITY_TOKEN": "sk-your-token"
      }
    }
  }
}

Tools

šŸ“– Read Operations

Tool

Description

sanity_query

Execute any GROQ query

sanity_get_document

Fetch a single document by ID

sanity_list_documents

List documents by type with pagination

sanity_search

Full-text search across content

sanity_get_types

Discover all document types

sanity_get_type_info

Get schema info for a type

sanity_count

Count documents matching a filter

āœļø Write Operations

Tool

Description

sanity_create

Create a new document

sanity_update

Replace an entire document

sanity_patch

Partially update specific fields

sanity_delete

Delete a document

sanity_publish

Publish a draft

sanity_unpublish

Move published to draft

šŸš€ Unique Tools (Not in Sanity's Official MCP)

Tool

Description

sanity_references

Find all documents referencing a given doc — essential before deleting

sanity_diff

Compare two documents — draft vs published, or any two docs

sanity_history

Get revision history — see who changed what

sanity_bulk

Atomic batch operations — all succeed or all fail

sanity_draft_status

Check publish state — draft, published, or both


What Makes This Better

1. Reference Tracking

Before you delete that image asset, you probably want to know what's using it:

sanity_references id="image-abc123"
→ Shows all 47 blog posts using that image

Sanity's official MCP doesn't have this. You'd find out the hard way.

2. Document Diffing

Content editor made changes. What changed?

sanity_diff idA="drafts.post-xyz" idB="post-xyz"
→ Shows exactly which fields differ

See the diff before you publish. Or compare any two documents.

3. Atomic Bulk Operations

Update 50 documents and they all need to succeed together? One transaction:

sanity_bulk operations=[
  { "patch": { "id": "post-1", "set": { "featured": true }}},
  { "patch": { "id": "post-2", "set": { "featured": false }}},
  ...
]
→ All or nothing. No partial states.

With dryRun: true, validate before executing.

4. Draft Status at a Glance

Is there a draft? Is it published? Both?

sanity_draft_status id="post-abc123"
→ { status: "both", hasUnpublishedChanges: true }

No more manually checking drafts.{id} vs {id}.


Environment Variables

Variable

Required

Default

Description

SANITY_PROJECT_ID

āœ…

—

Your Sanity project ID

SANITY_DATASET

āŒ

production

Dataset name

SANITY_TOKEN

āŒ

—

API token (required for writes)

SANITY_API_VERSION

āŒ

2024-01-20

API version

Getting Your Token

  1. Go to sanity.io/manage

  2. Select your project → API → Tokens

  3. Add new token with Editor or higher permissions

  4. Copy and set as SANITY_TOKEN


GROQ Examples

// All posts, newest first
*[_type == "post"] | order(_createdAt desc)

// Specific post by slug
*[_type == "post" && slug.current == "hello-world"][0]

// Posts with expanded author
*[_type == "post"]{
  title,
  slug,
  "author": author->name,
  "category": category->title
}

// Count by category
{
  "total": count(*[_type == "post"]),
  "published": count(*[_type == "post" && !(_id in path("drafts.**"))])
}

// Full-text search
*[_type == "post" && title match "AI*"]

Development

# Clone
git clone https://github.com/Purple-Horizons/sanity-mcp.git
cd sanity-mcp

# Install
npm install

# Build
npm run build

# Test
npm test

# Run locally
npm run dev

Comparison

Feature

This MCP

Sanity Official

Self-hosted

āœ…

āŒ (archived)

Simple token auth

āœ…

OAuth only

Works offline

āœ…

āŒ

Reference tracking

āœ…

āŒ

Document diff

āœ…

āŒ

Bulk transactions

āœ…

āŒ

Draft status

āœ…

āŒ

Revision history

āœ…

āŒ

Schema discovery

āœ…

āœ…

Full CRUD

āœ…

āœ…

GROQ queries

āœ…

āœ…

Release management

āŒ

āœ…

Semantic search

āŒ

āœ…

tl;dr: We're better for self-hosting, developer tooling, and content operations. They're better if you need releases and semantic search with embeddings.


License

MIT Ā© Purple Horizons


Available Tools

18 tools
sanity_bulkA

Execute multiple operations in a single atomic transaction. All succeed or all fail together.

Inputs: operations (required), dryRun.

sanity_countB

Count documents matching a GROQ filter

Inputs: filter (required), params.

sanity_createB

Create a new document in Sanity CMS. Requires write token.

Inputs: _type (required), document (required), _id.

sanity_deleteB

Delete a document. Requires write token.

Inputs: id (required).

sanity_diffA

Compare two documents and see what fields differ. Useful for comparing draft vs published or two versions.

Inputs: idA (required), idB (required).

sanity_draft_statusA

Check if a document has unpublished changes. Shows both draft and published versions if they exist.

Inputs: id (required).

sanity_get_documentB

Get a single Sanity document by its ID

Inputs: id (required).

sanity_get_type_infoA

Get information about a document type including field names and document count

Inputs: type (required).

sanity_get_typesA

Get all document types in the Sanity dataset

Inputs: no named parameters.

sanity_historyB

Get the revision history of a document. See who changed what and when.

Inputs: id (required), limit.

sanity_list_documentsA

List documents of a specific type with pagination

Inputs: type (required), limit, offset, order.

sanity_patchC

Partially update a document. Requires write token.

Inputs: id (required), set, unset, inc, +1 more.

sanity_publishA

Publish a draft document (moves from drafts.* to published). Requires write token.

Inputs: id (required).

sanity_queryB

Execute a GROQ query against Sanity CMS. GROQ is a query language similar to GraphQL but designed for JSON documents. Examples: *[_type == "post"] gets all posts, *[_type == "post" && slug.current == "my-post"][0] gets a specific post.

Inputs: query (required), params.

sanity_referencesA

Find all documents that reference a given document. Essential before deleting to avoid broken references.

Inputs: id (required), limit.

sanity_unpublishA

Unpublish a document (moves to drafts.*). Requires write token.

Inputs: id (required).

sanity_updateA

Replace an entire document. Requires write token.

Inputs: id (required), document (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 18 tools

    Disambiguation5/5

    Every tool has a clearly distinct purpose: create, update, patch, delete, publish, unpublish, query, search, list, get, diff, history, references, etc. No two tools overlap in functionality.

    Naming Consistency5/5

    All tools follow a consistent 'sanity_verb_noun' pattern using snake_case, e.g., sanity_create, sanity_publish, sanity_list_documents. No mixed conventions.

    Tool Count4/5

    18 tools is slightly above the ideal 3-15 range, but the scope of a CMS (CRUD, publishing, search, history, diffs) justifies the count. Still reasonable.

    Completeness5/5

    The toolset covers the full document lifecycle: CRUD, patch, publish/unpublish, query, search, history, diffs, references, bulk operations, and type introspection. No obvious gaps.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers