feat(server): in-server MCP endpoint over Streamable HTTP (rmcp), RFC-003

Adds POST /mcp: a stateless, JSON-only MCP (Model Context Protocol) endpoint
served by rmcp 1.7's StreamableHttpService, mounted inside per_graph_protected
so it inherits the bearer-auth and graph-handle middleware (single mode serves
/mcp; multi mode nests it to /graphs/{graph_id}/mcp).

rmcp's stateless mode (stateful_mode=false, json_response=true) provides
GET/DELETE 405, loopback Host validation, and MCP-Protocol-Version checking
(400 on unsupported, default 2025-03-26) without extra code. The handler
advertises the tools capability and answers the initialize handshake; the
tool catalog, resources, and stored-query projection land in the following
phases. Auth stays decoupled (RFC-003 5.8): the handler consumes the
already-resolved ResolvedActor and branches on nothing about how the token
was verified.

Tests (tests/server.rs): initialize handshake, GET 405, and bearer required
(401 without a token, 200 with). The docs/user/server.md MCP section is
deferred to the docs phase per the RFC-003 rollout, since the endpoint
exposes an empty tool list until the tools phase.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ragnor Comerford 2026-06-08 15:27:01 +02:00
parent 69fae71870
commit 9df9f394c0
No known key found for this signature in database
5 changed files with 321 additions and 5 deletions

122
Cargo.lock generated
View file

@ -25,7 +25,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
"cpufeatures 0.2.17",
]
[[package]]
@ -1085,7 +1085,7 @@ dependencies = [
"cc",
"cfg-if",
"constant_time_eq",
"cpufeatures",
"cpufeatures 0.2.17",
]
[[package]]
@ -1278,6 +1278,17 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chacha20"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
dependencies = [
"cfg-if",
"cpufeatures 0.3.0",
"rand_core 0.10.1",
]
[[package]]
name = "chrono"
version = "0.4.44"
@ -1487,6 +1498,15 @@ dependencies = [
"libc",
]
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "crc32c"
version = "0.6.8"
@ -2750,6 +2770,7 @@ dependencies = [
"cfg-if",
"libc",
"r-efi 6.0.0",
"rand_core 0.10.1",
"wasip2",
"wasip3",
]
@ -3423,7 +3444,7 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653"
dependencies = [
"cpufeatures",
"cpufeatures 0.2.17",
]
[[package]]
@ -4657,6 +4678,7 @@ dependencies = [
"omnigraph-engine",
"omnigraph-policy",
"regex",
"rmcp",
"serde",
"serde_json",
"serde_yaml",
@ -4793,6 +4815,12 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pastey"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4"
[[package]]
name = "path_abs"
version = "0.5.1"
@ -5309,6 +5337,17 @@ dependencies = [
"rand_core 0.9.5",
]
[[package]]
name = "rand"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
dependencies = [
"chacha20",
"getrandom 0.4.2",
"rand_core 0.10.1",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
@ -5347,6 +5386,12 @@ dependencies = [
"getrandom 0.3.4",
]
[[package]]
name = "rand_core"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]]
name = "rand_distr"
version = "0.5.1"
@ -5579,6 +5624,35 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "rmcp"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0810a9f717d9828f475fe1f629f4c305c8464b7f496c3a854b58d29e65f4058e"
dependencies = [
"async-trait",
"bytes",
"chrono",
"futures",
"http 1.4.0",
"http-body 1.0.1",
"http-body-util",
"pastey",
"pin-project-lite",
"rand 0.10.1",
"schemars 1.2.1",
"serde",
"serde_json",
"sse-stream",
"thiserror",
"tokio",
"tokio-stream",
"tokio-util",
"tower-service",
"tracing",
"uuid",
]
[[package]]
name = "roaring"
version = "0.11.3"
@ -5816,12 +5890,26 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc"
dependencies = [
"chrono",
"dyn-clone",
"ref-cast",
"schemars_derive",
"serde",
"serde_json",
]
[[package]]
name = "schemars_derive"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.117",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
@ -5926,6 +6014,17 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "serde_derive_internals"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "serde_json"
version = "1.0.149"
@ -6051,7 +6150,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures",
"cpufeatures 0.2.17",
"digest",
]
@ -6062,7 +6161,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"cpufeatures 0.2.17",
"digest",
]
@ -6241,6 +6340,19 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "sse-stream"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3962b63f038885f15bce2c6e02c0e7925c072f1ac86bb60fd44c5c6b762fb72"
dependencies = [
"bytes",
"futures-util",
"http-body 1.0.1",
"http-body-util",
"pin-project-lite",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.1"