@ondeinference / ed / commits / 824cc72

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;