Skip to main content

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}