Cooperative request cancellation for long-running tools.
MCP clients can abort an in-flight request by sending
notifications/cancelled with the original request's id. Because
ConduitMCP is stateless and each HTTP request runs in its own Bandit
process, the notification (which arrives on a different request)
cannot directly preempt the in-flight handler. Instead, the handler
records the cancellation in a shared ETS table and tool code polls
this module to decide whether to abort.
Tool integration
Inside a long-running tool, periodically check the conn's request id:
def my_long_tool(conn, params) do
ConduitMcp.Cancellation.cancelled?(conn)
|> case do
true -> {:error, %{"code" => -32800, "message" => "Request cancelled"}}
false -> continue_work(...)
end
endOr use the Process.exit/2 pattern for hard abort. Tools that complete
in tens of milliseconds typically do not need cancellation at all — the
client's notification will arrive after the response has already been
sent.
Lifecycle
- The handler stashes the request id into
conn.assigns[:mcp_request_id]before dispatching to the server callback. - On
notifications/cancelledarrival, the cancellation flag is set withcancel/2. - When a request completes (success or error), the handler calls
clear/1to release the flag so the entry does not linger. - For pathological cases (cancel arrives after completion has already
cleared the entry, or the server crashes before clear runs),
cleanup/1can be called periodically to prune stale entries by age. There is no janitor wired by default — cancellation entries are bounded by the rate of in-flight cancellations.
Emits [:conduit_mcp, :request, :cancelled] telemetry on cancellation
with metadata %{request_id: id, reason: reason}.
Summary
Functions
Marks a request id as cancelled with an optional reason.
Returns true when the given request has been cancelled.
Removes cancellation entries older than ttl_ms milliseconds. Defensive —
the handler clears entries inline when a request completes; this is for
the rare case where a cancel arrived after a crash or after the response
was already sent.
Clears a request id from the cancellation set. Idempotent.
Returns the cancellation reason recorded for the request, or nil.
Functions
Marks a request id as cancelled with an optional reason.
Returns true when the given request has been cancelled.
Accepts a request id directly, or a Plug.Conn whose assigns contains
:mcp_request_id (set by ConduitMcp.Handler before dispatch).
Removes cancellation entries older than ttl_ms milliseconds. Defensive —
the handler clears entries inline when a request completes; this is for
the rare case where a cancel arrived after a crash or after the response
was already sent.
Returns the number of entries removed.
Clears a request id from the cancellation set. Idempotent.
Returns the cancellation reason recorded for the request, or nil.