MCP Sampling and Elicitation

AIMCPAgentsProtocol
Share on LinkedIn

Most MCP interactions flow one direction: the client sends a user message to the model, the model decides to call a tool, the server executes it and returns results. But some server workflows need to ask the model a question or ask the user for input mid-execution. A code review server wants the model to analyze a diff before posting comments. A deployment server needs the user to confirm before pushing to production.

MCP sampling and elicitation reverse the typical flow. Sampling lets servers request LLM completions. Elicitation lets servers request structured input from the user. Together they enable multi-step server workflows that were previously impossible without embedding an LLM inside the server itself.

Sampling: server-initiated LLM requests

In normal MCP flow, the model calls server tools. With sampling, the server calls the client's model:

Standard flow:
  User → Model → tool_call → Server → result → Model → User

Sampling flow:
  User → Model → tool_call → Server → sampling_request → Model → Server → result → Model → User

The server sends a sampling/createMessage request to the client, which forwards it to the connected LLM:

// Server-side: request LLM analysis during tool execution
server.tool("review_code", {
  diff: { type: "string" },
}, async ({ diff }, context) => {
  // Ask the client's model to analyze the diff
  const analysis = await context.sample({
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: `Analyze this diff for security issues:\n${diff}`,
      },
    }],
    maxTokens: 1000,
  });

  const issues = parseIssues(analysis.content);
  return {
    content: [{ type: "text", text: JSON.stringify(issues) }],
  };
});

The client controls which models the server can access and can set rate limits, token budgets, and content policies on sampling requests.

Sampling use cases

SQL generation before execution:

server.tool("natural_language_query", {
  question: { type: "string" },
}, async ({ question }, context) => {
  const schema = await getSchemaResource();

  const sqlResponse = await context.sample({
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: `Given schema:\n${schema}\n\nGenerate SQL for: ${question}`,
      },
    }],
    maxTokens: 200,
  });

  const sql = extractSQL(sqlResponse.content);
  validateReadOnly(sql);
  const results = await db.query(sql);
  return { content: [{ type: "text", text: JSON.stringify(results.rows) }] };
});

Content classification before routing:

const category = await context.sample({
  messages: [{
    role: "user",
    content: { type: "text", text: `Classify as bug/feature/question: ${ticketText}` },
  }],
  maxTokens: 10,
});
// Route based on classification

Multi-step reasoning within a tool: A tool that needs the model to plan before executing — generate a migration plan, then execute each step.

Elicitation: gathering user input mid-workflow

Elicitation lets the server pause and ask the user for structured input:

server.tool("deploy_to_production", {
  service: { type: "string" },
  version: { type: "string" },
}, async ({ service, version }, context) => {
  // Confirm with the user before deploying
  const confirmation = await context.elicit({
    message: `Deploy ${service} v${version} to production?`,
    requestedSchema: {
      type: "object",
      properties: {
        confirmed: { type: "boolean", description: "Confirm deployment" },
        rollback_plan: { type: "string", description: "Rollback procedure if needed" },
      },
      required: ["confirmed"],
    },
  });

  if (!confirmation.confirmed) {
    return { content: [{ type: "text", text: "Deployment cancelled by user." }] };
  }

  await deployService(service, version);
  return { content: [{ type: "text", text: `Deployed ${service} v${version}` }] };
});

The client renders the elicitation request as a dialog, form, or inline prompt. The user responds, and the server continues execution with the provided data.

Elicitation use cases

Confirmation for destructive actions: Delete, deploy, send — anything irreversible should elicit confirmation rather than relying on the model to ask.

Missing required parameters: When the user's request is ambiguous, elicit the specific missing field:

const details = await context.elicit({
  message: "Which environment should I deploy to?",
  requestedSchema: {
    type: "object",
    properties: {
      environment: { type: "string", enum: ["staging", "production"] },
    },
    required: ["environment"],
  },
});

Multi-step wizards: Complex workflows that need sequential user input — creating a project with name, template, team, and permissions.

Capability negotiation

During MCP initialization, the client advertises its capabilities:

{
  "capabilities": {
    "sampling": {},
    "elicitation": {}
  }
}

Servers must check before using either feature:

server.tool("smart_query", schema, async (args, context) => {
  if (!context.clientCapabilities.sampling) {
    // Fallback: require the user to provide SQL directly
    return { content: [{ type: "text", text: "Please provide a SQL query." }] };
  }

  const sql = await context.sample({ /* ... */ });
  // ...
});

Graceful degradation ensures servers work with clients that lack newer capabilities.

Security considerations

Sampling and elicitation introduce new attack surfaces:

Sampling:

Elicitation:

// Client-side: validate sampling requests
function handleSamplingRequest(request: SamplingRequest): boolean {
  if (request.messages.some(m => containsSensitiveData(m.content))) {
    logger.warn("Blocked sampling request with sensitive data");
    return false;
  }
  if (samplingRateLimiter.isExceeded(request.serverId)) {
    return false;
  }
  return true;
}

Common production mistakes

Teams get sampling elicitation wrong in predictable ways:

Production implementations of sampling elicitation fail when staging mirrors production topology poorly, rollback is untested, and on-call runbooks describe the happy path only.

Debugging and triage workflow

When sampling elicitation misbehaves in production, work top-down instead of guessing:

  1. Confirm scope — one tenant, region, or deployment stage? Narrow blast radius before deep diving.
  2. Check recent changes — deploys, flag flips, config pushes, and schema migrations in the last 24 hours.
  3. Compare golden signals — latency, error rate, saturation, and traffic for the affected surface vs. baseline.
  4. Reproduce minimally — smallest input or scenario that triggers the failure; capture traces/logs with correlation IDs.
  5. Fix forward or rollback — if rollback is faster than root-cause during incident, rollback first, postmortem second.
  6. Add a guard — alert, integration test, or circuit breaker so the same class of failure is caught earlier next time.

Document the timeline during triage. Future you (and on-call) will need timestamps, not just conclusions.

Resources

Frequently asked questions

What is MCP sampling and why would a server need it?

Sampling lets an MCP server request an LLM completion from the client's model. A database MCP server might ask the model to generate SQL from a natural language question before executing it. The server needs LLM capability but should not embed its own model — sampling delegates inference to the client's existing model connection.

How is elicitation different from a regular tool call?

Elicitation lets the server ask the user for additional input mid-workflow — a confirmation dialog, a form field, or a choice between options. Tool calls execute actions; elicitation pauses execution to gather information the server cannot infer. The user sees a prompt and responds before the workflow continues.

Are sampling and elicitation supported by all MCP clients?

Support varies. Claude Desktop and Cursor support sampling in recent versions. Elicitation is newer and client support is still rolling out. Check your client's capability negotiation during MCP initialization — the server should gracefully degrade if the client does not advertise sampling or elicitation support.

Hiring a senior Android / Flutter engineer?

I architect and ship production mobile software — Kotlin, Jetpack Compose, Flutter — for robotics, EV infrastructure, fintech, and real-time systems. Open to remote roles in Europe and the US.

Get in touch →