ed-mcp: test a hook can cancel an elicitation
Covers the Cancel action path in the elicitation hook, asserting the result text is "cancelled" and the hook is invoked once. Co-Authored-By: siGit Code v1.6.6-acp <noreply@sigit.si>
Seto Elkahfi committed
Oct 12, 2026 at 09:57 UTC
824cc729e94c7e5647b3545c3081f608dd955907
1 file changed
+33
crates/ed-mcp/tests/http.rs
+33
@@ -343,6 +343,39 @@ async fn forwards_server_elicitation_to_the_hook() {
343
set.shutdown().await;
344
}
345
346
+#[tokio::test]
347
+async fn a_hook_can_cancel_an_elicitation() {
348
+ let url = serve().await;
349
+ let spec = ServerSpec::http("mail", url).with_bearer(TOKEN);
350
+
351
+ // A hook that cancels round-trips distinctly from one that declines: `create_elicitation`
352
+ // forwards the hook's `ElicitResult` verbatim, so the server sees Cancel, not Decline.
353
+ let seen = Arc::new(std::sync::Mutex::new(Vec::new()));
354
+ let hook = Arc::new(Recorder {
355
+ seen: seen.clone(),
356
+ answer: ElicitResult::new(ElicitationAction::Cancel),
357
+ });
358
+ let config = ToolsetConfig::new(client()).with_elicitation(hook);
359
+ let set = McpToolset::connect_specs(config, &[spec], &[]).await;
360
+
361
+ let out = set
362
+ .call(
363
+ "mcp__mail__collect_feedback",
364
+ json!({}),
365
+ &CancellationToken::new(),
366
+ &|_| {},
367
+ )
368
+ .await
369
+ .unwrap();
370
+
371
+ // The hook ran and the server mapped Cancel to "cancelled", distinct from "declined".
372
+ assert!(!out.failed, "{}", out.text);
373
+ assert_eq!(out.text, "cancelled");
374
+ assert_eq!(seen.lock().unwrap().len(), 1, "the hook was called once");
375
+
376
+ set.shutdown().await;
377
+}
378
+
379
#[tokio::test]
380
async fn without_a_hook_server_elicitation_is_declined() {
381
let url = serve().await;