Skip to content

v0.7.0

Choose a tag to compare

@RomanEmreis RomanEmreis released this 08 Oct 07:47
· 9 commits to main since this release
f5bf0a2

neva 0.7.0 hands MCP tools to a model through svir, gives Client and Context a namespaced API, and cancels the requests a client gives up on, on the wire and on the server. It is a minor bump for three narrow source breaks; see Migration. MSRV is unchanged at 1.90.

The svir bridge

New svir feature, neva::svir: MCP tools as a svir::Toolbox, for handing them to a model. A connected server's tools, or a server's own, called in-process through its middleware, so authorization, rate limits and audit apply to the model as to any other caller (#144).

use neva::svir::RemoteTools;
use svir::Toolbox;

// A connected server's tools, under a prefix of their own...
let tools = RemoteTools::new(client).with_prefix("board_").load().await?;
// ...or this server's own, with no MCP in between:
// let tools = app.into_toolbox().load().await?;

let model = svir::Client::openai("http://127.0.0.1:1234").build()?;
let request = svir::Request::new("qwen3").user("Plan my week").tools(&tools);
let done = model.complete(&request).await?;
let results = tools.call_all(&done.calls).await;
  • RemoteTools wraps a connected Client, or an Arc of one; App::into_toolbox consumes the server, App::with_toolbox keeps it serving as well, and ctx.tools().toolbox() hands a model the server's tools from inside a handler, holding that request's claims.
  • filter, rename and with_prefix choose what is offered and under which name; a call runs the tool the server knows by the original name. A name a model API cannot carry, or one two tools share, fails load instead of being renamed behind your back.
  • ctx.tools().toolbox() leaves out the tool it is called from, which would otherwise start its loop over inside its own call (#147); with_caller() keeps it for a tool that means to recurse. In-process calls nest at most four deep, counted along the chain whichever toolbox makes them; with_max_depth(n) changes the bound.
  • prompt_messages and resource_parts turn a prompt and a resource into svir messages; sampling_request and sampling_result answer a server's sampling with a model, tool calls included (#150).

The feature is in neither server-full nor client-full while svir is 0.x. The model call itself is svir's (its client feature). Example: examples/svir.

A namespaced API on Client and Context

One namespace per MCP method prefix (#143):

let client = Arc::new(client); // request methods take `&self` now

for tool in client.tools().list_all().await? {
    println!("{}", tool.name);
}
let sum = client.tools().call("add", [("a", 1), ("b", 2)]).await?;
let page = client.resources().list(None).await?;
  • client.tools(), resources(), prompts() and tasks(); on a handler's Context, ctx.tools(), resources() and prompts().
  • list_all() walks every page, and fails rather than return a partial list past 64 pages. tools().as_task() starts a task-augmented call.
  • Client request methods take &self, so a connected client can be shared across tasks; setup (connect, map_*, on_*, roots) keeps &mut self. Context methods take &self too.

The flat methods are deprecated, not removed; each #[deprecated] note names its replacement.

Abandoned requests are cancelled

MCP 2026-07-28 makes closing the response stream the cancellation over Streamable HTTP, has the client send notifications/cancelled over stdio, and has the sender cancel on a timeout. neva used to stop waiting and leave the request running on the server (#152).

  • Client. A request that times out or whose call is dropped is cancelled, the requests of an abandoned call_batch included; initialize never is. Under 2026-07-28 over Streamable HTTP that is closing the request's stream; over stdio and to a legacy peer, notifications/cancelled with the reason.
  • Server, 2026-07-28 HTTP. A POST closed before its answer cancels the requests it carried; a subscriptions/listen among them ends through its stream instead. The handler used to run on to an answer no one read. Since 0.4.0.
  • Server, both profiles. A notifications/cancelled on an HTTP session, batched or not, now finds its request; since 0.1.0 it was looked up without the session and cancelled nothing. A cancel that overtakes its request on the way to the handler is no longer lost, and a request cancelled by then never starts its handler.

Migration

ctx.tools() returns the tools namespace. ctx.tools().await no longer compiles, as the namespace is not a future; write ctx.tools().list().await.

TaskApi methods take &self, and wait_to_completion takes &A. Only implementations of TaskApi outside neva need the new receivers; callers are unaffected.

map_sampling is bound by ClientHandler<_, Result<CreateMessageResult, Error>, _>, so a sampling handler can fail: under legacy-spec the error answers the server, or fails the sampling task; under 2026-07-28 the asking call fails with it. A handler returning CreateMessageResult still fits; only code naming the old bound changes.

Deprecated, with their replacements: client.list_tools(cursor) -> client.tools().list(cursor), call_tool(name, args) -> tools().call(name, args), read_resource(uri) -> resources().read(uri), get_prompt(name, args) -> prompts().get(name, args); on Context, find_tool(name) -> ctx.tools().find(name), add_tool(tool) -> ctx.tools().add(tool), resource(uri) -> ctx.resources().read(uri), and so on.

Behaviour changes

  • A request the client stops waiting for is cancelled on the wire, as above. A server that relied on finishing work its caller abandoned now sees it cancelled.
  • Under 2026-07-28 the HTTP server stops a request whose POST the client closed.
  • Over Streamable HTTP under 2026-07-28, a cancelled subscription is ended by closing its stream alone, without a notifications/cancelled POST.
  • call_batch sends its requests under ids the client generates, and puts the caller's ids back on the responses: a caller-chosen id can no longer collide with one in flight.
  • mut ctx in a handler now warns (unused_mut), as Context methods take &self.

Fixed

  • Client::subscribe and the on_*_changed helpers no longer panic on a current_thread runtime (#139). Since 0.0.8.
  • A tasks/update or tasks/cancel the server refuses is an error, not Ok(()), and wait_to_completion stops at it. Since 0.5.4.
  • tools().call_raw keeps the _meta its params carry, a traceparent say, instead of replacing it (#145). Since 0.2.5. A request's own trace context wins over the trace context provider's (#137). Since 0.4.0.
  • Under legacy-spec, the client answers a failed tasks/result with the error's own code, not always InvalidParams.
  • x-mcp-header: a tools/list traversal that starts over no longer clears the registrations of later pages or lifts the block on a malformed tool.

The full list is in CHANGELOG.md. Diff: 0.6.2...0.7.0.