docs: align README examples with v2 model API (#928)
This commit is contained in:
parent
f1daa9277e
commit
f07ee4ae1a
2 changed files with 188 additions and 256 deletions
210
README.md
210
README.md
|
|
@ -239,12 +239,11 @@ struct MyServer;
|
||||||
|
|
||||||
impl ServerHandler for MyServer {
|
impl ServerHandler for MyServer {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerInfo {
|
||||||
ServerInfo {
|
ServerInfo::new(
|
||||||
capabilities: ServerCapabilities::builder()
|
ServerCapabilities::builder()
|
||||||
.enable_resources()
|
.enable_resources()
|
||||||
.build(),
|
.build(),
|
||||||
..Default::default()
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list_resources(
|
async fn list_resources(
|
||||||
|
|
@ -254,8 +253,8 @@ impl ServerHandler for MyServer {
|
||||||
) -> Result<ListResourcesResult, McpError> {
|
) -> Result<ListResourcesResult, McpError> {
|
||||||
Ok(ListResourcesResult {
|
Ok(ListResourcesResult {
|
||||||
resources: vec![
|
resources: vec![
|
||||||
RawResource::new("file:///config.json", "config").no_annotation(),
|
Resource::new("file:///config.json", "config"),
|
||||||
RawResource::new("memo://insights", "insights").no_annotation(),
|
Resource::new("memo://insights", "insights"),
|
||||||
],
|
],
|
||||||
next_cursor: None,
|
next_cursor: None,
|
||||||
meta: None,
|
meta: None,
|
||||||
|
|
@ -268,12 +267,12 @@ impl ServerHandler for MyServer {
|
||||||
_context: RequestContext<RoleServer>,
|
_context: RequestContext<RoleServer>,
|
||||||
) -> Result<ReadResourceResult, McpError> {
|
) -> Result<ReadResourceResult, McpError> {
|
||||||
match request.uri.as_str() {
|
match request.uri.as_str() {
|
||||||
"file:///config.json" => Ok(ReadResourceResult {
|
"file:///config.json" => Ok(ReadResourceResult::new(vec![
|
||||||
contents: vec![ResourceContents::text(r#"{"key": "value"}"#, &request.uri)],
|
ResourceContents::text(r#"{"key": "value"}"#, &request.uri),
|
||||||
}),
|
])),
|
||||||
"memo://insights" => Ok(ReadResourceResult {
|
"memo://insights" => Ok(ReadResourceResult::new(vec![
|
||||||
contents: vec![ResourceContents::text("Analysis results...", &request.uri)],
|
ResourceContents::text("Analysis results...", &request.uri),
|
||||||
}),
|
])),
|
||||||
_ => Err(McpError::resource_not_found(
|
_ => Err(McpError::resource_not_found(
|
||||||
"resource_not_found",
|
"resource_not_found",
|
||||||
Some(json!({ "uri": request.uri })),
|
Some(json!({ "uri": request.uri })),
|
||||||
|
|
@ -304,10 +303,9 @@ use rmcp::model::{ReadResourceRequestParams};
|
||||||
let resources = client.list_all_resources().await?;
|
let resources = client.list_all_resources().await?;
|
||||||
|
|
||||||
// Read a specific resource by URI
|
// Read a specific resource by URI
|
||||||
let result = client.read_resource(ReadResourceRequestParams {
|
let result = client.read_resource(
|
||||||
meta: None,
|
ReadResourceRequestParams::new("file:///config.json"),
|
||||||
uri: "file:///config.json".into(),
|
).await?;
|
||||||
}).await?;
|
|
||||||
|
|
||||||
// List resource templates
|
// List resource templates
|
||||||
let templates = client.list_all_resource_templates().await?;
|
let templates = client.list_all_resource_templates().await?;
|
||||||
|
|
@ -322,9 +320,9 @@ Servers can notify clients when the resource list changes or when a specific res
|
||||||
context.peer.notify_resource_list_changed().await?;
|
context.peer.notify_resource_list_changed().await?;
|
||||||
|
|
||||||
// Notify that a specific resource was updated
|
// Notify that a specific resource was updated
|
||||||
context.peer.notify_resource_updated(ResourceUpdatedNotificationParam {
|
context.peer.notify_resource_updated(
|
||||||
uri: "file:///config.json".into(),
|
ResourceUpdatedNotificationParam::new("file:///config.json"),
|
||||||
}).await?;
|
).await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
Clients handle these via `ClientHandler`:
|
Clients handle these via `ClientHandler`:
|
||||||
|
|
@ -397,7 +395,7 @@ impl MyServer {
|
||||||
#[prompt(name = "greeting", description = "A simple greeting")]
|
#[prompt(name = "greeting", description = "A simple greeting")]
|
||||||
async fn greeting(&self) -> Vec<PromptMessage> {
|
async fn greeting(&self) -> Vec<PromptMessage> {
|
||||||
vec![PromptMessage::new_text(
|
vec![PromptMessage::new_text(
|
||||||
PromptMessageRole::User,
|
Role::User,
|
||||||
"Hello! How can you help me today?",
|
"Hello! How can you help me today?",
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
|
|
@ -411,25 +409,20 @@ impl MyServer {
|
||||||
let focus = args.focus_areas
|
let focus = args.focus_areas
|
||||||
.unwrap_or_else(|| vec!["correctness".into()]);
|
.unwrap_or_else(|| vec!["correctness".into()]);
|
||||||
|
|
||||||
Ok(GetPromptResult {
|
Ok(GetPromptResult::new(vec![
|
||||||
description: Some(format!("Code review for {}", args.language)),
|
|
||||||
messages: vec![
|
|
||||||
PromptMessage::new_text(
|
PromptMessage::new_text(
|
||||||
PromptMessageRole::User,
|
Role::User,
|
||||||
format!("Review my {} code. Focus on: {}", args.language, focus.join(", ")),
|
format!("Review my {} code. Focus on: {}", args.language, focus.join(", ")),
|
||||||
),
|
),
|
||||||
],
|
])
|
||||||
})
|
.with_description(format!("Code review for {}", args.language)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[prompt_handler]
|
#[prompt_handler]
|
||||||
impl ServerHandler for MyServer {
|
impl ServerHandler for MyServer {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerInfo {
|
||||||
ServerInfo {
|
ServerInfo::new(ServerCapabilities::builder().enable_prompts().build())
|
||||||
capabilities: ServerCapabilities::builder().enable_prompts().build(),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -485,25 +478,22 @@ Access the client's sampling capability through `context.peer.create_message()`:
|
||||||
use rmcp::model::*;
|
use rmcp::model::*;
|
||||||
|
|
||||||
// Inside a ServerHandler method (e.g., call_tool):
|
// Inside a ServerHandler method (e.g., call_tool):
|
||||||
let response = context.peer.create_message(CreateMessageRequestParams {
|
let response = context.peer.create_message(
|
||||||
meta: None,
|
CreateMessageRequestParams::new(
|
||||||
task: None,
|
vec![SamplingMessage::user_text("Explain this error: connection refused")],
|
||||||
messages: vec![SamplingMessage::user_text("Explain this error: connection refused")],
|
150,
|
||||||
model_preferences: Some(ModelPreferences {
|
)
|
||||||
hints: Some(vec![ModelHint { name: Some("claude".into()) }]),
|
.with_model_preferences(
|
||||||
cost_priority: Some(0.3),
|
ModelPreferences::new()
|
||||||
speed_priority: Some(0.8),
|
.with_hints(vec![ModelHint::new("claude")])
|
||||||
intelligence_priority: Some(0.7),
|
.with_cost_priority(0.3)
|
||||||
}),
|
.with_speed_priority(0.8)
|
||||||
system_prompt: Some("You are a helpful assistant.".into()),
|
.with_intelligence_priority(0.7),
|
||||||
include_context: Some(ContextInclusion::None),
|
)
|
||||||
temperature: Some(0.7),
|
.with_system_prompt("You are a helpful assistant.")
|
||||||
max_tokens: 150,
|
.with_include_context(ContextInclusion::None)
|
||||||
stop_sequences: None,
|
.with_temperature(0.7),
|
||||||
metadata: None,
|
).await?;
|
||||||
tools: None,
|
|
||||||
tool_choice: None,
|
|
||||||
}).await?;
|
|
||||||
|
|
||||||
// Extract the response text
|
// Extract the response text
|
||||||
let text = response.message.content
|
let text = response.message.content
|
||||||
|
|
@ -531,11 +521,11 @@ impl ClientHandler for MyClient {
|
||||||
// Forward to your LLM, or return a mock response:
|
// Forward to your LLM, or return a mock response:
|
||||||
let response_text = call_your_llm(¶ms.messages).await;
|
let response_text = call_your_llm(¶ms.messages).await;
|
||||||
|
|
||||||
Ok(CreateMessageResult {
|
Ok(CreateMessageResult::new(
|
||||||
message: SamplingMessage::assistant_text(response_text),
|
SamplingMessage::assistant_text(response_text),
|
||||||
model: "my-model".into(),
|
"my-model".into(),
|
||||||
stop_reason: Some(CreateMessageResult::STOP_REASON_END_TURN.into()),
|
)
|
||||||
})
|
.with_stop_reason(CreateMessageResult::STOP_REASON_END_TURN))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -593,14 +583,9 @@ impl ClientHandler for MyClient {
|
||||||
&self,
|
&self,
|
||||||
_context: RequestContext<RoleClient>,
|
_context: RequestContext<RoleClient>,
|
||||||
) -> Result<ListRootsResult, ErrorData> {
|
) -> Result<ListRootsResult, ErrorData> {
|
||||||
Ok(ListRootsResult {
|
Ok(ListRootsResult::new(vec![
|
||||||
roots: vec![
|
Root::new("file:///home/user/project").with_name("My Project"),
|
||||||
Root {
|
]))
|
||||||
uri: "file:///home/user/project".into(),
|
|
||||||
name: Some("My Project".into()),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -631,12 +616,11 @@ use rmcp::{ServerHandler, model::*, service::RequestContext};
|
||||||
|
|
||||||
impl ServerHandler for MyServer {
|
impl ServerHandler for MyServer {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerInfo {
|
||||||
ServerInfo {
|
ServerInfo::new(
|
||||||
capabilities: ServerCapabilities::builder()
|
ServerCapabilities::builder()
|
||||||
.enable_logging()
|
.enable_logging()
|
||||||
.build(),
|
.build(),
|
||||||
..Default::default()
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client sets the minimum log level
|
// Client sets the minimum log level
|
||||||
|
|
@ -651,14 +635,16 @@ impl ServerHandler for MyServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a log message from any handler with access to the peer:
|
// Send a log message from any handler with access to the peer:
|
||||||
context.peer.notify_logging_message(LoggingMessageNotificationParam {
|
context.peer.notify_logging_message(
|
||||||
level: LoggingLevel::Info,
|
LoggingMessageNotificationParam::new(
|
||||||
logger: Some("my-server".into()),
|
LoggingLevel::Info,
|
||||||
data: serde_json::json!({
|
serde_json::json!({
|
||||||
"message": "Processing completed",
|
"message": "Processing completed",
|
||||||
"items_processed": 42
|
"items_processed": 42
|
||||||
}),
|
}),
|
||||||
}).await?;
|
)
|
||||||
|
.with_logger("my-server"),
|
||||||
|
).await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
Available log levels (from least to most severe): `Debug`, `Info`, `Notice`, `Warning`, `Error`, `Critical`, `Alert`, `Emergency`.
|
Available log levels (from least to most severe): `Debug`, `Info`, `Notice`, `Warning`, `Error`, `Critical`, `Alert`, `Emergency`.
|
||||||
|
|
@ -683,10 +669,7 @@ impl ClientHandler for MyClient {
|
||||||
Clients can also set the server's log level:
|
Clients can also set the server's log level:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
client.set_level(SetLevelRequestParams {
|
client.set_level(SetLevelRequestParams::new(LoggingLevel::Warning)).await?;
|
||||||
level: LoggingLevel::Warning,
|
|
||||||
meta: None,
|
|
||||||
}).await?;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -706,13 +689,12 @@ use rmcp::{ErrorData as McpError, ServerHandler, model::*, service::RequestConte
|
||||||
|
|
||||||
impl ServerHandler for MyServer {
|
impl ServerHandler for MyServer {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerInfo {
|
||||||
ServerInfo {
|
ServerInfo::new(
|
||||||
capabilities: ServerCapabilities::builder()
|
ServerCapabilities::builder()
|
||||||
.enable_completions()
|
.enable_completions()
|
||||||
.enable_prompts()
|
.enable_prompts()
|
||||||
.build(),
|
.build(),
|
||||||
..Default::default()
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn complete(
|
async fn complete(
|
||||||
|
|
@ -750,13 +732,9 @@ impl ServerHandler for MyServer {
|
||||||
.filter(|v| v.to_lowercase().contains(&request.argument.value.to_lowercase()))
|
.filter(|v| v.to_lowercase().contains(&request.argument.value.to_lowercase()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(CompleteResult {
|
let completion = CompletionInfo::with_pagination(filtered, None, false)
|
||||||
completion: CompletionInfo {
|
.map_err(|e| McpError::internal_error(e, None))?;
|
||||||
values: filtered,
|
Ok(CompleteResult::new(completion))
|
||||||
total: None,
|
|
||||||
has_more: Some(false),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -766,17 +744,10 @@ impl ServerHandler for MyServer {
|
||||||
```rust
|
```rust
|
||||||
use rmcp::model::*;
|
use rmcp::model::*;
|
||||||
|
|
||||||
let result = client.complete(CompleteRequestParams {
|
let result = client.complete(CompleteRequestParams::new(
|
||||||
meta: None,
|
Reference::for_prompt("sql_query"),
|
||||||
r#ref: Reference::Prompt(PromptReference {
|
ArgumentInfo::new("operation", "SEL"),
|
||||||
name: "sql_query".into(),
|
)).await?;
|
||||||
}),
|
|
||||||
argument: ArgumentInfo {
|
|
||||||
name: "operation".into(),
|
|
||||||
value: "SEL".into(),
|
|
||||||
},
|
|
||||||
context: None,
|
|
||||||
}).await?;
|
|
||||||
|
|
||||||
// result.completion.values contains suggestions like ["SELECT"]
|
// result.completion.values contains suggestions like ["SELECT"]
|
||||||
```
|
```
|
||||||
|
|
@ -802,12 +773,14 @@ use rmcp::model::*;
|
||||||
for i in 0..total_items {
|
for i in 0..total_items {
|
||||||
process_item(i).await;
|
process_item(i).await;
|
||||||
|
|
||||||
context.peer.notify_progress(ProgressNotificationParam {
|
context.peer.notify_progress(
|
||||||
progress_token: ProgressToken(NumberOrString::Number(i as i64)),
|
ProgressNotificationParam::new(
|
||||||
progress: i as f64,
|
ProgressToken(NumberOrString::Number(i as i64)),
|
||||||
total: Some(total_items as f64),
|
i as f64,
|
||||||
message: Some(format!("Processing item {}/{}", i + 1, total_items)),
|
)
|
||||||
}).await?;
|
.with_total(total_items as f64)
|
||||||
|
.with_message(format!("Processing item {}/{}", i + 1, total_items)),
|
||||||
|
).await?;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -817,10 +790,10 @@ Either side can cancel an in-progress request:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Send a cancellation
|
// Send a cancellation
|
||||||
context.peer.notify_cancelled(CancelledNotificationParam {
|
context.peer.notify_cancelled(CancelledNotificationParam::new(
|
||||||
request_id: the_request_id,
|
Some(the_request_id),
|
||||||
reason: Some("User requested cancellation".into()),
|
Some("User requested cancellation".into()),
|
||||||
}).await?;
|
)).await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
Handle cancellation in `ServerHandler` or `ClientHandler`:
|
Handle cancellation in `ServerHandler` or `ClientHandler`:
|
||||||
|
|
@ -891,13 +864,12 @@ struct MyServer {
|
||||||
|
|
||||||
impl ServerHandler for MyServer {
|
impl ServerHandler for MyServer {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerInfo {
|
||||||
ServerInfo {
|
ServerInfo::new(
|
||||||
capabilities: ServerCapabilities::builder()
|
ServerCapabilities::builder()
|
||||||
.enable_resources()
|
.enable_resources()
|
||||||
.enable_resources_subscribe()
|
.enable_resources_subscribe()
|
||||||
.build(),
|
.build(),
|
||||||
..Default::default()
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn subscribe(
|
async fn subscribe(
|
||||||
|
|
@ -924,9 +896,9 @@ When a subscribed resource changes, notify the client:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Check if the resource has subscribers, then notify
|
// Check if the resource has subscribers, then notify
|
||||||
context.peer.notify_resource_updated(ResourceUpdatedNotificationParam {
|
context.peer.notify_resource_updated(
|
||||||
uri: "file:///config.json".into(),
|
ResourceUpdatedNotificationParam::new("file:///config.json"),
|
||||||
}).await?;
|
).await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Client-side
|
### Client-side
|
||||||
|
|
@ -935,16 +907,10 @@ context.peer.notify_resource_updated(ResourceUpdatedNotificationParam {
|
||||||
use rmcp::model::*;
|
use rmcp::model::*;
|
||||||
|
|
||||||
// Subscribe to updates for a resource
|
// Subscribe to updates for a resource
|
||||||
client.subscribe(SubscribeRequestParams {
|
client.subscribe(SubscribeRequestParams::new("file:///config.json")).await?;
|
||||||
meta: None,
|
|
||||||
uri: "file:///config.json".into(),
|
|
||||||
}).await?;
|
|
||||||
|
|
||||||
// Unsubscribe when no longer needed
|
// Unsubscribe when no longer needed
|
||||||
client.unsubscribe(UnsubscribeRequestParams {
|
client.unsubscribe(UnsubscribeRequestParams::new("file:///config.json")).await?;
|
||||||
meta: None,
|
|
||||||
uri: "file:///config.json".into(),
|
|
||||||
}).await?;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Handle update notifications in `ClientHandler`:
|
Handle update notifications in `ClientHandler`:
|
||||||
|
|
|
||||||
|
|
@ -237,12 +237,11 @@ struct MyServer;
|
||||||
|
|
||||||
impl ServerHandler for MyServer {
|
impl ServerHandler for MyServer {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerInfo {
|
||||||
ServerInfo {
|
ServerInfo::new(
|
||||||
capabilities: ServerCapabilities::builder()
|
ServerCapabilities::builder()
|
||||||
.enable_resources()
|
.enable_resources()
|
||||||
.build(),
|
.build(),
|
||||||
..Default::default()
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list_resources(
|
async fn list_resources(
|
||||||
|
|
@ -252,8 +251,8 @@ impl ServerHandler for MyServer {
|
||||||
) -> Result<ListResourcesResult, McpError> {
|
) -> Result<ListResourcesResult, McpError> {
|
||||||
Ok(ListResourcesResult {
|
Ok(ListResourcesResult {
|
||||||
resources: vec![
|
resources: vec![
|
||||||
RawResource::new("file:///config.json", "config").no_annotation(),
|
Resource::new("file:///config.json", "config"),
|
||||||
RawResource::new("memo://insights", "insights").no_annotation(),
|
Resource::new("memo://insights", "insights"),
|
||||||
],
|
],
|
||||||
next_cursor: None,
|
next_cursor: None,
|
||||||
meta: None,
|
meta: None,
|
||||||
|
|
@ -266,12 +265,12 @@ impl ServerHandler for MyServer {
|
||||||
_context: RequestContext<RoleServer>,
|
_context: RequestContext<RoleServer>,
|
||||||
) -> Result<ReadResourceResult, McpError> {
|
) -> Result<ReadResourceResult, McpError> {
|
||||||
match request.uri.as_str() {
|
match request.uri.as_str() {
|
||||||
"file:///config.json" => Ok(ReadResourceResult {
|
"file:///config.json" => Ok(ReadResourceResult::new(vec![
|
||||||
contents: vec![ResourceContents::text(r#"{"key": "value"}"#, &request.uri)],
|
ResourceContents::text(r#"{"key": "value"}"#, &request.uri),
|
||||||
}),
|
])),
|
||||||
"memo://insights" => Ok(ReadResourceResult {
|
"memo://insights" => Ok(ReadResourceResult::new(vec![
|
||||||
contents: vec![ResourceContents::text("Analysis results...", &request.uri)],
|
ResourceContents::text("Analysis results...", &request.uri),
|
||||||
}),
|
])),
|
||||||
_ => Err(McpError::resource_not_found(
|
_ => Err(McpError::resource_not_found(
|
||||||
"resource_not_found",
|
"resource_not_found",
|
||||||
Some(json!({ "uri": request.uri })),
|
Some(json!({ "uri": request.uri })),
|
||||||
|
|
@ -302,10 +301,9 @@ use rmcp::model::{ReadResourceRequestParams};
|
||||||
let resources = client.list_all_resources().await?;
|
let resources = client.list_all_resources().await?;
|
||||||
|
|
||||||
// 通过 URI 读取特定资源
|
// 通过 URI 读取特定资源
|
||||||
let result = client.read_resource(ReadResourceRequestParams {
|
let result = client.read_resource(
|
||||||
meta: None,
|
ReadResourceRequestParams::new("file:///config.json"),
|
||||||
uri: "file:///config.json".into(),
|
).await?;
|
||||||
}).await?;
|
|
||||||
|
|
||||||
// 列出资源模板
|
// 列出资源模板
|
||||||
let templates = client.list_all_resource_templates().await?;
|
let templates = client.list_all_resource_templates().await?;
|
||||||
|
|
@ -320,9 +318,9 @@ let templates = client.list_all_resource_templates().await?;
|
||||||
context.peer.notify_resource_list_changed().await?;
|
context.peer.notify_resource_list_changed().await?;
|
||||||
|
|
||||||
// 通知特定资源已更新
|
// 通知特定资源已更新
|
||||||
context.peer.notify_resource_updated(ResourceUpdatedNotificationParam {
|
context.peer.notify_resource_updated(
|
||||||
uri: "file:///config.json".into(),
|
ResourceUpdatedNotificationParam::new("file:///config.json"),
|
||||||
}).await?;
|
).await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
客户端通过 `ClientHandler` 处理这些通知:
|
客户端通过 `ClientHandler` 处理这些通知:
|
||||||
|
|
@ -395,7 +393,7 @@ impl MyServer {
|
||||||
#[prompt(name = "greeting", description = "A simple greeting")]
|
#[prompt(name = "greeting", description = "A simple greeting")]
|
||||||
async fn greeting(&self) -> Vec<PromptMessage> {
|
async fn greeting(&self) -> Vec<PromptMessage> {
|
||||||
vec![PromptMessage::new_text(
|
vec![PromptMessage::new_text(
|
||||||
PromptMessageRole::User,
|
Role::User,
|
||||||
"Hello! How can you help me today?",
|
"Hello! How can you help me today?",
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
|
|
@ -409,25 +407,20 @@ impl MyServer {
|
||||||
let focus = args.focus_areas
|
let focus = args.focus_areas
|
||||||
.unwrap_or_else(|| vec!["correctness".into()]);
|
.unwrap_or_else(|| vec!["correctness".into()]);
|
||||||
|
|
||||||
Ok(GetPromptResult {
|
Ok(GetPromptResult::new(vec![
|
||||||
description: Some(format!("Code review for {}", args.language)),
|
|
||||||
messages: vec![
|
|
||||||
PromptMessage::new_text(
|
PromptMessage::new_text(
|
||||||
PromptMessageRole::User,
|
Role::User,
|
||||||
format!("Review my {} code. Focus on: {}", args.language, focus.join(", ")),
|
format!("Review my {} code. Focus on: {}", args.language, focus.join(", ")),
|
||||||
),
|
),
|
||||||
],
|
])
|
||||||
})
|
.with_description(format!("Code review for {}", args.language)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[prompt_handler]
|
#[prompt_handler]
|
||||||
impl ServerHandler for MyServer {
|
impl ServerHandler for MyServer {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerInfo {
|
||||||
ServerInfo {
|
ServerInfo::new(ServerCapabilities::builder().enable_prompts().build())
|
||||||
capabilities: ServerCapabilities::builder().enable_prompts().build(),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -481,25 +474,22 @@ context.peer.notify_prompt_list_changed().await?;
|
||||||
use rmcp::model::*;
|
use rmcp::model::*;
|
||||||
|
|
||||||
// 在 ServerHandler 方法内部(例如 call_tool):
|
// 在 ServerHandler 方法内部(例如 call_tool):
|
||||||
let response = context.peer.create_message(CreateMessageRequestParams {
|
let response = context.peer.create_message(
|
||||||
meta: None,
|
CreateMessageRequestParams::new(
|
||||||
task: None,
|
vec![SamplingMessage::user_text("Explain this error: connection refused")],
|
||||||
messages: vec![SamplingMessage::user_text("Explain this error: connection refused")],
|
150,
|
||||||
model_preferences: Some(ModelPreferences {
|
)
|
||||||
hints: Some(vec![ModelHint { name: Some("claude".into()) }]),
|
.with_model_preferences(
|
||||||
cost_priority: Some(0.3),
|
ModelPreferences::new()
|
||||||
speed_priority: Some(0.8),
|
.with_hints(vec![ModelHint::new("claude")])
|
||||||
intelligence_priority: Some(0.7),
|
.with_cost_priority(0.3)
|
||||||
}),
|
.with_speed_priority(0.8)
|
||||||
system_prompt: Some("You are a helpful assistant.".into()),
|
.with_intelligence_priority(0.7),
|
||||||
include_context: Some(ContextInclusion::None),
|
)
|
||||||
temperature: Some(0.7),
|
.with_system_prompt("You are a helpful assistant.")
|
||||||
max_tokens: 150,
|
.with_include_context(ContextInclusion::None)
|
||||||
stop_sequences: None,
|
.with_temperature(0.7),
|
||||||
metadata: None,
|
).await?;
|
||||||
tools: None,
|
|
||||||
tool_choice: None,
|
|
||||||
}).await?;
|
|
||||||
|
|
||||||
// 提取响应文本
|
// 提取响应文本
|
||||||
let text = response.message.content
|
let text = response.message.content
|
||||||
|
|
@ -527,11 +517,11 @@ impl ClientHandler for MyClient {
|
||||||
// 转发到你的 LLM,或返回模拟响应:
|
// 转发到你的 LLM,或返回模拟响应:
|
||||||
let response_text = call_your_llm(¶ms.messages).await;
|
let response_text = call_your_llm(¶ms.messages).await;
|
||||||
|
|
||||||
Ok(CreateMessageResult {
|
Ok(CreateMessageResult::new(
|
||||||
message: SamplingMessage::assistant_text(response_text),
|
SamplingMessage::assistant_text(response_text),
|
||||||
model: "my-model".into(),
|
"my-model".into(),
|
||||||
stop_reason: Some(CreateMessageResult::STOP_REASON_END_TURN.into()),
|
)
|
||||||
})
|
.with_stop_reason(CreateMessageResult::STOP_REASON_END_TURN))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -587,14 +577,9 @@ impl ClientHandler for MyClient {
|
||||||
&self,
|
&self,
|
||||||
_context: RequestContext<RoleClient>,
|
_context: RequestContext<RoleClient>,
|
||||||
) -> Result<ListRootsResult, ErrorData> {
|
) -> Result<ListRootsResult, ErrorData> {
|
||||||
Ok(ListRootsResult {
|
Ok(ListRootsResult::new(vec![
|
||||||
roots: vec![
|
Root::new("file:///home/user/project").with_name("My Project"),
|
||||||
Root {
|
]))
|
||||||
uri: "file:///home/user/project".into(),
|
|
||||||
name: Some("My Project".into()),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -623,12 +608,11 @@ use rmcp::{ServerHandler, model::*, service::RequestContext};
|
||||||
|
|
||||||
impl ServerHandler for MyServer {
|
impl ServerHandler for MyServer {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerInfo {
|
||||||
ServerInfo {
|
ServerInfo::new(
|
||||||
capabilities: ServerCapabilities::builder()
|
ServerCapabilities::builder()
|
||||||
.enable_logging()
|
.enable_logging()
|
||||||
.build(),
|
.build(),
|
||||||
..Default::default()
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 客户端设置最低日志级别
|
// 客户端设置最低日志级别
|
||||||
|
|
@ -643,14 +627,16 @@ impl ServerHandler for MyServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在任何可以访问 peer 的处理器中发送日志消息:
|
// 在任何可以访问 peer 的处理器中发送日志消息:
|
||||||
context.peer.notify_logging_message(LoggingMessageNotificationParam {
|
context.peer.notify_logging_message(
|
||||||
level: LoggingLevel::Info,
|
LoggingMessageNotificationParam::new(
|
||||||
logger: Some("my-server".into()),
|
LoggingLevel::Info,
|
||||||
data: serde_json::json!({
|
serde_json::json!({
|
||||||
"message": "Processing completed",
|
"message": "Processing completed",
|
||||||
"items_processed": 42
|
"items_processed": 42
|
||||||
}),
|
}),
|
||||||
}).await?;
|
)
|
||||||
|
.with_logger("my-server"),
|
||||||
|
).await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
可用日志级别(从低到高):`Debug`、`Info`、`Notice`、`Warning`、`Error`、`Critical`、`Alert`、`Emergency`。
|
可用日志级别(从低到高):`Debug`、`Info`、`Notice`、`Warning`、`Error`、`Critical`、`Alert`、`Emergency`。
|
||||||
|
|
@ -675,10 +661,7 @@ impl ClientHandler for MyClient {
|
||||||
客户端也可以设置服务端的日志级别:
|
客户端也可以设置服务端的日志级别:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
client.set_level(SetLevelRequestParams {
|
client.set_level(SetLevelRequestParams::new(LoggingLevel::Warning)).await?;
|
||||||
level: LoggingLevel::Warning,
|
|
||||||
meta: None,
|
|
||||||
}).await?;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -698,13 +681,12 @@ use rmcp::{ErrorData as McpError, ServerHandler, model::*, service::RequestConte
|
||||||
|
|
||||||
impl ServerHandler for MyServer {
|
impl ServerHandler for MyServer {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerInfo {
|
||||||
ServerInfo {
|
ServerInfo::new(
|
||||||
capabilities: ServerCapabilities::builder()
|
ServerCapabilities::builder()
|
||||||
.enable_completions()
|
.enable_completions()
|
||||||
.enable_prompts()
|
.enable_prompts()
|
||||||
.build(),
|
.build(),
|
||||||
..Default::default()
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn complete(
|
async fn complete(
|
||||||
|
|
@ -742,13 +724,9 @@ impl ServerHandler for MyServer {
|
||||||
.filter(|v| v.to_lowercase().contains(&request.argument.value.to_lowercase()))
|
.filter(|v| v.to_lowercase().contains(&request.argument.value.to_lowercase()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(CompleteResult {
|
let completion = CompletionInfo::with_pagination(filtered, None, false)
|
||||||
completion: CompletionInfo {
|
.map_err(|e| McpError::internal_error(e, None))?;
|
||||||
values: filtered,
|
Ok(CompleteResult::new(completion))
|
||||||
total: None,
|
|
||||||
has_more: Some(false),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -758,17 +736,10 @@ impl ServerHandler for MyServer {
|
||||||
```rust
|
```rust
|
||||||
use rmcp::model::*;
|
use rmcp::model::*;
|
||||||
|
|
||||||
let result = client.complete(CompleteRequestParams {
|
let result = client.complete(CompleteRequestParams::new(
|
||||||
meta: None,
|
Reference::for_prompt("sql_query"),
|
||||||
r#ref: Reference::Prompt(PromptReference {
|
ArgumentInfo::new("operation", "SEL"),
|
||||||
name: "sql_query".into(),
|
)).await?;
|
||||||
}),
|
|
||||||
argument: ArgumentInfo {
|
|
||||||
name: "operation".into(),
|
|
||||||
value: "SEL".into(),
|
|
||||||
},
|
|
||||||
context: None,
|
|
||||||
}).await?;
|
|
||||||
|
|
||||||
// result.completion.values 包含建议,例如 ["SELECT"]
|
// result.completion.values 包含建议,例如 ["SELECT"]
|
||||||
```
|
```
|
||||||
|
|
@ -794,12 +765,14 @@ use rmcp::model::*;
|
||||||
for i in 0..total_items {
|
for i in 0..total_items {
|
||||||
process_item(i).await;
|
process_item(i).await;
|
||||||
|
|
||||||
context.peer.notify_progress(ProgressNotificationParam {
|
context.peer.notify_progress(
|
||||||
progress_token: ProgressToken(NumberOrString::Number(i as i64)),
|
ProgressNotificationParam::new(
|
||||||
progress: i as f64,
|
ProgressToken(NumberOrString::Number(i as i64)),
|
||||||
total: Some(total_items as f64),
|
i as f64,
|
||||||
message: Some(format!("Processing item {}/{}", i + 1, total_items)),
|
)
|
||||||
}).await?;
|
.with_total(total_items as f64)
|
||||||
|
.with_message(format!("Processing item {}/{}", i + 1, total_items)),
|
||||||
|
).await?;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -809,10 +782,10 @@ for i in 0..total_items {
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// 发送取消通知
|
// 发送取消通知
|
||||||
context.peer.notify_cancelled(CancelledNotificationParam {
|
context.peer.notify_cancelled(CancelledNotificationParam::new(
|
||||||
request_id: the_request_id,
|
Some(the_request_id),
|
||||||
reason: Some("User requested cancellation".into()),
|
Some("User requested cancellation".into()),
|
||||||
}).await?;
|
)).await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
在 `ServerHandler` 或 `ClientHandler` 中处理取消:
|
在 `ServerHandler` 或 `ClientHandler` 中处理取消:
|
||||||
|
|
@ -883,13 +856,12 @@ struct MyServer {
|
||||||
|
|
||||||
impl ServerHandler for MyServer {
|
impl ServerHandler for MyServer {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerInfo {
|
||||||
ServerInfo {
|
ServerInfo::new(
|
||||||
capabilities: ServerCapabilities::builder()
|
ServerCapabilities::builder()
|
||||||
.enable_resources()
|
.enable_resources()
|
||||||
.enable_resources_subscribe()
|
.enable_resources_subscribe()
|
||||||
.build(),
|
.build(),
|
||||||
..Default::default()
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn subscribe(
|
async fn subscribe(
|
||||||
|
|
@ -916,9 +888,9 @@ impl ServerHandler for MyServer {
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// 检查资源是否有订阅者,然后通知
|
// 检查资源是否有订阅者,然后通知
|
||||||
context.peer.notify_resource_updated(ResourceUpdatedNotificationParam {
|
context.peer.notify_resource_updated(
|
||||||
uri: "file:///config.json".into(),
|
ResourceUpdatedNotificationParam::new("file:///config.json"),
|
||||||
}).await?;
|
).await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
### 客户端
|
### 客户端
|
||||||
|
|
@ -927,16 +899,10 @@ context.peer.notify_resource_updated(ResourceUpdatedNotificationParam {
|
||||||
use rmcp::model::*;
|
use rmcp::model::*;
|
||||||
|
|
||||||
// 订阅资源更新
|
// 订阅资源更新
|
||||||
client.subscribe(SubscribeRequestParams {
|
client.subscribe(SubscribeRequestParams::new("file:///config.json")).await?;
|
||||||
meta: None,
|
|
||||||
uri: "file:///config.json".into(),
|
|
||||||
}).await?;
|
|
||||||
|
|
||||||
// 不再需要时取消订阅
|
// 不再需要时取消订阅
|
||||||
client.unsubscribe(UnsubscribeRequestParams {
|
client.unsubscribe(UnsubscribeRequestParams::new("file:///config.json")).await?;
|
||||||
meta: None,
|
|
||||||
uri: "file:///config.json".into(),
|
|
||||||
}).await?;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
在 `ClientHandler` 中处理更新通知:
|
在 `ClientHandler` 中处理更新通知:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue