Sanity MCP Server

  • src
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import { createClient } from "@sanity/client"; // Initialize Sanity client const sanityClient = createClient({ projectId: process.env.SANITY_PROJECT_ID, dataset: process.env.SANITY_DATASET, apiVersion: "2025-03-05", useCdn: false, token: process.env.SANITY_TOKEN }); // Create MCP server const server = new McpServer({ name: "Sanity-MCP", version: "1.0.0" }); // Tool to create documents server.tool("create-document", { type: z.string(), content: z.record(z.any()) }, async ({ type, content }) => { try { // Add unique keys to block content if present const processedContent = processBlockContent(content); const result = await sanityClient.create({ _type: type, ...processedContent }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } ); // Helper function to process block content function processBlockContent(content: Record<string, any>): Record<string, any> { if (content.body && Array.isArray(content.body)) { return { ...content, body: content.body.map((block: any, index: number) => ({ ...block, _key: block._key || `auto-${index}-${Date.now()}` })) }; } return content; } // Tool to edit documents server.tool("edit-document", { id: z.string(), content: z.record(z.any()) }, async ({ id, content }) => { try { // Process block content if present const processedContent = processBlockContent(content); const result = await sanityClient .patch(id) .set(processedContent) .commit(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } ); // Tool to list documents by type server.tool("list-documents", { type: z.string(), limit: z.number().optional().default(10) }, async ({ type, limit }) => { try { const query = `*[_type == "${type}"] | order(_createdAt desc) [0...${limit}]`; const documents = await sanityClient.fetch(query); return { content: [{ type: "text", text: JSON.stringify(documents, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } ); // Tool to get schema from an example document server.tool("get-schema", { type: z.string() }, async ({ type }) => { try { // Get first document of the specified type const query = `*[_type == "${type}"][0]`; const document = await sanityClient.fetch(query); if (!document) { return { content: [{ type: "text", text: `No documents found for type ${type}` }], isError: true }; } // Create schema template from document const schema = Object.keys(document).reduce((acc, key) => { // Skip system fields if (key.startsWith('_')) return acc; const value = document[key]; acc[key] = { type: getValueType(value), required: true }; return acc; }, {} as Record<string, any>); return { content: [{ type: "text", text: JSON.stringify(schema, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } ); // Helper to determine value type function getValueType(value: any): string { if (Array.isArray(value)) { return 'array'; } if (typeof value === 'object' && value !== null) { return 'object'; } return typeof value; } // Start server const transport = new StdioServerTransport(); await server.connect(transport);