Answer.declined
Java · package io.github.muthuishere:toolnexus · SPEC §10 · Answer.java
public static Answer declined(String id, String reason)Wraps a human’s refusal into the Answer a suspended run resumes with, carrying a reason string
that defaults to "declined". declined builds new Answer(id, false, null, reason) — ok:false,
no payload — and normalises a null/empty reason to the literal string "declined" so the field
is never blank. reason is advisory only: the loop rule branches solely on ok, never on the
text of reason. It exists for a host, or the MCP elicitation bridge, that wants to distinguish an
explicit refusal ("declined") from a dismissal or a timeout ("cancelled" / "expired").
When to use it
Section titled “When to use it”Any time a suspended Request must resume as a refusal
rather than a result — a human declined an approval, dismissed a prompt without answering, or the
wait itself expired before anyone responded. Build the Answer with
Answer.declined(request.id(), "declined") (or "cancelled" / "expired", or any reason string
your host wants to record) and hand it back from waitFor, or pass it to whatever the host’s
durable-resume path calls to settle a parked Request by id.
Why this and not the alternative
Section titled “Why this and not the alternative”Examples
Section titled “Examples”1. The smallest useful call — the reason defaults to “declined”
Section titled “1. The smallest useful call — the reason defaults to “declined””import io.github.muthuishere.toolnexus.Answer;
public class Example { public static void main(String[] args) { Answer nullReason = Answer.declined("req-1", null); Answer emptyReason = Answer.declined("req-1", "");
if (nullReason.ok()) throw new AssertionError("declined() must be ok:false"); if (!"declined".equals(nullReason.reason())) throw new AssertionError(nullReason.reason()); if (!"declined".equals(emptyReason.reason())) throw new AssertionError(emptyReason.reason());
System.out.println("ok: both default to reason=" + nullReason.reason()); }}2. The realistic case — a waitFor that resolves a pending Request with a refusal
Section titled “2. The realistic case — a waitFor that resolves a pending Request with a refusal”import io.github.muthuishere.toolnexus.*;
public class Example { /** Stands in for a real approval-desk lookup keyed by request.id(). */ static boolean humanApproved(Request request) { return false; // the approver said no }
public static void main(String[] args) { Request request = new Request("", "approval", "Approve the refund?");
// This is the shape a real client.Options.waitFor(...) callback returns. Answer resolved = humanApproved(request) ? Answer.output(request.id(), "approved") : Answer.declined(request.id(), "declined");
if (resolved.ok()) throw new AssertionError("expected a refusal"); if (!"declined".equals(resolved.reason())) throw new AssertionError(resolved.reason()); System.out.println("ok: resumed with reason=" + resolved.reason()); }}3. The full surface — distinguishing an explicit refusal from a timeout
Section titled “3. The full surface — distinguishing an explicit refusal from a timeout”import io.github.muthuishere.toolnexus.Answer;
public class Example { public static void main(String[] args) { Answer refused = Answer.declined("req-1", "declined"); Answer timedOut = Answer.declined("req-2", "expired"); Answer dismissed = Answer.declined("req-3", "cancelled");
// `ok` is the ONLY field the loop rule branches on — all three resume the run as // "not answered", identically. `reason` is carried through for a host (or the MCP // elicitation bridge) that wants to tell them apart afterwards. for (Answer a : java.util.List.of(refused, timedOut, dismissed)) { if (a.ok()) throw new AssertionError("all three must be ok:false: " + a); } if (!"declined".equals(refused.reason())) throw new AssertionError(refused.reason()); if (!"expired".equals(timedOut.reason())) throw new AssertionError(timedOut.reason()); if (!"cancelled".equals(dismissed.reason())) throw new AssertionError(dismissed.reason());
System.out.println("ok: three distinct reasons, one uniform ok:false"); }}See also
Section titled “See also”Request.pending— Return a Pending from a tool to park the run until someone answers.Answer.output— The success counterpart: wrap a human’s typed reply into an Answer.Request.authRequired— The auth-shaped suspension: hand back a URL, resume once the user has granted access.LlmClient.waitFor— The single hook where the host resolves a suspension — in-process prompt or durable queue, same contract.Request.pendingOf— Detect that a RunResult is parked rather than finished, and get the Request that parked it.