Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
resolver = "2"
members = [
"crates/hydra-core",
"crates/hydra-mcp-http",
"crates/hydra-mcp-stdio",
"crates/hydra-codegen",
"examples/notes",
Expand Down Expand Up @@ -31,12 +32,14 @@ missing_panics_doc = "allow"

[workspace.dependencies]
hydra-core = { path = "crates/hydra-core" }
hydra-mcp-http = { path = "crates/hydra-mcp-http" }
hydra-mcp-stdio = { path = "crates/hydra-mcp-stdio" }

serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = "0.9"
anyhow = "1"
base64 = "0.22"
thiserror = "2"
tokio = { version = "1", features = ["full"] }
clap = { version = "4", features = ["derive"] }
Expand Down
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ inference — everything is declared**.
- `hydra-mcp-stdio` — a minimal reusable MCP stdio server (JSON-RPC 2.0,
newline-delimited) you hand your generated tool schemas and one dispatch
closure.
- `hydra-mcp-http` — a reusable, stateless MCP Streamable HTTP server for the
`2026-07-28` profile. It validates explicit per-request metadata, mirrored
method/name headers, and an explicit Origin policy, then calls one
consumer-owned async dispatch closure.
- `examples/notes` — a complete four-surface project. Copy it as a template.
- `examples/ts-client` — a TypeScript-only Iris consumer fixture with
`tsc --noEmit` coverage.
Expand Down Expand Up @@ -81,6 +85,55 @@ cargo run -p hydra-codegen -- check # CI guard: fails if artifacts are stale
4. `include!` the generated files, implement one dispatch function, and wire
your binaries. See `examples/notes/src/lib.rs`.

### Stateless MCP Streamable HTTP

`hydra-mcp-http` is an embedded-host runtime, not a standalone service. The
host owns the listener and nests the returned router at an explicit path. The
reference Notes consumer mounts it at `/mcp`:

```bash
cargo run -p notes-example --bin notes-mcp-http
```

The Notes binary binds only to `127.0.0.1:8942` and prints the local endpoint.
A current-protocol client sends one JSON-RPC request per POST with:

- `Content-Type: application/json`;
- an `Accept` value explicitly containing both `application/json` and
`text/event-stream`;
- `MCP-Protocol-Version: 2026-07-28`;
- `Mcp-Method` matching the JSON-RPC method; and
- `params._meta` containing
`io.modelcontextprotocol/protocolVersion` and
`io.modelcontextprotocol/clientCapabilities`.

`tools/call` additionally mirrors `params.name` through `Mcp-Name`. Names that
cannot be represented as a safe plain header use the MCP Base64 sentinel form
`=?base64?{value}?=`. The runtime validates the generated tool manifest at
construction, preserves its exact order and objects for `tools/list`, and
rejects unsupported `x-mcp-header` annotations rather than advertising a
contract it cannot validate.

The default `OriginPolicy::RejectPresented` permits non-browser clients that
omit `Origin` and rejects every presented Origin. Hosts that deliberately need
browser-origin validation must provide a finite `OriginPolicy::AllowExact`
list and own any separately reviewed CORS or Private Network Access layer.
This runtime emits no CORS/PNA headers, handles no `OPTIONS` preflights, does
not create sessions or `Mcp-Session-Id` headers, has no GET stream, and does
not downgrade to the legacy `initialize` protocol. It also never binds a
listener, chooses a mount path, or implements consumer operation semantics.

The reproducible reference checks are:

```bash
cargo test --locked -p hydra-mcp-http
cargo test --locked -p notes-example --test mcp_http
```

The second test starts a real ephemeral loopback listener and uses an
independent HTTP client fixture for `server/discover` → `tools/list` →
`tools/call`; it is not a `Router::oneshot`-only test.

### JSON batch operations

Hydra v0.2.0 can project a declared `json` body parameter to HTTP, MCP, and
Expand Down
20 changes: 20 additions & 0 deletions crates/hydra-mcp-http/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "hydra-mcp-http"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
description = "Stateless MCP Streamable HTTP runtime for Hydra-generated tools"

[lints]
workspace = true

[dependencies]
axum = { workspace = true }
base64 = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }

[dev-dependencies]
tokio = { workspace = true }
tower = { version = "0.5", features = ["util"] }
Loading
Loading