1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
use std::time::Duration;
use asyncai::mcp::{McpToolExecutor, StdioServerConfig};
use asyncai::resources::{EventExtractor, ResourceSource, WatchSignal};
use asyncai::ToolExecutor;
use serde_json::json;
const WAIT: Duration = Duration::from_secs(20);
async fn simple(args: &[&str]) -> McpToolExecutor {
McpToolExecutor::connect_stdio(vec![StdioServerConfig {
command: env!("CARGO_BIN_EXE_asyncai-mcp-simple").to_string(),
args: args.iter().map(|arg| arg.to_string()).collect(),
}])
.await
.unwrap()
}
#[tokio::test]
async fn watches_a_state_resource_with_subscriptions_listen() {
let executor = simple(&[]).await;
let resources = executor.list_resources().await.unwrap();
assert_eq!(resources[0].uri, "memo://notes");
assert!(!resources[0].template);
assert!(
resources[0].example.is_some(),
"memo description has an Example: line"
);
assert!(resources[0].subscribable);
let mut watch = executor.watch("memo://notes").await.unwrap();
let baseline = executor.read_resource("memo://notes").await.unwrap();
let mut extractor = EventExtractor::baseline(&baseline, 100);
executor
.call_tool("memo_set", json!({"key": "todo", "value": "ship"}))
.await
.unwrap();
let signal = tokio::time::timeout(WAIT, watch.signals.recv())
.await
.expect("no update notification")
.unwrap();
assert_eq!(signal, WatchSignal::Updated);
let events = extractor.extract(&executor.read_resource("memo://notes").await.unwrap());
assert_eq!(
events,
vec![json!({"type": "ADDED", "key": "todo", "new": "ship", "old": null})]
);
}
#[tokio::test]
async fn unknown_resources_are_errors() {
let executor = simple(&[]).await;
let error = executor.read_resource("memo://nope").await.unwrap_err();
assert!(error.to_string().contains("memo://nope"), "{error}");
}