emperor_mcp/lib.rs
1//! Opinionated framework for building MCP servers: stateless Streamable HTTP,
2//! content-aware output framing, and per-request credential forwarding.
3//!
4//! Hatched from the [kowalski](https://github.com/yarenty/kowalski) workspace;
5//! servers built there (`kowalski-mcp-rookery`, `kowalski-mcp-datafusion`) are the
6//! reference consumers.
7//!
8//! Two server styles share the same conventions (stateless HTTP, output framing, no auth shortcuts):
9//!
10//! 1. **[`transport::McpHandler`]** — hand-rolled JSON-RPC dispatch; use [`transport::run_stdio`]
11//! (local/dev) or [`transport::serve_http`].
12//! 2. **[`serve::serve`]** — rmcp `ServerHandler` + `#[tool_router]` bootstrap at `/mcp` + `/health`,
13//! with credential-forwarding middleware for multi-tenant deployments.
14//!
15//! Every server binary also ships a `doctor` subcommand built from [`doctor`] checks.
16//!
17//! Also in the box: [`credentials`] (an upstream `401`/`403` becomes the MCP response's status),
18//! [`limits`] (caps on what a tool returns), [`schema`] (lean tool definitions: strips JSON
19//! Schema boilerplate from `tools/list`, opt in with `Self::tool_router().compact_schemas()`),
20//! and two optional features: `cli` (shared
21//! `--bind`/`--stdio`/`--allowed-host`/`--allowed-origin` flags, `cli::ServeArgs`) and
22//! `testkit` (tool-list conformance checks for a server's own tests, `testkit::assert_tools`).
23//!
24//! See `MCP_REQUIREMENTS.md` and `MANIFEST_SPEC.md` in the repository root.
25
26#![warn(missing_docs)]
27#![cfg_attr(docsrs, feature(doc_cfg))]
28
29#[cfg(feature = "cli")]
30#[cfg_attr(docsrs, doc(cfg(feature = "cli")))]
31pub mod cli;
32pub mod credentials;
33pub mod doctor;
34pub mod framing;
35pub mod headers;
36pub mod limits;
37pub mod schema;
38pub mod serve;
39#[cfg(feature = "testkit")]
40#[cfg_attr(docsrs, doc(cfg(feature = "testkit")))]
41pub mod testkit;
42pub mod transport;
43
44pub use credentials::{Rejection, credentials_rejected};
45pub use framing::{FrameKind, frame, structured_framed};
46pub use headers::{ForwardConfig, ForwardedHeaders, forward_headers_middleware};
47pub use limits::{Limits, truncate_chars};
48pub use serve::{ServeOptions, serve};
49pub use transport::{ACCEPT_STREAMABLE, McpHandler, http_router, run_stdio, serve_http, wants_sse};
50
51/// Initialise tracing/logging to stderr (stdout stays clean for the stdio transport) with an
52/// `RUST_LOG`-configurable filter, defaulting to `info`.
53pub fn init_tracing() {
54 use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
55 let _ = tracing_subscriber::registry()
56 .with(
57 tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()),
58 )
59 .with(tracing_subscriber::fmt::layer().with_writer(std::io::stderr))
60 .try_init();
61}