3 stable releases

Uses new Rust 2024

2.1.0 May 2, 2026
2.0.0 Apr 30, 2026
1.14.0 Apr 22, 2026

#4 in #length-prefixed


Used in nexus-graph-sdk

Apache-2.0

105KB
2K SLoC

nexus-protocol

Wire-protocol clients for talking to a running Nexus server. Pure-client crate — no engine, no storage, no Cypher executor. If you want to embed the engine in-process, depend on nexus-core instead.

What ships

Module Transport Use it for
rpc length-prefixed MessagePack over TCP Native binary protocol shared with the Rust SDK and nexus-bench. Lowest overhead.
rest HTTP / JSON via reqwest Default integration. Same shape as curl http://127.0.0.1:15474/cypher.
mcp Model Context Protocol via rmcp LLM tool-calling integration (Claude, etc.).
umicp Universal Model Interoperability Protocol Cross-provider model-tool interop.
resp3 RESP3 codec (parser + writer) Building blocks for Redis-style framed protocols on top of TCP.

The crate re-exports the three high-level clients at the root:

use nexus_protocol::{RestClient, McpClient, UmicpClient};

rpc is namespaced (nexus_protocol::rpc::*) because it ships both a codec (codec.rs) and a typed message layer (types.rs) that callers compose at different levels.

Build & test

cargo +nightly build -p nexus-protocol
cargo +nightly test  -p nexus-protocol

cargo +nightly clippy -p nexus-protocol --all-targets --all-features -- -D warnings
cargo +nightly fmt --all

There is no slow-tests / live-bench feature here — every test in this crate is offline and runs in milliseconds. Live transport testing happens in nexus-bench (which depends on this crate's rpc module).

REST quick-start

use nexus_protocol::RestClient;

let client = RestClient::new("http://127.0.0.1:15474")?;
let result = client
    .execute_cypher("MATCH (n:Person) RETURN n.name LIMIT 10", None)
    .await?;

println!("{:?}", result.columns);   // ["n.name"]
println!("{:?}", result.rows);      // [["Alice"], ["Bob"], ...]

The response shape is always the Neo4j-compatible array form (rows: [[v1, v2]]). No object-of-columns fallback. SDK helpers (RowsAsMap() in Go / C#) are downstream conveniences, not server-side variants.

RPC quick-start

use nexus_protocol::rpc::{NexusRpcClient, NexusRpcCredentials};

let mut client = NexusRpcClient::connect("127.0.0.1:15475", None).await?;
client.ping().await?;
let result = client
    .execute("MATCH (n) RETURN count(n)", Default::default())
    .await?;

Authentication is opt-in. Pass Some(NexusRpcCredentials::ApiKey("...")) or Some(NexusRpcCredentials::UserPassword { user, password }) to connect; without credentials the handshake skips AUTH and relies on the server permitting unauthenticated access.

Hard timeouts (also enforced on the server side):

Step Timeout
TCP connect 5 s
HELLO / AUTH / PING 2 s each

MCP quick-start

use nexus_protocol::McpClient;

let mut client = McpClient::connect("http://127.0.0.1:15474/mcp").await?;
let tools = client.list_tools().await?;
let result = client
    .call_tool("execute_cypher",
               serde_json::json!({ "query": "MATCH (n) RETURN n LIMIT 1" }))
    .await?;

Hard constraints

  • Client-only. No nexus-core dependency, no engine instantiation. If you find yourself reaching for storage or executor types, you are in the wrong crate.
  • Errors via thiserror. Each transport exposes its own typed error (RestClientError, McpClientError, UmicpClientError, rpc::RpcError). Callers decide whether to bubble them up with ? or wrap with anyhow at the application layer.
  • No unwrap() / expect() outside of obvious compile-time invariants. This crate is a library; callers must be able to surface failures.
  • Wire format is server-defined. Do not invent new row shapes here — match the server contract verbatim. See docs/specs/api-protocols.md.

Dependencies

~14–23MB
~328K SLoC