Expanding Your Next.js MCP Server — Editing & Revalidation
Add write capabilities to your MCP server and handle Next.js cache revalidation for instant updates.

⚡ Next.js Implementation Guides
In-depth Next.js guides covering App Router, RSC, ISR, and deployment. Get code examples, optimization checklists, and prompts to accelerate development.
In my previous guide, we built a production-ready MCP server that allowed Claude Code to read from our blog. We could search for articles and fetch their content. This is fantastic for context—Claude can answer questions based on what I've already written.
But as I was using it, I hit a frustration point. I'd spot a typo or a sentence that needed clarification, and I'd have to:
- Open my CMS (Sanity).
- Find the article.
- Make the edit.
- Publish.
- Wait for revalidation.
That's too much friction. I'm already talking to Claude; why can't I just say, "Fix that typo in the title"?
Today, we're going to expand our MCP server to support write operations. We'll implement an update_article tool and, crucially, handle Next.js cache revalidation so our changes show up instantly.
The Goal
We want to be able to tell Claude:
"Update the article 'nextjs-mcp-guide' to change the title to 'Building a Production MCP Server' and fix the typo in the first paragraph."
To do this, we need:
- An
update_articletool in our MCP server. - A way to write to our CMS (Sanity).
- A way to tell Next.js to clear its cache for that page.
Step 1: The update_article Tool
We'll modify our existing src/app/api/mcp/[transport]/route.ts. We need to add a new tool definition that accepts the article slug and the fields we want to update.
First, make sure you have your SANITY_API_TOKEN in your .env.local (and Vercel env vars). This token needs write permissions.
// src/app/api/mcp/[transport]/route.ts
import { createClient } from 'next-sanity'
import { revalidatePath, revalidateTag } from 'next/cache' // 👈 Don't forget this!
// ... existing setup ...
server.tool(
'update_article',
'Update an existing article\'s content or metadata. Supports partial updates.',
{
slug: z.string().describe('The slug of the article to update'),
markdownContent: z.string().optional().describe('New markdown content'),
title: z.string().optional().describe('New title'),
subtitle: z.string().optional().describe('New subtitle'),
metaDescription: z.string().optional().describe('New meta description')
},
async ({ slug, markdownContent, title, subtitle, metaDescription }) => {
try {
console.log(`[MCP] update_article tool called for slug: ${slug}`)
// 1. Create a write client
const writeClient = createClient({
projectId,
dataset,
token: process.env.SANITY_API_TOKEN, // 👈 Must have write access
apiVersion: '2023-05-03',
useCdn: false,
})
// 2. Find the document ID
const existingPost = await writeClient.fetch(
`*[_type == "post" && slug.current == $slug][0]{_id, title}`,
{ slug }
)
if (!existingPost) {
return {
content: [{ type: 'text', text: JSON.stringify({ error: `Article with slug "${slug}" not found` }) }],
isError: true
}
}
// 3. Prepare the patch
const patch: any = {
dateModified: new Date().toISOString()
}
if (markdownContent !== undefined) patch.markdownContent = markdownContent
if (title !== undefined) patch.title = title
if (subtitle !== undefined) patch.subtitle = subtitle
if (metaDescription !== undefined) patch.metaDescription = metaDescription
// 4. Commit the patch
console.log(`[MCP] Patching document ${existingPost._id} with:`, Object.keys(patch))
const result = await writeClient.patch(existingPost._id).set(patch).commit()
// 5. Revalidate (The Magic Part ✨)
console.log(`[MCP] Revalidating paths for slug: ${slug}`)
revalidatePath(`/blog/${slug}`)
revalidatePath('/blog')
revalidateTag('post')
return {
content: [{
type: 'text',
text: JSON.stringify({
success: true,
message: 'Article updated successfully and revalidated',
updatedFields: Object.keys(patch),
article: {
slug,
title: result.title
}
}, null, 2)
}]
}
} catch (error) {
// ... error handling ...
}
}
)
Step 2: The Importance of Revalidation
The code above looks straightforward, but step 5 is critical.
Next.js is aggressive about caching. If you update the content in Sanity but don't tell Next.js, your site will continue serving the old static HTML until the cache naturally expires (which could be days).
Because our MCP server is running inside our Next.js application (via mcp-handler), we have direct access to next/cache.
revalidatePath('/blog/[slug]'): Clears the cache for the specific article page.revalidatePath('/blog'): Clears the blog listing page (so the new title/subtitle shows up there).revalidateTag('post'): Clears any data fetches tagged with 'post' (useful if you useunstable_cache).
This is much simpler than setting up a webhook handler for Sanity just to handle these manual edits. We know exactly what changed, so we can revalidate it immediately.
Step 3: Testing It Out
Now for the fun part. Restart your dev server (npm run dev) and Claude Code.
Try a more complex request, like adding an update note to an article:
"Check the article 'cloudflare-outage'. Add a note to the beginning saying: 'UPDATE: The issue has been resolved as of 10:15 AM.'"
Claude will:
- Call
get_article_contentto read the current text. - Prepend the update note to the markdown.
- Call
update_articlewith the new content. - Receive the success message confirming revalidation.
If you refresh your local browser (or production site, if you deployed this), you'll see the change instantly. This is much faster than the manual CMS workflow.
Security Note
With great power comes great responsibility. You are giving an AI agent write access to your database.
- Keep your
SANITY_API_TOKENsecret. Never commit it to git. - Scope your MCP server. If you're using
mcp-handler, it's protected by your Next.js auth or firewall rules in production. - Review changes. Claude is smart, but it can hallucinate. Always verify the changes it makes, especially for large content updates.
What's Next?
We've gone from reading to writing. Now our MCP server is a true CMS interface. You could expand this further:
- Create new articles: Add a
create_articletool. - Manage images: Add tools to upload images to Sanity.
- SEO optimization: Create a tool that analyzes content and updates keywords automatically.
The possibilities are endless when your AI coding assistant is directly connected to your application's core logic.