mcp: handle list changed notifications
This commit is contained in:
+31
@@ -16,6 +16,7 @@ fn main() {
|
||||
"tools-call-forbidden" => tools_call_forbidden(),
|
||||
"fail-init" => fail_init(),
|
||||
"sampling" => sampling_request(),
|
||||
"list-changed-all" => list_changed_all(),
|
||||
"shutdown-hang" => shutdown_hang(),
|
||||
other => panic!("unknown mock mode: {other}"),
|
||||
}
|
||||
@@ -223,6 +224,36 @@ fn sampling_request() {
|
||||
assert_eq!(response["error"]["code"], -32601);
|
||||
}
|
||||
|
||||
fn list_changed_all() {
|
||||
let init = read_json();
|
||||
write_json(json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": init["id"],
|
||||
"result": initialize_result(),
|
||||
}));
|
||||
let initialized = read_json();
|
||||
assert_eq!(initialized["method"], "notifications/initialized");
|
||||
for method in [
|
||||
"notifications/tools/list_changed",
|
||||
"notifications/resources/list_changed",
|
||||
"notifications/prompts/list_changed",
|
||||
] {
|
||||
write_json(json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": method,
|
||||
"params": {
|
||||
"malicious_instruction": "INJECT_ME_FROM_LIST_CHANGED_PARAMS"
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
let shutdown = read_json();
|
||||
assert_eq!(shutdown["method"], "shutdown");
|
||||
write_json(json!({"jsonrpc":"2.0", "id": shutdown["id"], "result": {}}));
|
||||
let notification = read_json();
|
||||
assert_eq!(notification["method"], "exit");
|
||||
}
|
||||
|
||||
fn shutdown_hang() {
|
||||
let init = read_json();
|
||||
write_json(json!({
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use mcp::stdio::{
|
||||
CallToolRequest, McpErrorKind, McpPhase, McpStdioClient, McpStdioLimits, McpStdioServerSpec,
|
||||
McpToolListLimits,
|
||||
CallToolRequest, McpErrorKind, McpListChangedKind, McpPhase, McpStdioClient, McpStdioLimits,
|
||||
McpStdioServerSpec, McpToolListLimits,
|
||||
};
|
||||
|
||||
fn mock_server(mode: &str) -> McpStdioServerSpec {
|
||||
@@ -239,6 +239,36 @@ async fn shutdown_terminates_or_kills_uncooperative_server() {
|
||||
assert!(shutdown.terminated || shutdown.killed);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn list_changed_notifications_record_bounded_kind_only_state() {
|
||||
let mut client = McpStdioClient::connect(mock_server("list-changed-all"), tight_limits())
|
||||
.await
|
||||
.expect("initialize succeeds");
|
||||
tokio::time::sleep(Duration::from_millis(50)).await;
|
||||
|
||||
let snapshot = client.snapshot_list_changes().await;
|
||||
assert_eq!(snapshot.server_name, "mock");
|
||||
assert!(snapshot.contains(McpListChangedKind::Tools));
|
||||
assert!(snapshot.contains(McpListChangedKind::Resources));
|
||||
assert!(snapshot.contains(McpListChangedKind::Prompts));
|
||||
let methods: Vec<&'static str> = snapshot
|
||||
.kinds()
|
||||
.map(McpListChangedKind::notification_method)
|
||||
.collect();
|
||||
assert_eq!(
|
||||
methods,
|
||||
vec![
|
||||
"notifications/tools/list_changed",
|
||||
"notifications/resources/list_changed",
|
||||
"notifications/prompts/list_changed"
|
||||
]
|
||||
);
|
||||
|
||||
client.clear_list_changes().await;
|
||||
assert!(client.snapshot_list_changes().await.is_empty());
|
||||
client.shutdown().await.expect("shutdown succeeds");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sampling_requests_fail_closed_and_are_not_advertised() {
|
||||
let mut client = McpStdioClient::connect(mock_server("sampling"), tight_limits())
|
||||
|
||||
Reference in New Issue
Block a user