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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! A minimal MCP server that speaks over Streamable HTTP.
//!
//! The stdio counterpart is
//! [`stdio_server.rs`](protocol_droid::adapters::primary::stdio::serve_stdio);
//! this file shows the **HTTP** counterpart — the same `Server`
//! built with the `#[server]` / `#[tool]` macros, but mounted
//! on a `Router` by [`HttpBuilder`] and bound to a TCP listener
//! instead of being driven over a child's standard streams.
//!
//! # What this is
//!
//! The smallest thing an external MCP client can actually
//! reach over HTTP. It wires the four pieces a Streamable HTTP
//! server needs:
//!
//! 1. a `Server` carrying at least one real tool, built with
//! the `#[server]` / `#[tool]` macros,
//! 2. a [`Dispatcher`] that routes a method name to that server,
//! 3. a [`PeerSession`] carrying the negotiated revision and
//! the `FeatureProfile` every feature gate in the crate
//! consults, and
//! 4. an [`HttpBuilder`] that assembles the `Router`, binds
//! the listener, and runs the per-session loop.
//!
//! # What this is not
//!
//! The
//! [`conformance_reference_server.rs`](https://github.com/greysquirr3l/protocol-droid/blob/main/crates/protocol-droid/examples/conformance_reference_server.rs)
//! example is the conformance target — it exposes every
//! feature the suite reads and is wired to the `conformance
//! self` subcommand. This file is the consumer-facing
//! counterpart: one tool, one route, no conformance surface.
//! A real consumer who needs the full feature set copies
//! the conformance target; a consumer who needs a focused
//! HTTP service starts here.
//!
//! # How to run it
//!
//! ```text
//! cargo run -p protocol-droid --example http_server --features http
//! ```
//!
//! A one-shot probe, after the server is running:
//!
//! ```text
//! curl -X POST -H 'content-type: application/json' \
//! -H 'accept: application/json, text/event-stream' \
//! -d '{"jsonrpc":"2.0","id":1,"method":"initialize",
//! "params":{"protocolVersion":"2025-06-18",
//! "capabilities":{},
//! "clientInfo":{"name":"curl","version":"1.0"}}}' \
//! http://127.0.0.1:3000/mcp
//! ```
//!
//! Override the bind address with `MCP_ADDR=0.0.0.0:8080`.
//! A bad value is reported on stderr and the default
//! (`127.0.0.1:3000`) is used.
use SocketAddr;
use HttpBuilder;
use Serialize;
// `#[tool]` is called once per request rather than once per server, and the
// function is `async fn` because the call site `.await`s it. A body that
// did no real work would still hand back a future, and the compiler would
// warn about an unused-async body. `tokio::task::yield_now` is the
// one-byte future the lint accepts: it costs one task queue turn and
// changes nothing else, so a fixture whose tools are hot-paths in a
// benchmark does not regress here.
async
;
async
/// Reads the bind address from the `MCP_ADDR` env var,
/// or falls back to `127.0.0.1:3000`. A malformed
/// `MCP_ADDR` is reported on stderr and the default
/// is used.