pmcp/shared/mod.rs
1//! Shared components used by both client and server.
2
3pub mod batch;
4pub mod context;
5/// The DEFAULT on-disk credential store — the gated I/O counterpart to
6/// [`credential_store`] below.
7///
8/// Gated on `not(wasm32)` AND `feature = "oauth"` because every item in it needs
9/// a filesystem, and `default_credential_path` needs the `oauth` feature's
10/// `dirs` dependency. It is a SEPARATE module rather than a gated half of
11/// `credential_store` so that the pure tier keeps its "no `#[cfg]` other than
12/// `cfg(test)`" property, which is what makes its wasm32 cleanliness reviewable
13/// at a glance. It knows nothing about the credential document's shape: the
14/// format, the schema migration and the migration report all stay next door.
15#[cfg(all(not(target_arch = "wasm32"), feature = "oauth"))]
16pub mod credential_file;
17/// Target-agnostic OAuth credential storage: the three-part key, the record,
18/// the document format, the schema 1 to 2 migration and the platform seam.
19///
20/// Ungated on purpose — a file under the user's home directory is unusable on
21/// AWS Lambda and per-container on Cloudflare Workers and Cloud Run, so
22/// credential storage lands behind a trait and everything a platform needs in
23/// order to implement that trait must compile where the `oauth` feature does
24/// not exist, on host AND wasm32. Its only imports are this crate's error type,
25/// `serde`, `async_trait`, `parking_lot` and the non-optional `url` crate. Do
26/// NOT "tidy" a target or feature gate onto it: a second copy of the document
27/// format and its migration is how a platform store and the CLI come to
28/// disagree about what a stored credential means. A gated FILE implementation
29/// is the deliberate counterpart and belongs in its own module. (Contrast the
30/// `#[cfg(not(target_arch = "wasm32"))]` peer/stdio entries elsewhere in this
31/// file; `oauth_validation` and `pkce` below carry the same rationale.)
32pub mod credential_store;
33// SMPL-02: the single largest severance win in `src/shared/`.
34//
35// `event_store.rs` is 421 lines of MCP 2025-11-25 SSE-resumability machinery —
36// the `Last-Event-ID` replay store, its resumption tokens and its retention
37// window. The 2026-07-28 transport states that resumable SSE streams via
38// `Last-Event-ID` are not supported, so on a `full-v2` build not one line of it
39// has a caller. Re-measured before this gate landed: ZERO consumers anywhere in
40// `src/`, `crates/`, `tests/`, `examples/`, `cargo-pmcp/` or `fuzz/` outside the
41// file itself and the re-export below. (The `EventStore`/`InMemoryEventStore`
42// that the integration tests DO use is a different, 3-method trait that lives in
43// `src/server/streamable_http_server.rs`.)
44//
45// GATED, NOT DELETED. Both items are PUBLIC API; removing them is a semver-major
46// change and belongs to SMPL-F1 / pmcp 3.0 — see `docs/v1-sunset-policy.md`. The
47// `#[cfg]` must stay on BOTH this declaration and the `pub use event_store::{…}`
48// re-export further down; gating only one of the two is a compile break.
49//
50// DO NOT "FINISH THE JOB" BY GATING THE SSE FILES (correction A-D03). Neither
51// `src/shared/sse_parser.rs` nor `src/shared/sse_optimized.rs` is v1-only: v2's
52// `subscriptions/listen` returns a live `text/event-stream`
53// (`src/server/streamable_http_server.rs`, whose subscribe handler REJECTS any
54// non-V2 era), so SSE framing and parsing are SHARED by both eras. Only
55// RESUMABILITY is v1-only. `src/shared/http_constants.rs` is likewise
56// deliberately ungated — per-constant gating is plan 117-13's job — and
57// `src/shared/session.rs` is unmeasured and deliberately left alone.
58#[cfg(feature = "v1-compat")]
59#[cfg_attr(docsrs, doc(cfg(feature = "v1-compat")))]
60/// v1-only SSE resumability: the `Last-Event-ID` replay store and its tokens.
61pub mod event_store;
62/// Hardened HTTP plumbing for this crate's OAuth/OIDC surfaces: the streaming
63/// bounded whole-body read every auth response is read through, and the
64/// discovery HTTP client whose redirect policy cannot be steered off the
65/// issuer's origin.
66///
67/// Gated on `feature = "http-client"` because every item in it takes or returns
68/// a `reqwest` type; the wasm32 build does not enable that feature and must not
69/// see this module. `pub(crate)` on purpose — the four auth files that consume
70/// it are all in-crate, and this hardening adds no public surface it does not
71/// need.
72#[cfg(feature = "http-client")]
73pub(crate) mod http_body_cap;
74pub mod http_utils;
75/// Neutralising untrusted text before it reaches a log sink or an error message.
76///
77/// Feature-agnostic: its two consumers sit on opposite sides of a FEATURE gate
78/// (`server::core`'s `skills/get` URI echo is ungated, the skills projection is
79/// `feature = "skills"`), which is exactly why the classification had drifted
80/// before it lived here. Both consumers are nevertheless server-side and thus
81/// `not(wasm32)`, so the module carries that target gate — the wasm server core
82/// is a separate minimal surface that renders no untrusted text into a sink.
83#[cfg(not(target_arch = "wasm32"))]
84pub(crate) mod log_sanitize;
85pub mod logging;
86pub mod middleware;
87pub mod middleware_presets;
88/// Target-agnostic OAuth authorization-RESPONSE validation (RFC 9207 `iss`,
89/// CSRF `state`).
90///
91/// Ungated on purpose — it must be callable from a Cloudflare Workers or
92/// Lambda redirect handler, where the `oauth` feature (and its `webbrowser` /
93/// `dirs` / `rand` dependencies) does not exist and does not build. Its only
94/// imports are this crate's error type and the non-optional `url` crate, so it
95/// compiles on host AND wasm32. Do NOT "tidy" a `cfg` onto it: a second copy of
96/// the RFC 9207 decision table is how a platform handler and the CLI come to
97/// disagree about what "valid" means. (Contrast the
98/// `#[cfg(not(target_arch = "wasm32"))]` peer/stdio entries elsewhere in this
99/// file, and note `pkce` below carries the same rationale for the same reason.)
100pub mod oauth_validation;
101/// Peer back-channel trait for server-to-client RPCs from inside request handlers.
102#[cfg(not(target_arch = "wasm32"))]
103pub mod peer;
104/// Target-agnostic one-slot pending-response buffer for one-shot transports.
105///
106/// Internal plumbing (`pub(crate)`) backing the `WasmHttpTransport`
107/// send→receive correlation; ungated so it host-tests under plain `cargo test`.
108pub(crate) mod pending_slot;
109/// Target-agnostic PKCE (RFC 7636) crypto helper (verifier/challenge/state).
110///
111/// Ungated on purpose — compiles on host AND wasm32 via `getrandom::fill`
112/// (contrast the `#[cfg(not(target_arch = "wasm32"))]` peer/stdio entries).
113pub mod pkce;
114pub mod protocol;
115pub mod protocol_helpers;
116#[cfg(not(target_arch = "wasm32"))]
117pub mod reconnect;
118pub mod session;
119pub mod simd_parsing;
120// Dead on wasm32 by CONFIGURATION, not by disuse: this module's consumers are the
121// native server/client tier (`src/server/core.rs`, `src/server/task_dispatch.rs`,
122// `src/client/mod.rs`), all of which are `#[cfg(not(target_arch = "wasm32"))]`. The
123// items are `pub(crate)` and very much alive natively, so they must NOT be deleted;
124// the wasm build simply has no callers for them. Scoped to wasm32 so genuine dead
125// code is still caught on every other target.
126#[cfg_attr(
127 any(target_arch = "wasm32", not(feature = "streamable-http")),
128 allow(dead_code)
129)]
130pub mod sse_parser;
131/// Wire-level request/response tracing (`pmcp::wire` target).
132pub mod wire_trace;
133
134#[cfg(feature = "sse")]
135pub mod sse_optimized;
136
137#[cfg(not(target_arch = "wasm32"))]
138pub mod connection_pool;
139#[cfg(not(target_arch = "wasm32"))]
140pub mod stdio;
141pub mod transport;
142pub mod uri_template;
143
144// Cross-platform runtime abstraction
145pub mod runtime;
146
147// Platform-specific WebSocket modules
148#[cfg(all(feature = "websocket", not(target_arch = "wasm32")))]
149pub mod websocket;
150
151#[cfg(all(feature = "websocket-wasm", target_arch = "wasm32"))]
152pub mod wasm_websocket;
153
154#[cfg(target_arch = "wasm32")]
155pub mod wasm_http;
156
157#[cfg(all(feature = "http", not(target_arch = "wasm32")))]
158pub mod http;
159pub mod http_constants;
160
161#[cfg(all(feature = "streamable-http", not(target_arch = "wasm32")))]
162/// Streamable HTTP transport implementation for MCP.
163pub mod streamable_http;
164
165// Re-export commonly used types
166pub use batch::{BatchRequest, BatchResponse};
167pub use context::{ClientInfo, ContextPropagator, RequestContext};
168// The other half of the SMPL-02 `event_store` gate. Must carry the SAME `#[cfg]`
169// as the `pub mod event_store;` declaration above — gating one without the other
170// is a compile break, not a warning.
171#[cfg(feature = "v1-compat")]
172#[cfg_attr(docsrs, doc(cfg(feature = "v1-compat")))]
173pub use event_store::{
174 EventStore, EventStoreConfig, InMemoryEventStore, MessageDirection, ResumptionManager,
175 ResumptionState, ResumptionToken, StoredEvent,
176};
177#[cfg(all(not(target_arch = "wasm32"), feature = "logging"))]
178pub use logging::init_logging;
179pub use logging::{CorrelatedLogger, LogConfig, LogEntry, LogFormat, LogLevel};
180pub use middleware::{
181 AdvancedMiddleware, AuthMiddleware, CircuitBreakerMiddleware, CompressionMiddleware,
182 CompressionType, EnhancedMiddlewareChain, LoggingMiddleware, MetricsMiddleware, Middleware,
183 MiddlewareChain, MiddlewareContext, MiddlewarePriority, PerformanceMetrics,
184 RateLimitMiddleware, RetryMiddleware,
185};
186pub use protocol::{ProgressCallback, Protocol, ProtocolOptions, RequestOptions};
187pub use protocol_helpers::{
188 create_notification, create_request, parse_notification, parse_request,
189};
190#[cfg(not(target_arch = "wasm32"))]
191pub use reconnect::{ReconnectConfig, ReconnectGuard, ReconnectManager};
192pub use session::{Session, SessionConfig, SessionManager};
193#[cfg(not(target_arch = "wasm32"))]
194pub use stdio::StdioTransport;
195pub use transport::{SharedSender, Transport, TransportMessage};
196pub use uri_template::UriTemplate;
197
198#[cfg(all(feature = "websocket", not(target_arch = "wasm32")))]
199pub use websocket::{WebSocketConfig, WebSocketTransport};
200
201#[cfg(all(feature = "websocket-wasm", target_arch = "wasm32"))]
202pub use wasm_websocket::{WasmWebSocketConfig, WasmWebSocketTransport};
203
204#[cfg(target_arch = "wasm32")]
205pub use wasm_http::{WasmHttpClient, WasmHttpConfig, WasmHttpTransport};
206
207#[cfg(all(feature = "http", not(target_arch = "wasm32")))]
208pub use http::{HttpConfig, HttpTransport};
209
210#[cfg(all(feature = "streamable-http", not(target_arch = "wasm32")))]
211pub use streamable_http::{StreamableHttpTransport, StreamableHttpTransportConfig};
212
213// Why: `OptimizedSseTransport` is deprecated on purpose (plan 113.1-03, D-01)
214// but is NOT removed — retiring a public item is a 3.0 action, and this
215// milestone's additivity claim is "zero removed public items". The `deprecated`
216// lint fires on a `pub use` re-export within the defining crate, and `make lint`
217// runs with `-D warnings`, so the crate must allow it to compile its own
218// retained transport. `OptimizedSseConfig` is deliberately NOT deprecated.
219#[allow(deprecated)]
220#[cfg(feature = "sse")]
221pub use sse_optimized::{OptimizedSseConfig, OptimizedSseTransport};
222
223#[cfg(not(target_arch = "wasm32"))]
224pub use connection_pool::{
225 ConnectionId, ConnectionPool, ConnectionPoolConfig, HealthStatus, LoadBalanceStrategy,
226 PoolStats, PooledTransport,
227};
228
229pub use simd_parsing::{
230 CpuFeatures, ParsingMetrics, SimdBase64, SimdHttpHeaderParser, SimdJsonParser, SimdSseParser,
231};