---
title: "Expanding Your Next.js MCP Server — Editing & Revalidation"
slug: "expanding-mcp-server-nextjs"
published: "2025-12-06"
updated: "2025-12-21"
categories:
  - "Next.js"
tags:
  - "MCP server write operations"
  - "update article MCP tool"
  - "Next.js revalidatePath"
  - "revalidateTag MCP"
  - "Sanity CMS patch API"
  - "MCP content publishing"
  - "cache invalidation Next.js"
  - "MCP server CRUD"
  - "Claude Code content editing"
  - "real-time content updates"
llm-intent: "how-to"
audience-level: "intermediate"
llm-purpose: "Show developers how to add write capabilities and cache revalidation to their existing MCP server"
llm-prereqs:
  - "Completed Part 1: Basic MCP server"
  - "Sanity CMS with write token"
  - "Understanding of Next.js caching"
llm-outputs:
  - "update_article tool implementation"
  - "Sanity CMS write integration"
  - "Next.js cache revalidation working"
  - "Claude can now edit content directly"
---

**Summary Triples**
- (Expanding Your Next.js MCP Server — Editing & Revalidation, expresses-intent, how-to)
- (Expanding Your Next.js MCP Server — Editing & Revalidation, covers-topic, MCP server write operations)
- (Expanding Your Next.js MCP Server — Editing & Revalidation, provides-guidance-for, Show developers how to add write capabilities and cache revalidation to their existing MCP server)

### {GOAL}
Show developers how to add write capabilities and cache revalidation to their existing MCP server

### {PREREQS}
- Completed Part 1: Basic MCP server
- Sanity CMS with write token
- Understanding of Next.js caching

### {STEPS}
1. Add update_article tool
2. Implement Sanity write logic
3. Add revalidation
4. Verify with Claude

<!-- llm:goal="Show developers how to add write capabilities and cache revalidation to their existing MCP server" -->
<!-- llm:prereq="Completed Part 1: Basic MCP server" -->
<!-- llm:prereq="Sanity CMS with write token" -->
<!-- llm:prereq="Understanding of Next.js caching" -->
<!-- llm:output="update_article tool implementation" -->
<!-- llm:output="Sanity CMS write integration" -->
<!-- llm:output="Next.js cache revalidation working" -->
<!-- llm:output="Claude can now edit content directly" -->

# Expanding Your Next.js MCP Server — Editing & Revalidation
> Expand your Next.js MCP server with write capabilities. Learn to implement an update_article tool and handle Next.js cache revalidation for instant…
Matija Žiberna · 2025-12-06

**This is Part 2 of the MCP Server Series.** Make sure you've completed [Part 1: Build a Production MCP Server](/blog/build-mcp-server-nextjs) first.

---

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:
1.  Open my CMS (Sanity).
2.  Find the article.
3.  Make the edit.
4.  Publish.
5.  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:
1.  An `update_article` tool in our MCP server.
2.  A way to write to our CMS (Sanity).
3.  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**.

```typescript
// 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 use `unstable_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:
1.  Call `get_article_content` to read the current text.
2.  Prepend the update note to the markdown.
3.  Call `update_article` with the new content.
4.  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_TOKEN` secret.** 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?
---

## Continue the Series

Your MCP server now has both read and write capabilities. Here's where to go next:

- **Part 3: [OAuth for MCP Servers](/blog/oauth-mcp-server-claude)** — Essential security before deploying write tools to production
- **Part 4: [Send Emails from MCP](/blog/send-emails-mcp-react-email-brevo)** — Add email automation capabilities

Or if you're looking for the business perspective, see [Why Your Business Needs an MCP Server](/blog/why-your-business-needs-an-mcp-server).

The possibilities are endless when your AI coding assistant is directly connected to your application's core logic.

Thanks,
Matija