Skip to content

Python: [BREAKING] Add sampling guardrails to MCP tools#6413

Merged
eavanvalkenburg merged 2 commits into
microsoft:mainfrom
eavanvalkenburg:fix_sampling
Jun 10, 2026
Merged

Python: [BREAKING] Add sampling guardrails to MCP tools#6413
eavanvalkenburg merged 2 commits into
microsoft:mainfrom
eavanvalkenburg:fix_sampling

Conversation

@eavanvalkenburg

Copy link
Copy Markdown
Member

Motivation and Context

The MCP tools (MCPStdioTool, MCPStreamableHTTPTool, MCPWebsocketTool)
register a sampling_callback whenever they are configured with a chat
client. Previously every server-initiated sampling/createMessage request
was forwarded to the chat client as-is, with no caller control over whether
the request was allowed, how many tokens it could request, or how many
requests a session could make.

Description

Adds opt-in guardrails to the MCP sampling callback, exposed as three new
constructor parameters on MCPTool and its subclasses, positioned directly
after client:

  • sampling_approval_callback: a sync or async gate invoked with the raw
    CreateMessageRequestParams before the request reaches the chat client.
    A truthy return approves the request, a falsy return denies it. When
    None (the default) every sampling request is denied.
  • sampling_max_tokens: clamps an approved request's maxTokens to
    min(requested, cap). Defaults to 4096; None disables the clamp.
  • sampling_max_requests: per-session cap on the number of sampling
    requests, reset on reconnect. Defaults to 25; None disables the cap.

Additional changes:

  • Incoming sampling requests are logged at WARNING level (counts only, no
    content).
  • SamplingApprovalCallback is exported from the public API.
  • Added unit tests, a sample (mcp_sampling_approval.py), and documentation
    updates.

This is a breaking change: by default, sampling requests are now denied
unless a sampling_approval_callback is supplied. To restore the previous
auto-approve behavior, pass sampling_approval_callback=lambda params: True.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

Copilot AI review requested due to automatic review settings June 9, 2026 12:08
@moonbox3 moonbox3 added documentation Improvements or additions to documentation python labels Jun 9, 2026
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework
   _mcp.py11799292%306, 325, 517, 577–578, 707, 769, 782, 806–807, 826–829, 831–832, 836, 862, 896–898, 900, 953–955, 1014–1015, 1284, 1325–1326, 1339, 1342, 1351–1352, 1357–1358, 1364, 1412–1413, 1429–1430, 1439–1440, 1445–1446, 1452, 1527, 1530, 1557, 1580–1584, 1607–1609, 1614, 1618–1619, 1721, 1728, 1730, 1743, 1749, 1811, 1826–1827, 1834–1835, 1840–1841, 1846, 1850, 1865, 1927, 2110, 2112, 2134, 2136–2139, 2152–2153, 2197, 2259, 2646–2647, 2863–2864, 2882
TOTAL38615441788% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
7749 34 💤 0 ❌ 0 🔥 1m 49s ⏱️

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds opt-in safety guardrails around server-initiated MCP sampling (sampling/createMessage) in the Python Agent Framework MCP tools, making sampling deny-by-default unless explicitly approved by the caller.

Changes:

  • Introduces sampling_approval_callback, sampling_max_tokens, and sampling_max_requests constructor parameters on MCPTool and subclasses, and enforces them in the default sampling_callback.
  • Exports SamplingApprovalCallback in the public Python API and documents the new deny-by-default behavior (including changelog entry).
  • Adds unit tests plus a new sample demonstrating human-in-the-loop sampling approval.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
python/samples/02-agents/mcp/README.md Adds the new sampling-approval sample to the MCP samples index.
python/samples/02-agents/mcp/mcp_sampling_approval.py New sample showing approval gating + token/request caps for MCP sampling.
python/packages/core/tests/core/test_mcp.py Adds coverage for deny-by-default, callback approval/denial, maxTokens clamping, and per-session request caps.
python/packages/core/AGENTS.md Documents the sampling guardrails and their default behavior/order of enforcement.
python/packages/core/agent_framework/_mcp.py Implements sampling guardrails (approval gate, maxTokens cap, per-session request cap) and warning-level logging (counts only).
python/packages/core/agent_framework/init.py Exports SamplingApprovalCallback from the package public surface.
python/CHANGELOG.md Documents the breaking deny-by-default MCP sampling behavior under Security.

Comment thread python/packages/core/agent_framework/_mcp.py Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review

Reviewers: 5 | Confidence: 89% | Result: All clear

Reviewed: Correctness, Security Reliability, Test Coverage, Failure Modes, Design Approach


Automated review by eavanvalkenburg's agents

@eavanvalkenburg eavanvalkenburg enabled auto-merge June 9, 2026 15:28
Comment thread python/CHANGELOG.md Outdated
eavanvalkenburg and others added 2 commits June 10, 2026 11:15
Add approval, token, and request-count controls to the MCP sampling
callback used when an MCPTool is configured with a chat client.

- Add `sampling_approval_callback`, `sampling_max_tokens`, and
  `sampling_max_requests` parameters to `MCPTool` and its
  `MCPStdioTool`, `MCPStreamableHTTPTool`, and `MCPWebsocketTool`
  subclasses, positioned directly after `client`.
- Gate each server-initiated `sampling/createMessage` request behind the
  approval callback, which denies by default when no callback is provided.
- Clamp the requested `maxTokens` to `sampling_max_tokens` and enforce a
  per-session request count via `sampling_max_requests`.
- Log incoming sampling requests at WARNING level (counts only).
- Export `SamplingApprovalCallback` from the public API.
- Add tests, a sample, and documentation updates.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Distinguish the deny-by-default case (no approval callback configured)
from an explicit denial by a configured `sampling_approval_callback`, so
the returned ErrorData message is accurate for callback-driven denials
and exceptions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eavanvalkenburg eavanvalkenburg added this pull request to the merge queue Jun 10, 2026
Merged via the queue into microsoft:main with commit 9a56bc9 Jun 10, 2026
38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants