See AI content operations in action at Braze. Join the live session April 14th

Connecting LLMs to your CMS

LLMs are useful. Copy-pasting between a chat window and your CMS is not. This guide shows how to connect LLMs directly to Sanity using two patterns: reading your structured content to ground model responses (RAG), and writing data back, schema-aware and staged for human review

  • Knut Melvær

    Principal Developer Marketing Manager

Published:

As LLMs become capable enough to use in production, the best integrations aren't AI in a separate browser tab — they're AI wired directly into the editorial workflow. An editor clicks a button in Sanity Studio. An agent reads the document, generates a meta description, and writes it back to the correct field. No copying, no pasting, no reformatting.


But a direct pipeline (user asks question, app sends prompt to LLM, LLM responds, app displays result) is fragile on its own. LLMs hallucinate. They invent facts, use outdated information, and ignore your brand guidelines. To make an LLM useful in production, you have to ground it in your actual data.

For most businesses, that data lives in the CMS.

Legacy CMSes make this hard. Content is locked in HTML blobs behind slow REST APIs. Sanity is different: content is stored as structured JSON in the Content Lake, queryable with GROQ, and writable through a well-defined API. That makes it a natural integration point for LLMs.

The problem with disconnected AI

When AI lives in a separate window, your content operations pay for it in a few ways:

Context loss. The LLM doesn't know your brand guidelines, existing content structure, or taxonomy. You're prompting a generic model, not one that understands your business.

Manual overhead. Editors waste time copying prompts, pasting results, and reformatting output into CMS fields. This isn't automation — it's extra work.

Governance risk. Pasting draft content into public AI chat interfaces can violate data policies, especially for regulated industries.

API-to-API integration solves all three. By connecting LLM APIs directly to your CMS, you turn AI from a generic tool into something that knows your content model, operates within your workflow, and never leaves your infrastructure.

Because Sanity Studio is a customizable React application backed by a real-time API, you can build AI tooling right where editors already work.

The two patterns

There are two ways LLMs and Sanity interact:

Sanity as context provider (RAG). The LLM reads data from Sanity to answer questions or generate content grounded in your actual data.

Sanity as action target (agentic workflows). The LLM writes data back to Sanity — updating documents, generating structured content, or triggering workflows.

Both patterns rely on the same technical foundation: the Content Lake for structured storage, GROQ for precise querying, and the Actions API for writing data.

Pattern 1: Sanity as context provider

When you want an LLM to answer questions based on your company's data — a customer support bot, an internal knowledge base, a product Q&A — you need to feed it structured context. This is Retrieval-Augmented Generation (RAG).

Step 1: Fetch precise context with GROQ

If a user asks "What is the return policy for the SuperRunner shoe?", you don't want to send the LLM the entire HTML of your website. You want to send it the exact return policy for that specific product.

GROQ lets you fetch exactly that:

import { createClient } from '@sanity/client';

const client = createClient({
  projectId: 'your-project-id',
  dataset: 'production',
  apiVersion: '2026-01-01',
  useCdn: true,
});

async function getProductContext(productName: string) {
  const query = `*[_type == "product" && name match $name][0] {
    name,
    price,
    "returnPolicy": returnPolicy->body,
    "features": features[].text
  }`;

  return await client.fetch(query, { name: productName });
}

The projection ({ name, price, ... }) ensures you only send the LLM the fields it needs. Fewer tokens, lower cost, better responses.

Step 2: Serialize Portable Text for the LLM

Sanity stores rich text as Portable Text, a structured JSON format. Before sending it to an LLM, serialize it to plain text:

import { toPlainText } from '@portabletext/react';

async function buildSystemPrompt(productName: string) {
  const context = await getProductContext(productName);
  const returnPolicyText = toPlainText(context.returnPolicy);

  return `
    You are a helpful customer support agent.
    Answer the user's question using ONLY the following product context:

    Product: ${context.name}
    Price: $${context.price}
    Features: ${context.features.join(', ')}
    Return Policy: ${returnPolicyText}
  `;
}

This system prompt becomes the grounding context for every LLM call. The model can't hallucinate a return policy it didn't receive.

Step 3: Call the LLM API

With context prepared, call Claude or any other LLM API:

import Anthropic from '@anthropic-ai/sdk';

const anthropic = new Anthropic();

async function generateResponse(userQuery: string, productName: string) {
  const systemPrompt = await buildSystemPrompt(productName);

  const response = await anthropic.messages.create({
    model: 'claude-sonnet-4-20250514',
    max_tokens: 1024,
    system: systemPrompt,
    messages: [{ role: 'user', content: userQuery }],
  });

  return response.content[0].type === 'text' ? response.content[0].text : '';
}

Pattern 2: Sanity as action target

The more powerful pattern is letting the LLM take action. Instead of generating text for a human to copy, it writes directly back to Sanity — schema-aware, draft-safe, and staged for human review.

The recommended approach here is Agent Actions, Sanity's built-in, schema-aware AI API. It handles draft creation, schema validation, and structured output without you writing mutation code by hand.

Using Agent Actions

Agent Actions understand your content model. When you ask them to generate or transform content, they validate the output against your schema and write to drafts by default — never directly to published documents.

import { createClient } from '@sanity/client';

const client = createClient({
  projectId: 'your-project-id',
  dataset: 'production',
  apiVersion: 'vX',
  token: process.env.SANITY_API_TOKEN,
});

// Generate an SEO meta description from the document body
await client.agent.action.generate({
  schemaId: 'sanity.workspace.schema.default',
  documentId: 'article-123',
  instruction: `
    Write a concise meta description for this article.
    Max 150 characters. Focus on the main topic and value to the reader.
  `,
  target: { path: 'seo.description' },
});

Agent Actions also support transform (modify existing content) and translate (localization-aware translation). They run from Studio, Compute, your frontend, or any code that can call an API.

Tool calling for custom workflows

For cases where Agent Actions don't cover the full workflow — or when you're integrating with OpenAI or other models — you can use tool calling to let the LLM decide when and how to write back to Sanity.

Define the tool:

const tools = [
  {
    type: 'function',
    function: {
      name: 'update_seo_metadata',
      description: 'Updates the SEO title and description for a specific document in the CMS.',
      parameters: {
        type: 'object',
        properties: {
          documentId: {
            type: 'string',
            description: 'The Sanity document ID (_id)',
          },
          seoTitle: {
            type: 'string',
            description: 'The optimized SEO title (max 60 characters)',
          },
          metaDescription: {
            type: 'string',
            description: 'The optimized meta description (max 150 characters)',
          },
        },
        required: ['documentId', 'seoTitle', 'metaDescription'],
      },
    },
  },
];

Execute the mutation using the Actions API when the tool fires. The Actions API is the correct way to programmatically mutate documents — it uses the same system Sanity Studio uses, handles draft/published state correctly, and is transactional:

async function executeSanityAction(toolCall: { function: { arguments: string } }) {
  const args = JSON.parse(toolCall.function.arguments);

  try {
    await client.action({
      actionType: 'sanity.action.document.edit',
      publishedId: args.documentId,
      patch: {
        set: {
          'seo.title': args.seoTitle,
          'seo.description': args.metaDescription,
        },
      },
    });

    return { success: true };
  } catch (error) {
    console.error('Action failed:', error);
    return { success: false, error: error instanceof Error ? error.message : String(error) };
  }
}

The Actions API writes to drafts by default when a publishedId is provided. A human editor retains final approval in Sanity Studio before anything goes live.

Building the integration into Sanity Studio

Most LLM integrations in Sanity follow the same three-component structure:

Custom document actions. You can add buttons and UI components directly into the document editor using Sanity's document actions API. This gives editors a "Generate SEO" or "Translate" button that triggers the AI flow without leaving Studio.

Serverless middleware. The Studio action calls a serverless function (a Next.js API route, or natively via Compute) rather than calling the LLM API directly. This keeps your API keys secure server-side and gives you a place to add validation, rate limiting, and logging.

The LLM API. The middleware calls Claude, GPT-4, or whichever model fits the task. It processes the response and writes back to Sanity using Agent Actions or the Actions API.

Using Compute for event-driven AI

If you want AI to run automatically — enriching content when it's published, tagging new documents, generating translations on create — Compute is the right tool. Compute lets you deploy TypeScript functions directly to the Content Lake, triggered by content events:

import { documentEventHandler } from '@sanity/functions';
import { createClient } from '@sanity/client';

export const handler = documentEventHandler(async ({ context, event }) => {
  if (event.data._type !== 'article') return;

  const client = createClient({ ...context.clientOptions });

  // Auto-generate tags when an article is published
  await client.agent.action.generate({
    schemaId: 'sanity.workspace.schema.default',
    documentId: event.data._id,
    instruction: 'Suggest up to five relevant tags based on the article content.',
    target: { path: 'tags' },
  });
});

No external webhooks, no separate infrastructure to maintain. The logic lives alongside your content.

Connecting agents directly with MCP

If you're building AI agents that need direct, structured access to your Content Lake — rather than building a custom RAG pipeline — Sanity provides two MCP (Model Context Protocol) endpoints for different use cases.

The Sanity MCP server at mcp.sanity.io gives agents in tools like Claude Code, Cursor, and VS Code full read/write access to your workspace. They can query content, manage releases, deploy schemas, and execute GROQ queries with full awareness of your content model. This is the right tool for developer workflows and content operations automation. Authentication is via OAuth — no API tokens to manage.

Agent Context is a separate, read-only MCP endpoint designed for production-facing agents: customer support bots, site search, product recommendation engines. It provides scoped, schema-aware access to a single dataset, with semantic search built in. Embeddings live in the Content Lake alongside your content, so when a document is updated the embeddings update too — no separate reindexing pipeline.

The distinction matters: the MCP server is for development and operations tooling; Agent Context is for your users' experience.

High-impact use cases

Automated SEO and metadata generation. An editor clicks "Generate SEO" in Studio. The action sends the article body to Claude via a serverless middleware function. Claude summarizes it into a 150-character meta description. Agent Actions writes it back as a draft for review. The editor approves and publishes. No copy-paste, no formatting work.

Intelligent taxonomy and tagging. Pass a new article to an LLM along with your predefined list of tags (fetched directly from your Content Lake as a controlled vocabulary). The model suggests the most relevant categories. Consistency improves across your dataset without manual overhead.

Bulk content transformation. Need to rename a product across 400 pages, adjust tone for a rebrand, or convert British English to American English across a market? Agent Actions' transform can batch through your documents, stage changes as drafts, and let editors spot-check before anything publishes.

Context-aware localization. Wire Agent Actions' translate to a Compute function triggered on publish. When an English article is published, it automatically creates draft translations in your target languages, using your style guide and glossary (also stored in the Content Lake) to preserve terminology.

What to avoid

Mutating published documents directly. The article pattern of patching drafts.${documentId} manually is common but not recommended. Sanity's documentation advises against creating documents with drafts. prefixed IDs programmatically, as it can cause unexpected behavior with the draft system. Use Agent Actions or the Actions API instead — they handle draft/published state correctly by design.

Sending entire documents as context. Always use GROQ to project only the fields the LLM needs. Sending full documents inflates token counts, increases cost, and can accidentally expose internal fields.

Skipping human review on agentic writes. Agent Actions writes to drafts by default, which is the right behavior. Don't override this (forcePublishedWrite: true) unless you have a specific reason and a tested workflow.

Ignoring deletions in RAG pipelines. If you maintain a vector store alongside Sanity, use webhooks to listen for delete events and purge the corresponding vectors. Content that no longer exists in your CMS shouldn't exist in your AI's context either.

The architecture in summary

Sanity's structured content, GROQ query language, and Actions API make it well-suited for LLM integration. Content is already organized semantically, which means you're not spending engineering time cleaning and transforming data before it's useful to a model.

The patterns here — RAG for context, Agent Actions for agentic writes, Compute for event-driven automation, MCP for direct agent access — cover most production AI use cases without requiring custom infrastructure. Your content model becomes the foundation your AI operates on.

Ready to start building? See Agent Actions, Agent Context, and the Sanity MCP server in the documentation.