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}"); }