Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 1 addition & 81 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -823,8 +823,7 @@ This enables servers to leverage the client's LLM capabilities without needing d
**Using Sampling in Tools:**

Tools that accept a `server_context:` parameter can call `create_sampling_message` on it.
The request is automatically routed to the correct client session.
Set `server.server_context = server` so that `server_context.create_sampling_message` delegates to the server:
The request is automatically routed to the correct client session:

```ruby
class SummarizeTool < MCP::Tool
Expand Down Expand Up @@ -852,7 +851,6 @@ class SummarizeTool < MCP::Tool
end

server = MCP::Server.new(name: "my_server", tools: [SummarizeTool])
server.server_context = server
```

**Parameters:**
Expand All @@ -873,86 +871,8 @@ Optional:
- `tools:` (Array) - Tools available to the LLM (requires `sampling.tools` capability)
- `tool_choice:` (Hash) - Tool selection mode (e.g., `{ mode: "auto" }`)

**Direct Usage:**

`Server#create_sampling_message` can also be called directly outside of tools:

```ruby
result = server.create_sampling_message(
messages: [
{ role: "user", content: { type: "text", text: "What is the capital of France?" } }
],
max_tokens: 100,
system_prompt: "You are a helpful assistant.",
temperature: 0.7
)
```

Result contains the LLM response:

```ruby
{
role: "assistant",
content: { type: "text", text: "The capital of France is Paris." },
model: "claude-3-sonnet-20240307",
stopReason: "endTurn"
}
```

For multi-client transports (e.g., `StreamableHTTPTransport`), use `server_context.create_sampling_message` inside tools
to route the request to the correct client session.

**Tool Use in Sampling:**

When tools are provided in a sampling request, the LLM can call them during generation.
The server must handle tool calls and continue the conversation with tool results:

```ruby
result = server.create_sampling_message(
messages: [
{ role: "user", content: { type: "text", text: "What's the weather in Paris?" } }
],
max_tokens: 1000,
tools: [
{
name: "get_weather",
description: "Get weather for a city",
inputSchema: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"]
}
}
],
tool_choice: { mode: "auto" }
)

if result[:stopReason] == "toolUse"
tool_results = result[:content].map do |tool_use|
weather_data = get_weather(tool_use[:input][:city])

{
type: "tool_result",
toolUseId: tool_use[:id],
content: [{ type: "text", text: weather_data.to_json }]
}
end

final_result = server.create_sampling_message(
messages: [
{ role: "user", content: { type: "text", text: "What's the weather in Paris?" } },
{ role: "assistant", content: result[:content] },
{ role: "user", content: tool_results }
],
max_tokens: 1000,
tools: [...]
)
end
```

**Error Handling:**

- Raises `RuntimeError` if transport is not set
- Raises `RuntimeError` if client does not support `sampling` capability
- Raises `RuntimeError` if `tools` are used but client lacks `sampling.tools` capability
- Raises `StandardError` if client returns an error response
Expand Down
38 changes: 0 additions & 38 deletions lib/mcp/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -206,44 +206,6 @@ def notify_log_message(data:, level:, logger: nil)
report_exception(e, { notification: "log_message" })
end

# Sends a `sampling/createMessage` request to the client.
# For single-client transports (e.g., `StdioTransport`). For multi-client transports
# (e.g., `StreamableHTTPTransport`), use `ServerSession#create_sampling_message` instead
# to ensure the request is routed to the correct client.
def create_sampling_message(
messages:,
max_tokens:,
system_prompt: nil,
model_preferences: nil,
include_context: nil,
temperature: nil,
stop_sequences: nil,
metadata: nil,
tools: nil,
tool_choice: nil,
related_request_id: nil
)
unless @transport
raise "Cannot send sampling request without a transport."
end

params = build_sampling_params(
@client_capabilities,
messages: messages,
max_tokens: max_tokens,
system_prompt: system_prompt,
model_preferences: model_preferences,
include_context: include_context,
temperature: temperature,
stop_sequences: stop_sequences,
metadata: metadata,
tools: tools,
tool_choice: tool_choice,
)

@transport.send_request(Methods::SAMPLING_CREATE_MESSAGE, params)
end

# Sets a custom handler for `resources/read` requests.
# The block receives the parsed request params and should return resource
# contents. The return value is set as the `contents` field of the response.
Expand Down
Loading