rust-sdk/examples/servers/src/sampling_stdio.rs
Dale Seo 77932141e3
feat!: align model types with MCP 2025-11-25 spec (#927)
* feat!: align model types with MCP 2025-11-25 spec

* fix: complete 2025-11-25 model spec conformance
2026-06-24 20:41:13 -04:00

140 lines
4.9 KiB
Rust

#![allow(deprecated)]
use std::sync::Arc;
use anyhow::Result;
use rmcp::{
ServerHandler, ServiceExt,
model::*,
service::{RequestContext, RoleServer},
transport::stdio,
};
use tracing_subscriber::{self, EnvFilter};
/// Simple Sampling Demo Server
///
/// This server demonstrates how to request LLM sampling from clients.
/// Run with: cargo run -p mcp-server-examples --example servers_sampling_stdio
#[derive(Clone, Debug, Default)]
pub struct SamplingDemoServer;
impl ServerHandler for SamplingDemoServer {
fn get_info(&self) -> ServerInfo {
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
.with_instructions(concat!(
"This is a demo server that requests sampling from clients. It provides tools that use LLM capabilities.\n\n",
"IMPORTANT: This server requires a client that supports the 'sampling/createMessage' method. ",
"Without sampling support, the tools will return errors."
))
}
async fn call_tool(
&self,
request: CallToolRequestParams,
context: RequestContext<RoleServer>,
) -> Result<CallToolResult, ErrorData> {
match request.name.as_ref() {
"ask_llm" => {
// Get the question from arguments
let question = request
.arguments
.as_ref()
.and_then(|args| args.get("question"))
.and_then(|q| q.as_str())
.unwrap_or("Hello LLM");
let response = context
.peer
.create_message(
CreateMessageRequestParams::new(
vec![SamplingMessage::user_text(question)],
150,
)
.with_model_preferences(
ModelPreferences::new()
.with_hints(vec![ModelHint::new("claude")])
.with_cost_priority(0.3)
.with_speed_priority(0.8)
.with_intelligence_priority(0.7),
)
.with_system_prompt("You are a helpful assistant.")
.with_include_context(ContextInclusion::None)
.with_temperature(0.7),
)
.await
.map_err(|e| {
ErrorData::new(
ErrorCode::INTERNAL_ERROR,
format!("Sampling request failed: {}", e),
None,
)
})?;
tracing::debug!("Response: {:?}", response);
Ok(CallToolResult::success(vec![ContentBlock::text(format!(
"Question: {}\nAnswer: {}",
question,
response
.message
.content
.first()
.and_then(|c| c.as_text())
.map(|t| &t.text)
.unwrap_or(&"No text response".to_string())
))]))
}
_ => Err(ErrorData::new(
ErrorCode::INTERNAL_ERROR,
format!("Unknown tool: {}", request.name),
None,
)),
}
}
async fn list_tools(
&self,
_request: Option<PaginatedRequestParams>,
_context: RequestContext<RoleServer>,
) -> Result<ListToolsResult, ErrorData> {
Ok(ListToolsResult {
tools: vec![Tool::new(
"ask_llm",
"Ask a question to the LLM through sampling",
Arc::new(
serde_json::from_value(serde_json::json!({
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "The question to ask the LLM"
}
},
"required": ["question"]
}))
.unwrap(),
),
)],
meta: None,
next_cursor: None,
})
}
}
#[tokio::main]
async fn main() -> Result<()> {
// Initialize logging
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env().add_directive(tracing::Level::INFO.into()))
.with_writer(std::io::stderr)
.with_ansi(false)
.init();
tracing::info!("Starting Sampling Demo Server");
// Create and serve the sampling demo server
let service = SamplingDemoServer.serve(stdio()).await.inspect_err(|e| {
tracing::error!("Serving error: {:?}", e);
})?;
service.waiting().await?;
Ok(())
}