Sanity connector
Connect your own Sanity project as an MCP Tool so the marketing agent can publish content straight into it. Once connected, the agent can push content it creates in the app into your Sanity dataset instead of (or in addition to) keeping it only inside the app.
What it does
- Blog posts, case studies, white papers, and markdown documents publish as structured Sanity documents — real content your Sanity-powered site can query, render, and index like any other Sanity content.
- Landing pages and web forms publish as reference documents that stay hosted by the app. Sanity holds a pointer to the page (its live URL and embed URL), but the page itself (and its form) keeps running here — submissions keep flowing through the app's own submission endpoint via the embed.
Connecting Sanity
- Open Settings and go to the Connectors tab (labeled "MCP Tools").
- Choose Sanity from the catalog and click Connect.
- Enter:
- Your Project ID and Dataset name, both found in your Sanity project.
- An API token created at
sanity.io/manage→ your project → API → Tokens. The token needs Editor (write) access so the agent can create and update documents. - MCP user role (optional) — leave blank to use the token's own role.
Set it only if your Sanity setup expects a specific role name (e.g.
developeroreditor) for write access.
- Assign the connection to the Marketing area so the agent can use it when publishing campaign content.
What you can publish
| Mode | Content types | Where it lives |
|---|---|---|
Structured document (marketingPost) | Blog posts, case studies, white papers, markdown documents | Fully in Sanity, as a real document: title, slug, excerpt, Markdown body, hero image and an optional topic tag |
Reference document (marketingHostedPage) | Landing pages, web forms | Hosted in the app; Sanity holds a reference (title, live URL, embed URL, social share image) |
The schema: marketingPost and marketingHostedPage
The connector uses two document types — marketingPost for structured
content and marketingHostedPage for hosted-page references.
You usually don't have to create these by hand. When it publishes, the agent checks whether the two types already exist in your project and, if they don't, registers them for you — additively, so it never drops document types your project already has. (If the agent can't safely read your current schema to add to it, it stops and asks you to add the types yourself rather than risk overwriting your schema.)
If your schema is managed from your own Studio code, add the types
yourself. When your project's workspace is deployed from a Studio you
maintain, Sanity doesn't allow the agent to change that schema, so it never
tries. If marketingPost or marketingHostedPage is missing there, the agent
gives you the definitions, asks you to add them to your Studio and deploy it,
and waits for you to confirm before publishing. Add them up front (the
definitions are below) and it publishes straight away.
Registering the schema is what lets Sanity Studio recognize and validate these documents. Your front-end site still needs components to render them (see Rendering below).
If you prefer to manage schema in your own Studio repo, add these type definitions yourself:
// schemaTypes/marketingPost.ts
import {defineType, defineField} from 'sanity'
import {DocumentTextIcon} from '@sanity/icons'
export const marketingPost = defineType({
name: 'marketingPost',
title: 'Post',
type: 'document',
icon: DocumentTextIcon,
fields: [
defineField({name: 'title', type: 'string'}),
defineField({name: 'slug', type: 'slug', options: {source: 'title'}}),
defineField({name: 'excerpt', type: 'text'}),
defineField({name: 'publishedAt', type: 'datetime'}),
defineField({name: 'heroImage', type: 'image'}),
defineField({
name: 'body',
type: 'text',
description: 'Markdown — render with a Markdown component',
}),
defineField({
name: 'docKind',
type: 'string',
options: {
list: [
{title: 'Post', value: 'post'},
{title: 'Case study', value: 'caseStudy'},
{title: 'White paper', value: 'whitePaper'},
],
layout: 'radio',
},
}),
defineField({
name: 'topic',
type: 'string',
description: 'Optional. Tag shown on the blog index. Add options.list to limit the values — the agent then picks from that list.',
}),
defineField({
name: 'sourceArtifactId',
type: 'string',
description: 'Originating app artifact id — used to update the same document on re-publish.',
readOnly: true,
}),
],
})
// schemaTypes/marketingHostedPage.ts
import {defineType, defineField} from 'sanity'
import {DocumentIcon} from '@sanity/icons'
export const marketingHostedPage = defineType({
name: 'marketingHostedPage',
title: 'Hosted page',
type: 'document',
icon: DocumentIcon,
fields: [
defineField({name: 'title', type: 'string'}),
defineField({name: 'slug', type: 'slug', options: {source: 'title'}}),
defineField({name: 'externalUrl', type: 'url', description: 'Live page URL (hosted by the app)'}),
defineField({name: 'embedUrl', type: 'url', description: 'Iframe embed URL (externalUrl + /embed.js)'}),
defineField({name: 'description', type: 'text'}),
defineField({name: 'ogImage', type: 'image', title: 'Social share image'}),
defineField({
name: 'sourceArtifactId',
type: 'string',
description: 'Originating app artifact id — used to update the same document on re-publish.',
readOnly: true,
}),
],
})
Hero images and social share images
When a blog post has a hero image, the agent uploads the post's hero image to
your Sanity dataset and attaches it to the post as an image reference — a
Sanity image field holds a reference to an uploaded file, never a web address.
It does the same for a hosted page's social share image (ogImage). In the
Marketing chat the agent may ask you to approve the upload first.
If the upload can't be done — you decline it, the connection's token doesn't have write access, the image address can't be fetched, or it isn't an image — the post still publishes, without the image, and the agent tells you which step failed and why. Re-publishing a post never clears an image that is already there, and uploading an unchanged image again doesn't create a duplicate (Sanity matches identical files).
The topic tag
topic is an optional string, and it's the tag your site shows on the blog
index. It's the one field whose allowed values are yours: if your Studio gives
topic an allowed list (add options.list to the field), the agent reads that
allowed list when it publishes, picks the closest match for the post from its
categories, tags, title and excerpt, and writes the value exactly as it's
spelled in the list. If nothing fits, it leaves topic empty rather than
inventing a value.
- If
topichas no list, the agent uses the post's primary category. - If your schema has no
topicfield, the agent doesn't write one. - Re-publishing never overwrites a
topicsomeone has already set in Studio.
Rendering
Sanity stores content but doesn't render it — your deployed Sanity front-end does. Add rendering for both types:
- Render
marketingHostedPageas an iframe ofembedUrl. Forms inside the iframe keep posting back to the app's own submission endpoint, so they keep working exactly as they do today — Sanity only holds the reference, not the form itself.externalUrlis the direct link to the live page. - Render
marketingPost.bodywith your Markdown component, the same way you'd render any other Markdown field. - Render
marketingPost.heroImage(andmarketingHostedPage.ogImage) with Sanity's image URL builder, the way you'd render any other Sanity image — it's a reference to an uploaded asset. ShowmarketingPost.topicas the post's tag when it's set.
Notes / limits
- No duplicates on re-publish. The agent stamps each document with a
sourceArtifactIdand looks it up before writing, so re-publishing the same artifact updates the same Sanity document (Sanity assigns the document_id). - Slugs stay put and stay unique. A document's slug is its URL. It's set
from the title when the document is first published, and a re-publish never
changes it, even if you rename the post. If another document already uses
that slug, the agent adds a number (
-2,-3, …) and tells you which slug it used. - Schema registration is additive. The agent only ever adds the two types to your schema; it never removes types you already have.
- The app keeps hosting landing pages and web forms. Sanity only holds a reference document for these; it never becomes the source of truth for the page or its form.
- Your API token needs write access to publish. A read-only token will connect but publishing will fail.