* feat(transport): support streamable http server * refactor(transport): add common module for shared transport utilities * perf(tracing): add more log for streamable http session * fix(test): fix port conflict in test with js * fix(transport): fix id generating and session management 1. cancel when deleting 2. use `wrapping` and `saturating` correctly * chore(naming): rename sse server to streamable http server
99 lines
2.9 KiB
Rust
99 lines
2.9 KiB
Rust
use rmcp::{
|
|
ServiceExt,
|
|
transport::{SseServer, TokioChildProcess, streamable_http_server::axum::StreamableHttpServer},
|
|
};
|
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
|
mod common;
|
|
use common::calculator::Calculator;
|
|
|
|
const SSE_BIND_ADDRESS: &str = "127.0.0.1:8000";
|
|
const STREAMABLE_HTTP_BIND_ADDRESS: &str = "127.0.0.1:8001";
|
|
|
|
#[tokio::test]
|
|
async fn test_with_js_client() -> anyhow::Result<()> {
|
|
let _ = tracing_subscriber::registry()
|
|
.with(
|
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
|
.unwrap_or_else(|_| "debug".to_string().into()),
|
|
)
|
|
.with(tracing_subscriber::fmt::layer())
|
|
.try_init();
|
|
tokio::process::Command::new("npm")
|
|
.arg("install")
|
|
.current_dir("tests/test_with_js")
|
|
.spawn()?
|
|
.wait()
|
|
.await?;
|
|
|
|
let ct = SseServer::serve(SSE_BIND_ADDRESS.parse()?)
|
|
.await?
|
|
.with_service(Calculator::default);
|
|
|
|
let exit_status = tokio::process::Command::new("node")
|
|
.arg("tests/test_with_js/client.js")
|
|
.spawn()?
|
|
.wait()
|
|
.await?;
|
|
assert!(exit_status.success());
|
|
ct.cancel();
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_with_js_server() -> anyhow::Result<()> {
|
|
let _ = tracing_subscriber::registry()
|
|
.with(
|
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
|
.unwrap_or_else(|_| "debug".to_string().into()),
|
|
)
|
|
.with(tracing_subscriber::fmt::layer())
|
|
.try_init();
|
|
tokio::process::Command::new("npm")
|
|
.arg("install")
|
|
.current_dir("tests/test_with_js")
|
|
.spawn()?
|
|
.wait()
|
|
.await?;
|
|
let transport = TokioChildProcess::new(
|
|
tokio::process::Command::new("node").arg("tests/test_with_js/server.js"),
|
|
)?;
|
|
|
|
let client = ().serve(transport).await?;
|
|
let resources = client.list_all_resources().await?;
|
|
tracing::info!("{:#?}", resources);
|
|
let tools = client.list_all_tools().await?;
|
|
tracing::info!("{:#?}", tools);
|
|
|
|
client.cancel().await?;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_with_js_streamable_http_client() -> anyhow::Result<()> {
|
|
let _ = tracing_subscriber::registry()
|
|
.with(
|
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
|
.unwrap_or_else(|_| "debug".to_string().into()),
|
|
)
|
|
.with(tracing_subscriber::fmt::layer())
|
|
.try_init();
|
|
tokio::process::Command::new("npm")
|
|
.arg("install")
|
|
.current_dir("tests/test_with_js")
|
|
.spawn()?
|
|
.wait()
|
|
.await?;
|
|
|
|
let ct = StreamableHttpServer::serve(STREAMABLE_HTTP_BIND_ADDRESS.parse()?)
|
|
.await?
|
|
.with_service(Calculator::default);
|
|
|
|
let exit_status = tokio::process::Command::new("node")
|
|
.arg("tests/test_with_js/streamable_client.js")
|
|
.spawn()?
|
|
.wait()
|
|
.await?;
|
|
assert!(exit_status.success());
|
|
ct.cancel();
|
|
Ok(())
|
|
}
|