Skip to main content

Tasks

Neva supports long-running tasks — a way to call tools asynchronously and manage their lifecycle. Tasks allow clients to execute tools that may take a long time or require additional interactions (such as sampling or elicitation), with optional TTL-based cancellation.

Enabling Tasks on the Server

Use with_tasks() to enable task support:

use neva::prelude::*;

fn main() {
App::new()
.with_options(|opt| opt
.with_default_http()
.with_tasks(|t| t.with_all()))
.run_blocking();
}

with_all() enables all task-related capabilities. You can also enable them individually as needed.

Declaring a Task-Capable Tool

Mark a tool as a task by setting task_support = "required" in the #[tool] attribute macro:

#[tool(task_support = "required")]
async fn endless_tool() {
loop {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
}

A tool marked with task_support = "required" must be called as a task (via client.task().call_tool() on the client side). Calling it as a regular tool will be rejected.

Combining Tasks with Sampling and Elicitation

Task-capable tools can also trigger sampling or elicitation mid-execution:

#[tool(task_support = "required")]
async fn tool_with_sampling(mut ctx: Context) -> String {
let params = CreateMessageRequestParams::new()
.with_message(SamplingMessage::from("Write a haiku."))
.with_ttl(Some(5000));

let res = ctx.sample(params).await;
format!("{:?}", res.unwrap().content)
}

#[tool(task_support = "required")]
async fn tool_with_elicitation(mut ctx: Context, task: Meta<RelatedTaskMetadata>) -> String {
let params = ElicitRequestParams::form("Are you sure to proceed?")
.with_related_task(task);

let res = ctx.elicit(params.into()).await;
format!("{:?}", res.unwrap().action)
}

Meta<RelatedTaskMetadata> carries task context automatically injected by the framework. It is passed to with_related_task() so the client can correlate the elicitation request with the running task.

Learn By Example

Here you may find the full example.