Skip to content

Implement SEP-1577: Sampling With Tools#628

Open
DaleSeo wants to merge 2 commits intomodelcontextprotocol:mainfrom
DaleSeo:SEP-1577
Open

Implement SEP-1577: Sampling With Tools#628
DaleSeo wants to merge 2 commits intomodelcontextprotocol:mainfrom
DaleSeo:SEP-1577

Conversation

@DaleSeo
Copy link
Contributor

@DaleSeo DaleSeo commented Jan 24, 2026

Closes #552

Motivation and Context

This PR implements SEP-1577: Sampling With Tools, enabling MCP servers to run agentic loops using the client's LLM while maintaining user supervision.

Key additions:

  • ToolChoice / ToolChoiceMode - Control tool selection behavior
  • ToolUseContent / ToolResultContent - Tool calling content types
  • SamplingContent<T> - Single or array content wrapper
  • SamplingMessageContent - Unified content enum with ToolUse and ToolResult variants
  • SamplingCapability - Structured capability with tools and context sub-capabilities

Reference implementations:

How Has This Been Tested?

  • New unit tests covering serialization, deserialization, and API usage for all new types
  • All existing tests updated and passing
  • Backward compatibility tests verifying old JSON formats still deserialize correctly

Breaking Changes

The type signature of SamplingMessage.content changed from Content to SamplingContent<SamplingMessageContent>.

Migration Made Easy

Convenience constructors (recommended):

// Before
let msg = SamplingMessage { role: Role::User, content: Content::text("hi") };

// After - use the new helper methods
let msg = SamplingMessage::user_text("hi");
let msg = SamplingMessage::assistant_text("Hello");

Converting existing Content values:

use std::convert::TryInto;

let content: Content = Content::text("hello");

// Convert to SamplingMessageContent
let sampling_content: SamplingMessageContent = content.try_into()?;

// Or convert directly to SamplingContent<SamplingMessageContent>
let content: Content = Content::text("hello");
let wrapped: SamplingContent<SamplingMessageContent> = content.try_into()?;

Note: TryFrom is used because Content::Resource and Content::ResourceLink variants are not supported in sampling messages.

Wire Format Compatibility

  • Deserialization is backward compatible - Old JSON format (single content object) still deserializes correctly
  • ClientCapabilities.sampling - Empty {} JSON still deserializes to SamplingCapability

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

// Advertise capability
let capabilities = ClientCapabilities::builder()
    .enable_sampling()
    .enable_sampling_tools()
    .build();

// Create request with tools
let params = CreateMessageRequestParams {
    messages: vec![SamplingMessage::user_text("What's the weather?")],
    tools: Some(vec![weather_tool]),
    tool_choice: Some(ToolChoice::auto()),
    max_tokens: 1000,
    ..Default::default()
};

// Handle tool use response  
if result.stop_reason.as_deref() == Some(CreateMessageResult::STOP_REASON_TOOL_USE) {
    for content in result.message.content.iter() {
        if let Some(tool_use) = content.as_tool_use() {
            // Execute tool and continue conversation
        }
    }
}

@github-actions github-actions bot added T-test Testing related changes T-config Configuration file changes T-core Core library changes T-examples Example code changes T-model Model/data structure changes labels Jan 24, 2026
@github-actions github-actions bot added the T-documentation Documentation improvements label Jan 24, 2026
@github-actions github-actions bot removed the T-documentation Documentation improvements label Jan 24, 2026
@DaleSeo DaleSeo marked this pull request as ready for review January 25, 2026 02:37
@DaleSeo DaleSeo force-pushed the SEP-1577 branch 6 times, most recently from c2da172 to f037e01 Compare January 25, 2026 03:08
@DaleSeo
Copy link
Contributor Author

DaleSeo commented Jan 28, 2026

Hi @alexhancock, could you please take a look at this PR? Thanks!

@alexhancock alexhancock self-requested a review January 30, 2026 20:20
@alexhancock
Copy link
Collaborator

@DaleSeo Sorry it took me a bit. I'm glad to have support for this, but a little worried about the breaking change. Can you think of any alternatives that would keep compat, or make it a but lighter for updaters?

Interested to discuss!

@DaleSeo DaleSeo force-pushed the SEP-1577 branch 2 times, most recently from 95e640e to 73e60d7 Compare February 4, 2026 21:56
@DaleSeo
Copy link
Contributor Author

DaleSeo commented Feb 4, 2026

Thanks for the feedback, @alexhancock! I've made a few updates to simplify the migration:

  1. I added TryFrom conversions for existing Content values:
use std::convert::TryInto;

let content: Content = Content::text("hello");
let sampling_content: SamplingMessageContent = content.try_into()?;
  1. The wire format is backward compatible. The old JSON (single content object) still deserializes correctly, so existing serialized data will remain intact.

  2. I created convenience constructors to make common cases easier:

// Before
SamplingMessage { role: Role::User, content: Content::text("hi") }

// After
SamplingMessage::user_text("hi")

The type change is unavoidable to support tool use and result content types from SEP-1577, but I think the migration path is now reasonable. Most users can either use the new constructors or add .try_into() to their existing code.

Let me know if you have any other suggestions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-config Configuration file changes T-core Core library changes T-examples Example code changes T-model Model/data structure changes T-test Testing related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement SEP-1577: Sampling With Tools

2 participants