How to Use MCP Sampling to Let Claude Make Smarter Decisions on Your Behalf

Learn how MCP's sampling feature lets Claude think through multi-step problems and request its own completions — so your automations get smarter without more prompting from you.

When I Realized Claude Could Ask Itself Questions

Here's something that genuinely surprised me when I was digging into MCP a while back: Claude doesn't have to wait for you to tell it what to do next. With a feature called sampling, an MCP server can actually ask Claude to run a completion mid-task — meaning Claude can pause, think something through, and continue without you being in the loop at every step.

When I first tried this, my brain sort of short-circuited. I kept thinking of AI as a back-and-forth thing — I ask, it answers, I ask again. But sampling flips that model a little. It lets Claude operate more like an agent that reasons as it goes, rather than a chatbot waiting for your next message.

This isn't magic, and it's not scary. It's actually a pretty elegant design decision in the MCP spec. Let me walk you through what it is, why it matters, and how to start using it in your own MCP setups.

What MCP Sampling Actually Is

Before we get practical, let's get the concept straight. In the MCP architecture, you've got a client (like Claude Desktop) and a server (a tool or integration you've built or installed). Normally the flow looks like this:

  1. User sends a message to Claude
  2. Claude calls a tool on the MCP server
  3. Server returns a result
  4. Claude uses that result to respond to the user

Simple enough. But what if the server needs Claude's intelligence partway through? What if it needs Claude to evaluate something, summarize data, or make a judgment call before it can finish its job?

That's what sampling enables. The MCP spec defines a sampling/createMessage request that an MCP server can send back to the client — essentially asking Claude to run a new completion. The client (Claude Desktop, in most setups) handles it and sends the result back to the server so the tool can keep going.

Think of it like this: you're the manager, Claude is your analyst, and your MCP server is a research assistant. The research assistant is partway through a report and hits a judgment call — so instead of stopping and bothering you, they walk down the hall and ask your analyst to weigh in. Work continues. You get the finished report.

A Real-World Example: Smart File Triage

Let me give you something concrete. Say you've built a simple MCP server that scans a folder of documents and categorizes them. Without sampling, you'd probably just write some rules: if the filename contains "invoice," label it finance. But rules are brittle — they miss edge cases constantly.

With sampling, your server can read a file, hit a weird case (say, a file called "Q3_notes_final_FINAL_v3.docx"), and then ask Claude: "Hey, based on the first 200 words of this document, is this a finance document, a project plan, or something else?"

Claude responds with its judgment. Your server uses that to categorize the file correctly. No extra prompting from you. No rule-writing. The workflow just... handles it.

Here's a stripped-down version of what that sampling request looks like in your server code:

server.py
# Inside your MCP server tool handler
async def categorize_document(file_content: str, server_session):
# Ask Claude to classify the document
response = await server_session.create_message(
messages=[{
"role": "user",
"content": {
"type": "text",
"text": f"Classify this document as 'finance', 'project', or 'other'. Reply with one word only.\n\n{file_content[:500]}"
}
}],
max_tokens=10,
system_prompt="You are a document classifier. Respond with exactly one word."
)
return response.content.text.strip().lower()

Notice a few things. You're calling server_session.create_message() — that's the sampling request going back to the client. You're keeping max_tokens super low because you only need one word. And you're using a tight system prompt to constrain the response. These details matter — sampling isn't free, and sloppy prompts waste tokens fast.

The Human-in-the-Loop Part (Don't Skip This)

Here's something the MCP spec is pretty thoughtful about: sampling requests don't have to be invisible. Claude Desktop, for example, can be configured to show you a sampling request before it goes through — letting you approve or tweak it. This is called the "human in the loop" design.

When I first set this up, I actually left approvals on for a while. It was genuinely educational. I could watch my server mid-task, see exactly what it was asking Claude, and decide if those questions were smart or wasteful. Turns out a couple of my early sampling prompts were way too vague and I was basically paying Claude tokens to shrug at me.

Start with approvals turned on

In Claude Desktop's MCP settings, you can require approval for sampling requests. Do this while you're building — it lets you catch bad prompts before they run in a loop and eat your token budget.

Once you trust your server's sampling prompts, you can let them run automatically. But don't skip the manual review phase. It'll save you headaches (and API costs) later.

Where Sampling Really Shines

You might be wondering if this is worth the extra complexity. Fair question. Here's where I've found sampling genuinely earns its keep:

Dynamic routing decisions. If your server needs to decide which tool to call next based on ambiguous input, a quick sampling call to Claude is way more reliable than a pile of if/else logic.

Quality checking outputs. Your server generates something — a summary, a code snippet, a formatted report — and before returning it to the user, it asks Claude: "Does this look correct? Any obvious errors?" You catch problems inside the workflow instead of after.

Translating messy data. Real-world data is ugly. Dates in weird formats, addresses missing fields, product names with typos. A sampling call can normalize messy inputs on the fly without you having to write a custom parser for every variation.

Summarizing before deciding. If your tool processes something long — a PDF, a log file, a Slack thread — you can sample to get a quick summary first, then use that summary to decide what to actually do with the full content.

Keeping Sampling From Going Rogue

I want to be real with you: sampling can go sideways if you're not careful. The main failure mode I've hit is sampling loops — where a server samples Claude, Claude's response triggers another tool call, that tool call triggers another sample, and suddenly you've got a chain of ten LLM calls for something that should've been two.

A few guard rails that have saved me:

Set a max depth counter. Track how many sampling calls you've made in a single tool invocation. If you hit three, stop and return what you have. Hard limit, no exceptions.

server.py
# Simple depth guard
MAX_SAMPLING_DEPTH = 3

async def run_with_guard(task, session, depth=0):
if depth >= MAX_SAMPLING_DEPTH:
return "Max depth reached — returning partial result"
# ... your sampling logic here ...
result = await session.create_message(...)
return result

Keep sampling prompts narrow. Ask for one thing, in one format, with low max_tokens. The more open-ended your sampling prompt, the more likely Claude gives you something your server wasn't designed to handle.

Log everything during development. Print the sampling request and response to your terminal. You want to see exactly what's happening before you trust it to run unattended.

Your First Sampling Experiment

If you want to try this out without building something complex from scratch, here's a quick experiment. Take an existing MCP server you've built (or the file system one from another tutorial here), and add a single sampling call that validates output before returning it.

Something like: after your tool generates a result, call create_message with a prompt like "Does this look like a valid result for a file listing tool? Reply YES or NO." If Claude says NO, return an error message instead. It's a tiny change, but it teaches you the full sampling round-trip without risking anything complicated.

Once that works, you'll start seeing opportunities for sampling everywhere. That's when MCP stops feeling like a fancy plugin system and starts feeling like something genuinely different — a way to build tools that think as they run, not just tools that execute and return.

And honestly? That shift in mental model is what made MCP click for me. Give it a shot.

Keep going

More tutorials in this category, or explore the full field guide.

More Plugin Tutorials Official Docs ↗