use crate::application::mcp::initialize::{read_offer, server_capabilities_json};
use crate::domain::ir::{JsonObject, JsonObjectError};
use crate::domain::{
Agreement, FeatureProfile, Leg, Negotiation, ProtocolVersion, SupportedVersions, TransportKind,
handshake_defined_in, negotiate_server, resolve,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DownstreamConfig {
supported: SupportedVersions,
transport: TransportKind,
identity: Identity,
}
impl DownstreamConfig {
pub fn new(
name: impl Into<String>,
version: impl Into<String>,
transport: TransportKind,
) -> Result<Self, DownstreamError> {
let supported =
SupportedVersions::all().map_err(|_| DownstreamError::UnnegotiableVersion)?;
Ok(Self::with(
supported,
Identity::new(name, version),
transport,
))
}
#[must_use]
pub const fn with(
supported: SupportedVersions,
identity: Identity,
transport: TransportKind,
) -> Self {
Self {
supported,
transport,
identity,
}
}
#[must_use]
pub const fn transport(&self) -> TransportKind {
self.transport
}
#[must_use]
pub const fn supported(&self) -> &SupportedVersions {
&self.supported
}
#[must_use]
pub const fn identity(&self) -> &Identity {
&self.identity
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Identity {
name: String,
version: String,
}
impl Identity {
#[must_use]
pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
Self {
name: name.into(),
version: version.into(),
}
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn version(&self) -> &str {
&self.version
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Downstream {
leg: Leg,
agreement: Agreement,
body: JsonObject,
}
impl Downstream {
#[must_use]
pub const fn leg(&self) -> &Leg {
&self.leg
}
#[must_use]
pub const fn agreement(&self) -> &Agreement {
&self.agreement
}
#[must_use]
pub const fn body(&self) -> &JsonObject {
&self.body
}
#[must_use]
pub const fn served(&self) -> FeatureProfile {
self.agreement.served()
}
}
#[derive(Debug, thiserror::Error)]
pub enum DownstreamError {
#[error("the initialize request was not readable as an offer")]
Malformed,
#[error("{0} has no initialize handshake to answer")]
NoHandshake(ProtocolVersion),
#[error("this build supports no revisions that define a handshake")]
UnnegotiableVersion,
#[error("the initialize result could not be assembled: {0}")]
Assemble(#[from] JsonObjectError),
}
pub fn negotiate(
params: &JsonObject,
upstream: &Leg,
config: &DownstreamConfig,
) -> Result<Downstream, DownstreamError> {
let offer = read_offer(params).map_err(|_| DownstreamError::Malformed)?;
let negotiated = negotiate_server(offer.version, &config.supported).version();
let version = if handshake_defined_in(negotiated) {
negotiated
} else {
config.supported.latest_handshake()
};
if !handshake_defined_in(version) {
return Err(DownstreamError::NoHandshake(version));
}
let profile = resolve(Negotiation {
version,
client_caps: offer.capabilities,
server_caps: upstream.profile.advertised_by_server(),
transport: config.transport,
});
let leg = Leg::new(version, profile);
let agreement = Agreement::between(leg, *upstream);
let body = serde_json::json!({
"protocolVersion": version.as_wire(),
"capabilities": server_capabilities_json(agreement.served()),
"serverInfo": {
"name": config.identity.name(),
"version": config.identity.version(),
},
});
Ok(Downstream {
leg,
agreement,
body: JsonObject::from_value(body)?,
})
}
#[cfg(test)]
mod tests {
use super::{DownstreamConfig, DownstreamError, Identity, negotiate};
use crate::domain::ir::JsonObject;
use crate::domain::{
Capability, CapabilitySet, Feature, Leg, Negotiation, ProtocolVersion, TransportKind,
resolve,
};
#[derive(Debug)]
enum Failure {
Fixture(crate::domain::ir::JsonObjectError),
Message(String),
}
impl core::fmt::Display for Failure {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Fixture(error) => write!(f, "bad fixture: {error}"),
Self::Message(text) => f.write_str(text),
}
}
}
impl std::error::Error for Failure {}
impl From<crate::domain::ir::JsonObjectError> for Failure {
fn from(error: crate::domain::ir::JsonObjectError) -> Self {
Self::Fixture(error)
}
}
impl From<String> for Failure {
fn from(text: String) -> Self {
Self::Message(text)
}
}
impl From<DownstreamError> for Failure {
fn from(error: DownstreamError) -> Self {
Self::Message(format!("the downstream leg was refused: {error}"))
}
}
fn params(text: &str) -> Result<JsonObject, Failure> {
JsonObject::new(text).map_err(Failure::from)
}
fn upstream_agreeing(version: ProtocolVersion, server_caps: CapabilitySet) -> Leg {
Leg::new(
version,
resolve(Negotiation {
version,
client_caps: CapabilitySet::all_for(version),
server_caps,
transport: TransportKind::StreamableHttp,
}),
)
}
fn config() -> Result<DownstreamConfig, Failure> {
DownstreamConfig::new("bridge", "0.1.0", TransportKind::Stdio)
.map_err(|e| Failure::Message(format!("a stdio bridge could not be configured: {e}")))
}
#[test]
fn the_advertisement_is_the_intersection_not_the_revision() -> Result<(), Failure> {
let version = ProtocolVersion::V2025_06_18;
let upstream = upstream_agreeing(version, CapabilitySet::empty().with(Capability::Tools));
let body = params(
r#"{"protocolVersion":"2025-06-18","capabilities":{"roots":{}},"clientInfo":{"name":"p"}}"#,
)?;
let downstream = negotiate(&body, &upstream, &config()?)?;
assert!(
downstream.agreement().serves(Feature::Tools),
"the one feature the upstream offers vanished"
);
assert!(
!downstream.agreement().serves(Feature::Resources),
"the bridge advertised a feature the upstream never agreed to"
);
let advertised = downstream.body().as_str();
assert!(
!advertised.contains("\"resources\""),
"the reply contradicts the agreement: {advertised}"
);
Ok(())
}
#[test]
fn the_clients_offer_narrows_it_too() -> Result<(), Failure> {
let version = ProtocolVersion::V2025_06_18;
let upstream = upstream_agreeing(version, CapabilitySet::all_for(version));
let body = params(
r#"{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"p"}}"#,
)?;
let downstream = negotiate(&body, &upstream, &config()?)?;
assert!(
!downstream.agreement().serves(Feature::Roots),
"a client that never offered roots was promised them"
);
Ok(())
}
#[test]
fn the_reply_carries_the_bridges_own_name() -> Result<(), Failure> {
let version = ProtocolVersion::V2025_06_18;
let upstream = upstream_agreeing(version, CapabilitySet::all_for(version));
let body = params(
r#"{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"p"}}"#,
)?;
let reply = negotiate(&body, &upstream, &config()?)?
.body()
.as_str()
.to_owned();
assert!(reply.contains(r#""name":"bridge""#), "{reply}");
assert!(reply.contains(r#""version":"0.1.0""#), "{reply}");
Ok(())
}
#[test]
fn the_reply_names_the_negotiated_revision() -> Result<(), Failure> {
let version = ProtocolVersion::V2025_06_18;
let upstream = upstream_agreeing(version, CapabilitySet::all_for(version));
let older = params(
r#"{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"p"}}"#,
)?;
assert_eq!(
negotiate(&older, &upstream, &config()?)?.leg().version,
ProtocolVersion::V2025_03_26
);
let future = params(
r#"{"protocolVersion":"2099-01-01","capabilities":{},"clientInfo":{"name":"p"}}"#,
)?;
let walked_back = negotiate(&future, &upstream, &config()?)?;
assert_ne!(
walked_back.leg().version,
ProtocolVersion::latest_known(),
"the newest revision has no handshake, so it cannot be the answer"
);
Ok(())
}
#[test]
fn a_malformed_offer_is_refused() -> Result<(), Failure> {
let version = ProtocolVersion::V2025_06_18;
let upstream = upstream_agreeing(version, CapabilitySet::all_for(version));
let body = params(r#"{"capabilities":{},"clientInfo":{"name":"p"}}"#)?;
assert!(matches!(
negotiate(&body, &upstream, &config()?),
Err(DownstreamError::Malformed)
));
Ok(())
}
#[test]
fn a_restricted_config_only_answers_with_what_it_supports() -> Result<(), Failure> {
let supported = crate::domain::SupportedVersions::new([ProtocolVersion::V2025_03_26])
.map_err(|e| Failure::Message(format!("a one-revision set was refused: {e}")))?;
let held = DownstreamConfig::with(
supported,
Identity::new("bridge", "0.1.0"),
TransportKind::Stdio,
);
let upstream = upstream_agreeing(ProtocolVersion::V2025_03_26, CapabilitySet::empty());
let body = params(
r#"{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"p"}}"#,
)?;
assert_eq!(
negotiate(&body, &upstream, &held)?.leg().version,
ProtocolVersion::V2025_03_26
);
Ok(())
}
}