neva/lib.rs
1//! # Neva
2//! Easy configurable MCP server and client SDK for Rust
3//!
4//! ## Dependencies
5//! ```toml
6//! [dependencies]
7//! neva = { version = "0.3.3", features = ["full"] }
8//! tokio = { version = "1", features = ["full"] }
9//! ```
10//!
11//! ## Example Server
12//! ```no_run
13//! # #[cfg(feature = "server")] {
14//! use neva::App;
15//!
16//! #[tokio::main]
17//! async fn main() {
18//! let mut app = App::new()
19//! .with_options(|opt| opt
20//! .with_stdio());
21//!
22//! app.map_tool("hello", |name: String| async move {
23//! format!("Hello, {name}!")
24//! });
25//!
26//! app.run().await;
27//! }
28//! # }
29//! ```
30//! # Example Client
31//! ```no_run
32//! # #[cfg(feature = "client")] {
33//! use std::time::Duration;
34//! use neva::{Client, error::Error};
35//!
36//! #[tokio::main]
37//! async fn main() -> Result<(), Error> {
38//! let mut client = Client::new()
39//! .with_options(|opt| opt
40//! .with_stdio("npx", ["-y", "@modelcontextprotocol/server-everything"]));
41//!
42//! client.connect().await?;
43//!
44//! // Call a tool
45//! let args = [("message", "Hello MCP!")];
46//! let result = client.call_tool("echo", Some(args)).await?;
47//! println!("{:?}", result.content);
48//!
49//! client.disconnect().await
50//! }
51//! # }
52//! ```
53
54#[cfg(all(feature = "server", feature = "proto-2026-07-28-rc"))]
55pub use app::extension::Extension;
56#[cfg(all(feature = "server", feature = "proto-2026-07-28-rc", feature = "tasks"))]
57pub use app::extension::TasksExtension;
58#[cfg(all(feature = "server", feature = "proto-2026-07-28-rc"))]
59pub use app::mrtr_store::{InMemoryStateStore, RequestStateStore};
60#[cfg(feature = "server")]
61pub use app::{App, context::Context};
62#[cfg(feature = "client")]
63pub use client::Client;
64
65#[cfg(feature = "server")]
66pub mod app;
67#[cfg(feature = "client")]
68pub mod client;
69pub mod commands;
70#[cfg(feature = "di")]
71pub mod di;
72pub mod error;
73#[cfg(feature = "macros")]
74pub mod macros;
75#[cfg(feature = "server")]
76pub mod middleware;
77pub mod shared;
78#[cfg(any(feature = "server", feature = "client"))]
79pub mod transport;
80pub mod types;
81
82#[cfg(feature = "client-macros")]
83pub use neva_macros::elicitation;
84#[cfg(feature = "macros")]
85pub use neva_macros::json_schema;
86#[cfg(all(feature = "client-macros", not(feature = "proto-2026-07-28-rc")))]
87pub use neva_macros::sampling;
88#[cfg(feature = "server-macros")]
89pub use neva_macros::{completion, handler, prompt, resource, resources, tool};
90
91pub(crate) const SDK_NAME: &str = "neva";
92#[cfg(any(feature = "server", feature = "client"))]
93pub(crate) const PROTOCOL_VERSIONS: &[&str] = &[
94 "2024-11-05",
95 "2025-03-26",
96 "2025-06-18",
97 "2025-11-25",
98 #[cfg(feature = "proto-2026-07-28-rc")]
99 "2026-07-28",
100];
101
102// Mutual-exclusion guard for `proto-*` generation flags.
103//
104// One pairwise `all(...)` lives in the `any(...)` body for every pair of
105// `proto-*` flags. Today only one such flag exists, so the body is empty
106// and the guard is dormant (`cfg(any())` with no operands evaluates to
107// `false`). When a second `proto-*` flag is introduced, append
108// `all(feature = "proto-A", feature = "proto-B")` to the list.
109#[cfg(any(
110 // all(feature = "proto-2026-07-28-rc", feature = "proto-2027-XX-XX-rc"),
111))]
112compile_error!("Only one `proto-*` feature flag may be enabled per build");
113
114#[cfg(feature = "http-server")]
115pub mod auth {
116 //! Authentication utilities — neva's engine-neutral [`Claims`] trait
117 //! + (under the Volga adapter) the bearer-auth configuration types.
118
119 /// `Claims` is neva's engine-neutral trait for typed per-tool
120 /// authorization. Implement this for your custom claims type to enable
121 /// `with_roles` / `with_permissions` gating regardless of which HTTP
122 /// engine delivered the request.
123 ///
124 /// The Volga adapter's `DefaultClaims` already implements both this
125 /// trait and `volga::auth::AuthClaims`, so the same per-tool validator
126 /// runs across every engine.
127 ///
128 /// # Engine contract
129 ///
130 /// An [`HttpEngine`](crate::transport::http::core::engine::HttpEngine)
131 /// adapter that wants protected tools/prompts/resources to authorize
132 /// must wrap its decoded claims in `Arc<dyn Claims>` and insert it
133 /// into the inbound request's extensions before calling the
134 /// `dispatch_post` helper:
135 ///
136 /// ```rust,ignore
137 /// use std::sync::Arc;
138 /// use neva::auth::Claims;
139 ///
140 /// // in the engine's POST route, after decoding the bearer token:
141 /// let claims: Arc<dyn Claims> = Arc::new(my_decoded_claims);
142 /// neutral_req.extensions_mut().insert(claims);
143 /// ```
144 pub use crate::transport::http::core::types::Claims;
145
146 /// `DefaultClaims` is a pre-built [`Claims`] impl matching the JWT
147 /// standard claim names. Engine-agnostic — under the Volga adapter
148 /// it additionally implements `volga::auth::AuthClaims` so it can
149 /// be fed straight into Volga's bearer-auth pipeline.
150 pub use crate::transport::http::core::types::DefaultClaims;
151
152 /// `AuthConfig` is the Volga-flavored builder used with
153 /// `HttpServer::with_auth(...)`. Available only under the Volga adapter.
154 #[cfg(feature = "http-server-volga")]
155 pub use crate::transport::http::server::volga::auth_config::AuthConfig;
156
157 /// Volga's claims trait, re-exported for users who need to plug a
158 /// custom claims type into Volga's `Authorizer<C>`. For neva's own
159 /// per-tool checks, implement [`Claims`] instead — that one is
160 /// engine-neutral.
161 #[cfg(feature = "http-server-volga")]
162 pub use volga::auth::AuthClaims;
163
164 // Volga's `Claims` is a derive macro in the macro namespace; re-export
165 // it as `ClaimsDerive` so it doesn't collide with the `Claims` trait
166 // alias above (which lives in the type namespace).
167 #[cfg(feature = "http-server-volga")]
168 pub use volga::auth::{Algorithm, Authorizer, Claims as ClaimsDerive};
169}
170
171pub mod json {
172 //! JSON utilities
173
174 #[doc(hidden)]
175 pub use schemars;
176 pub use schemars::JsonSchema;
177}
178
179/// Internal re-exports used by `neva_macros`-generated code. Not public API.
180#[cfg(feature = "proto-2026-07-28-rc")]
181#[doc(hidden)]
182pub mod __macro_support {
183 pub use crate::types::schema_2020::{
184 SchemaProbe, ViaFallback, ViaJsonSchema, object_schema, primitive_subschema,
185 };
186}
187
188pub mod prelude {
189 //! Prelude with commonly used items
190
191 pub use crate::error::*;
192 pub use crate::json::*;
193 pub use crate::types::*;
194
195 #[cfg(feature = "http-server-volga")]
196 pub use crate::auth::AuthConfig;
197 #[cfg(feature = "http-server")]
198 pub use crate::auth::{Claims, DefaultClaims};
199
200 #[cfg(all(feature = "http-server", feature = "server-tls"))]
201 pub use crate::transport::http::{DevCertMode, TlsConfig};
202 #[cfg(feature = "http-server")]
203 pub use crate::transport::{
204 HttpContext, HttpEngine, HttpRequest, HttpResponse, HttpServer, SseResponse, handlers,
205 };
206
207 #[cfg(all(feature = "server", feature = "proto-2026-07-28-rc"))]
208 pub use crate::app::extension::Extension;
209 #[cfg(all(feature = "server", feature = "proto-2026-07-28-rc", feature = "tasks"))]
210 pub use crate::app::extension::TasksExtension;
211 #[cfg(feature = "server")]
212 pub use crate::app::{App, context::Context, options};
213 #[cfg(feature = "server")]
214 pub use crate::middleware::{MwContext, Next};
215
216 #[cfg(feature = "client")]
217 pub use crate::client::Client;
218
219 #[cfg(feature = "client-macros")]
220 pub use crate::elicitation;
221 #[cfg(feature = "macros")]
222 pub use crate::json_schema;
223 #[cfg(all(feature = "client-macros", not(feature = "proto-2026-07-28-rc")))]
224 pub use crate::sampling;
225 #[cfg(feature = "server-macros")]
226 pub use crate::{completion, handler, prompt, resource, resources, tool};
227
228 #[cfg(feature = "di")]
229 pub use crate::di::Dc;
230
231 #[cfg(feature = "tasks")]
232 pub use crate::shared::TaskApi;
233}
234
235#[cfg(test)]
236#[cfg(any(feature = "server", feature = "client"))]
237mod proto_versions_tests {
238 use super::PROTOCOL_VERSIONS;
239
240 #[test]
241 fn rc_version_listed_only_under_flag() {
242 let has_rc = PROTOCOL_VERSIONS.contains(&"2026-07-28");
243 let flag_on = cfg!(feature = "proto-2026-07-28-rc");
244 assert_eq!(has_rc, flag_on, "RC version listing must match the flag");
245 }
246
247 #[test]
248 fn stable_versions_always_listed() {
249 // Stable versions are PROTOCOL_VERSIONS minus the RC entry (when enabled).
250 // Future stable additions land in PROTOCOL_VERSIONS and are automatically
251 // covered by this test — no need to update the test when new versions
252 // are advertised.
253 let stable: Vec<_> = PROTOCOL_VERSIONS
254 .iter()
255 .filter(|v| **v != "2026-07-28")
256 .copied()
257 .collect();
258 assert!(
259 !stable.is_empty(),
260 "PROTOCOL_VERSIONS must always advertise at least one stable version"
261 );
262 // The set must include 2024-11-05 (the inaugural MCP version) — this is
263 // a stronger invariant: even if we ever retire intermediate versions,
264 // the original SHOULD remain for backwards compatibility.
265 assert!(
266 stable.contains(&"2024-11-05"),
267 "PROTOCOL_VERSIONS must always advertise the inaugural MCP version 2024-11-05"
268 );
269 }
270}