Free live webinar

Same team, more pipeline: AI-powered marketing for Dynamics 365 · Wed 11 Nov · 1:00pm ET · with Microsoft MVP Mark SmithSame team, more pipeline · 11 Nov

Save my seat

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

  1. Open Settings and go to the Connectors tab (labeled "MCP Tools").
  2. Choose Sanity from the catalog and click Connect.
  3. 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. developer or editor) for write access.
  4. Assign the connection to the Marketing area so the agent can use it when publishing campaign content.

What you can publish

ModeContent typesWhere it lives
Structured document (marketingPost)Blog posts, case studies, white papers, markdown documentsFully in Sanity, as a real document: title, slug, excerpt, Markdown body, hero image and an optional topic tag
Reference document (marketingHostedPage)Landing pages, web formsHosted 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 topic has no list, the agent uses the post's primary category.
  • If your schema has no topic field, the agent doesn't write one.
  • Re-publishing never overwrites a topic someone 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 marketingHostedPage as an iframe of embedUrl. 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. externalUrl is the direct link to the live page.
  • Render marketingPost.body with your Markdown component, the same way you'd render any other Markdown field.
  • Render marketingPost.heroImage (and marketingHostedPage.ogImage) with Sanity's image URL builder, the way you'd render any other Sanity image — it's a reference to an uploaded asset. Show marketingPost.topic as the post's tag when it's set.

Notes / limits

  • No duplicates on re-publish. The agent stamps each document with a sourceArtifactId and 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.