Rightshold™ Sign up

Answers · MCP security

How do I handle MCP sampling requests (a server asking my client's model for a completion) without leaking secrets or burning my model quota?

Last updated

TL;DR: Don't auto-approve them, cap them, and plan to stop using them. Sampling (sampling/createMessage) lets an MCP server get a completion from your client's model, on your model account, with no API key of its own; the server writes the prompt and reads the answer. The spec says a human SHOULD always be able to deny sampling requests, see and edit the prompt before it's sent and review the response before it goes back, that clients MUST respect maxTokens and SHOULD rate-limit, and that both sides SHOULD limit tool-loop iterations. Keep includeContext at "none" ("thisServer" and "allServers" are deprecated), so one server never receives another server's context. As of the 2026-07-28 spec, sampling itself is deprecated by SEP-2577: new implementations SHOULD NOT adopt it, existing ones SHOULD migrate to calling an LLM provider API directly, and it becomes eligible for removal in the first spec revision released on or after 2027-07-28. Elicitation, which asks the user a question, is a different feature and isn't deprecated.

What should I check, step by step?

1. Decide whether you need it at all. If your client doesn't need to serve sampling, don't declare the sampling capability; servers may only send sampling to clients that declared it (and sampling.tools for tool-enabled requests).

  1. Require approval per request, at least per server: show which server is asking, the full messages and systemPrompt, the model it would run on and maxTokens, with Deny as an easy option. Don't offer "always allow" for servers you didn't write.
  2. Cap the spend: set your own ceiling on maxTokens and lower the server's value when it asks for more (the spec only says clients MUST respect maxTokens, so the ceiling is yours), add a per-server rate limit and a daily token budget, and map model hints to a cheaper model when they ask for the most capable one. Hints are advisory; the client makes the final model choice.
  3. Keep other context out: treat includeContext as "none" whatever the server asks; the spec lets the client modify or ignore it, for example when honoring it would share sensitive information with the server.
  4. Review what goes back: the server receives the model's full reply, so look at it before returning it. If the user's secrets or another server's tool results appear in it, deny or edit.
  5. Bound tool loops: when a request includes tools, cap iterations and review the model's tool calls before they go to the server, as the spec's sampling-with-tools flow shows.
  6. Log every request: server, time, model used, maxTokens, tokens actually used, stopReason, and whether you approved, edited or denied. Don't keep the message list between requests; the spec says it SHOULD NOT be retained.
  7. If you maintain a server that uses sampling: move to a direct provider call from your server with your own key and budget. On 2026-07-28 connections the request travels inside an InputRequiredResult; the MCP TypeScript SDK v2 notes its requestSampling helper throws there.

What does an MCP server get when it sends a sampling request?

Sampling runs a server's prompt through the model your client controls, so the cost lands on your model account and quota. The spec presents this as the feature's benefit ("no server API keys necessary"), and that's also why an unchecked server can spend your quota in a loop.

How should an MCP client approve, cap and log sampling requests?

The spec's user interaction model is clear for clients: easy-to-use review UI, the ability to view and edit prompts before sending, and review of generated responses before delivery. Its security list adds approval controls, message validation by both parties, rate limiting, and that both parties MUST handle sensitive data appropriately.

How do I migrate an MCP server off deprecated sampling?

The deprecated-features registry gives the path: integrate directly with LLM provider APIs. The TypeScript SDK's guide says the handler keeps its shape; only the requestSampling call becomes a call to your provider's SDK with your own key, and you stop depending on which clients support sampling.

How do I ask Rightshold about this (free, no key)?

Add the MCP server https://www.rightshold.com/mcp to Claude, Cursor, ChatGPT, Grok or VS Code, then ask: “Record a signed receipt for the sampling request I just approved: which MCP server asked, the model that answered, the token cap and usage, and hashes of the prompt and the reply that went back to the server.”

One-click setup

{
  "tool": "record_ai_run",
  "arguments": {
    "model": "<model the client actually used>",
    "provider": "<provider>",
    "prompt_sha256": "<sha256 of the sampling messages and systemPrompt as sent>",
    "output_sha256": "<sha256 of the reply returned to the server>",
    "params": {
      "mcp_server": "<server name from your config>",
      "max_tokens_requested": 4000,
      "max_tokens_allowed": 1000,
      "stop_reason": "endTurn",
      "include_context": "none",
      "decision": "approved"
    },
    "ran_at": "2026-11-08T09:00:00Z"
  }
}

Rightshold records receipts and approvals you send it. It doesn't run inside your MCP client, see or block sampling requests, enforce token caps or meter your model usage. Not legal advice.

What are the sources?

Related

Not legal advice. JSON · Markdown · All answers

Get a free API key in one call

No card and no email needed. 100 free credits; the license and policy checks stay free without a key.

curl -s -X POST https://www.rightshold.com/v1/accounts/signup -H "Content-Type: application/json" -d '{"accept_terms":true}'
export RIGHTSHOLD_API_KEY=rs_...   # the api_key from the reply (shown once)
curl -s https://www.rightshold.com/v1/licenses/check -H "Authorization: Bearer $RIGHTSHOLD_API_KEY" \
  -H "Content-Type: application/json" -d '{"license":"MIT","intended_use":{"commercial_use":true}}'