Skip to content

Commit f976f96

Browse files
committed
feat: implement SEP-1577 sampling with tools support
1 parent 8d09f88 commit f976f96

File tree

13 files changed

+1885
-191
lines changed

13 files changed

+1885
-191
lines changed

PR_DESCRIPTION.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Implement SEP-1577: Sampling With Tools
2+
3+
## Summary
4+
5+
This PR implements [SEP-1577: Sampling With Tools](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1577), which adds tool calling support to `sampling/createMessage` requests. This allows MCP servers to run agentic loops using the client's tokens while maintaining user supervision.
6+
7+
Closes #552
8+
9+
## Changes
10+
11+
### New Types
12+
13+
#### Tool Choice Configuration
14+
- **`ToolChoiceMode`**: Enum with values `Auto`, `Required`, `None` to control how the model handles tool calling
15+
- **`ToolChoice`**: Configuration struct with helper constructors (`ToolChoice::auto()`, `ToolChoice::required()`, `ToolChoice::none()`)
16+
17+
#### Tool Content Types
18+
- **`ToolUseContent`**: Represents a tool call request from the assistant
19+
- `id`: Unique identifier for the tool call
20+
- `name`: Name of the tool to invoke
21+
- `input`: Arguments for the tool call
22+
23+
- **`ToolResultContent`**: Represents tool execution results from the user
24+
- `tool_use_id`: References the original tool call
25+
- `content`: Result content blocks
26+
- `structured_content`: Optional structured output
27+
- `is_error`: Whether the tool execution failed
28+
29+
#### Sampling Content Wrapper
30+
- **`SamplingContent<T>`**: Generic wrapper supporting both single and array content per SEP-1577 spec
31+
- Implements `From<T>` and `From<Vec<T>>` for easy construction
32+
- Helper methods: `into_vec()`, `first()`, `iter()`, `is_empty()`, `len()`
33+
34+
- **`SamplingMessageContent`**: Unified enum for all sampling message content types
35+
- `Text`, `Image`, `Audio`, `ToolUse`, `ToolResult` variants
36+
- Accessor methods: `as_text()`, `as_tool_use()`, `as_tool_result()`
37+
38+
#### Sampling Capability
39+
- **`SamplingCapability`**: Structured capability for sampling support
40+
- `tools`: Enables `tools` and `toolChoice` parameters in CreateMessageRequest
41+
- `context`: Enables `includeContext` with values other than "none" (soft-deprecated per SEP-1577)
42+
43+
### Updated Types
44+
45+
#### `CreateMessageRequestParams`
46+
Added two new optional fields:
47+
- `tools: Option<Vec<Tool>>` - Tools available for the model to call
48+
- `tool_choice: Option<ToolChoice>` - Configuration for tool selection behavior
49+
50+
#### `CreateMessageResult`
51+
Added new stop reason constant:
52+
- `STOP_REASON_TOOL_USE = "toolUse"` - Indicates the model wants to use a tool
53+
54+
#### `SamplingMessage`
55+
- Updated `content` field from `Content` to `SamplingContent<SamplingMessageContent>`
56+
- Added convenience constructors:
57+
- `SamplingMessage::user_text(text)`
58+
- `SamplingMessage::assistant_text(text)`
59+
- `SamplingMessage::user_tool_result(tool_use_id, content)`
60+
- `SamplingMessage::assistant_tool_use(id, name, input)`
61+
62+
#### `ClientCapabilities`
63+
- Changed `sampling` field from `Option<JsonObject>` to `Option<SamplingCapability>`
64+
- Added builder methods:
65+
- `enable_sampling_tools()` - Advertise tool calling support
66+
- `enable_sampling_context()` - Advertise context inclusion support
67+
68+
## Usage Example
69+
70+
```rust
71+
use rmcp::model::*;
72+
73+
// Create a sampling request with tools
74+
let params = CreateMessageRequestParams {
75+
messages: vec![SamplingMessage::user_text("What's the weather in SF?")],
76+
tools: Some(vec![
77+
Tool::new("get_weather", "Get current weather", schema),
78+
]),
79+
tool_choice: Some(ToolChoice::auto()),
80+
max_tokens: 1000,
81+
// ... other fields
82+
};
83+
84+
// Handle tool use response
85+
if result.stop_reason == Some("toolUse".to_string()) {
86+
if let Some(tool_use) = result.message.content.first()
87+
.and_then(|c| c.as_tool_use())
88+
{
89+
// Execute the tool and send result back
90+
let tool_result = SamplingMessage::user_tool_result(
91+
&tool_use.id,
92+
vec![Content::text("72°F and sunny")],
93+
);
94+
}
95+
}
96+
97+
// Advertise capability
98+
let capabilities = ClientCapabilities::builder()
99+
.enable_sampling()
100+
.enable_sampling_tools()
101+
.build();
102+
```
103+
104+
## Testing
105+
106+
Added 15 new tests covering:
107+
- Tool choice serialization/deserialization
108+
- Sampling capability configuration
109+
- Tool use/result content serialization
110+
- Sampling messages with tool use/result
111+
- Create message result with tool use stop reason
112+
- Full sampling with tools workflow
113+
- Client capability builder methods
114+
115+
All existing tests updated to use the new API and continue to pass.
116+
117+
## Breaking Changes
118+
119+
- `SamplingMessage.content` type changed from `Content` to `SamplingContent<SamplingMessageContent>`
120+
- Migration: Use `SamplingMessage::user_text()` or `SamplingMessage::assistant_text()` helpers
121+
- Or access text via `message.content.first().and_then(|c| c.as_text())`
122+
123+
- `ClientCapabilities.sampling` type changed from `Option<JsonObject>` to `Option<SamplingCapability>`
124+
- Empty `{}` JSON still deserializes correctly to `SamplingCapability { tools: None, context: None }`
125+
126+
## Specification Reference
127+
128+
- SEP-1577: https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1577
129+
- Rust SDK Tracking Issue: https://github.com/modelcontextprotocol/rust-sdk/issues/552

0 commit comments

Comments
 (0)