{
  "question": "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?",
  "slug": "how-to-handle-mcp-sampling-requests-without-leaking-secrets-or-burning-model-quota",
  "url": "https://www.rightshold.com/answers/how-to-handle-mcp-sampling-requests-without-leaking-secrets-or-burning-model-quota",
  "short_answer": "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.",
  "checklist": [
    "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).",
    "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.",
    "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.",
    "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.",
    "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.",
    "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.",
    "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.",
    "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."
  ],
  "sections": [
    {
      "heading": "What sampling hands to a server",
      "points": [
        "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.",
        "The server chooses the messages, an optional systemPrompt, model preferences and maxTokens. The client may change or ignore systemPrompt, temperature, stopSequences and metadata, and maps model hints to whatever model it has. The completion goes back to the server, so anything the model writes, including text it copied from the context you gave it, leaves your machine.",
        "SEP-2577 names sampling as the most security-sensitive of the three features it deprecates (with Roots and Logging), because it creates attack surface for prompt injection and data exfiltration, and notes few clients ever supported it."
      ]
    },
    {
      "heading": "Approve, cap and log in the client",
      "points": [
        "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.",
        "Put the cost limits where the server can't argue with them: a client-side maxTokens ceiling, a per-server request rate, a per-day token budget and an iteration cap for tool loops. A denied or failed request needs no special reply; under the InputRequiredResult pattern the client simply doesn't retry.",
        "For tool-enabled sampling, the tools in the request are defined by the server for that request and don't have to match its registered tools. Treat them like any other unreviewed tool list, and keep tool calls visible to the user."
      ]
    },
    {
      "heading": "Migrating off sampling",
      "points": [
        "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.",
        "Deprecation doesn't break anything yet. Wire behavior is unchanged during the window, and the registry lists the earliest removal as the first revision released on or after 2027-07-28. What changes is that new clients have little reason to add support.",
        "After migrating, the server pays for its own completions, so the user's prompts and secrets aren't routed through it unless you send them. Send the provider only what the task needs, and give the server its own budget and key."
      ]
    }
  ],
  "checks": [],
  "ask_rightshold": {
    "start": "https://www.rightshold.com/start?ref=seo",
    "mcp_endpoint": "https://www.rightshold.com/mcp",
    "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.",
    "mcp_call": {
      "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"
      }
    },
    "also": [
      "Needs an API key. Only the hashes and fields you send are recorded, never the prompt or reply. The receipt proves what was recorded and when, not that the request was safe.",
      "verify_ai_run is free: anyone with the receipt id can check it, and can add the original text to compare it with the recorded hash.",
      "To make a person approve requests above a budget, request_human_approval records the ask and a signed yes or no that verify_human_approval can check later."
    ],
    "scope": "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."
  },
  "sources": [
    {
      "title": "MCP specification 2026-07-28: Sampling (deprecation notice, human in the loop, maxTokens, includeContext, security considerations)",
      "url": "https://modelcontextprotocol.io/specification/2026-07-28/client/sampling"
    },
    {
      "title": "MCP specification 2026-07-28: Deprecated Features registry",
      "url": "https://modelcontextprotocol.io/specification/2026-07-28/deprecated"
    },
    {
      "title": "SEP-2577: Deprecate Roots, Sampling, and Logging",
      "url": "https://modelcontextprotocol.io/seps/2577-deprecate-roots-sampling-and-logging"
    },
    {
      "title": "MCP TypeScript SDK v2: Sampling (deprecated, replace with a direct provider call)",
      "url": "https://ts.sdk.modelcontextprotocol.io/v2/servers/sampling.html"
    },
    {
      "title": "MCP specification 2026-07-28: Tools (input required results)",
      "url": "https://modelcontextprotocol.io/specification/2026-07-28/server/tools"
    }
  ],
  "related": [
    {
      "title": "How to ask the user to confirm an action from an MCP server with elicitation",
      "url": "https://www.rightshold.com/answers/how-to-ask-the-user-to-confirm-an-action-from-an-mcp-server-with-elicitation"
    },
    {
      "title": "How to stop an AI agent from looping forever and burning tokens",
      "url": "https://www.rightshold.com/answers/how-to-stop-an-ai-agent-from-looping-forever-and-burning-tokens"
    },
    {
      "title": "How to set a separate budget for each AI agent calling paid APIs",
      "url": "https://www.rightshold.com/answers/how-to-set-a-separate-budget-for-each-ai-agent-calling-paid-apis"
    },
    {
      "title": "How to prevent MCP tool shadowing between servers",
      "url": "https://www.rightshold.com/answers/how-to-prevent-mcp-tool-shadowing-between-servers"
    }
  ],
  "computed_from": "Curated guidance; license verdicts computed from Rightshold's license profiles at render time. Not legal advice.",
  "updated": "2026-11-08"
}