1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//! The Streamable HTTP client, and nothing else.
//!
//! The mirror image of [`crate::adapters::primary::transport::http`]. The
//! server half decides who is calling, how large a body is, and which session;
//! this half does the same three things from the other side, plus the one the
//! server does not need: surviving a dropped stream without repeating or
//! skipping an event.
//!
//! Neither half names an MCP method or a revision. That is the property that
//! makes the transport reusable by the version-translating bridge, which has
//! to carry revisions it does not understand and therefore cannot have a
//! method table baked into its socket layer. The one MCP-shaped name here is
//! the session header, and it is named in exactly one constant so a revision
//! that spells it differently is a one-line change rather than a search.
//!
//! Three files, one concern each: `client` holds the request/response
//! conversation and the resume state machine, `frames` turns bytes into
//! frames, and `error` is the one enum a caller above this layer matches on.
//! The frame *grammar* is not re-implemented here — it is shared with the
//! server's codec, because a client with its own grammar is a client that
//! disagrees with the server about where an event ends.
//!
//! # Examples
//!
//! A client that has been handed a session id, sending a body and reading a
//! document back:
//!
//! ```
//! use protocol_droid::adapters::secondary::transport::http::{
//! HttpClient, HttpClientConfig,
//! };
//!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # tokio::runtime::Builder::new_current_thread().enable_all().build()?.block_on(async {
//! # let endpoint = "https://example.invalid/mcp";
//! let client = HttpClient::new(HttpClientConfig::new(endpoint)?)?;
//!
//! // No session yet, so this call cannot require one. A peer that answers
//! // with a status the transport does not recognise is a `Refused`, and
//! // nothing here has to look at the status to tell that from a dead socket.
//! assert!(matches!(client.session_id(), None));
//! # Ok::<_, Box<dyn std::error::Error>>(())
//! # })
//! # }
//! ```
pub use ;
pub use HttpClientError;
pub use ;
pub use StatelessHttpClient;