#[cfg(feature = "test-utils")]
use klieo_tools_mcp::MCP_PROTOCOL_VERSION;
#[cfg(feature = "test-utils")]
use rmcp::handler::server::ServerHandler;
#[cfg(feature = "test-utils")]
use rmcp::model::{
CallToolRequestParams, CallToolResult, Content, Implementation, InitializeResult,
ListToolsResult, ProtocolVersion, ServerCapabilities, ServerInfo, Tool,
};
#[cfg(feature = "test-utils")]
use rmcp::service::{RequestContext, RoleServer};
#[cfg(feature = "test-utils")]
use rmcp::{ErrorData as McpError, ServiceExt};
#[cfg(feature = "test-utils")]
use std::borrow::Cow;
#[cfg(feature = "test-utils")]
use std::sync::Arc;
#[cfg(feature = "test-utils")]
const ECHO_TOOL_NAME: &str = "echo";
#[cfg(feature = "test-utils")]
const ECHO_REPLY: &str = "hello-mcp";
#[cfg(feature = "test-utils")]
#[derive(Clone)]
struct EchoServer;
#[cfg(feature = "test-utils")]
impl ServerHandler for EchoServer {
fn get_info(&self) -> ServerInfo {
InitializeResult::new(ServerCapabilities::builder().enable_tools().build())
.with_protocol_version(pinned_protocol_version())
.with_server_info(Implementation::new(
"mcp-echo-server",
env!("CARGO_PKG_VERSION"),
))
}
async fn list_tools(
&self,
_request: Option<rmcp::model::PaginatedRequestParams>,
_context: RequestContext<RoleServer>,
) -> Result<ListToolsResult, McpError> {
Ok(ListToolsResult::with_all_items(vec![echo_tool()]))
}
async fn call_tool(
&self,
request: CallToolRequestParams,
_context: RequestContext<RoleServer>,
) -> Result<CallToolResult, McpError> {
if request.name != ECHO_TOOL_NAME {
return Err(McpError::invalid_params(
format!("unknown tool: {}", request.name),
None,
));
}
Ok(CallToolResult::success(vec![Content::text(ECHO_REPLY)]))
}
}
#[cfg(feature = "test-utils")]
fn echo_tool() -> Tool {
let schema = serde_json::json!({
"type": "object",
"properties": { "message": { "type": "string" } },
"additionalProperties": false
});
let schema = schema.as_object().cloned().unwrap_or_default();
Tool::new(
Cow::Borrowed(ECHO_TOOL_NAME),
Cow::Borrowed("Returns a fixed greeting as a text content block"),
Arc::new(schema),
)
}
#[cfg(feature = "test-utils")]
fn pinned_protocol_version() -> ProtocolVersion {
serde_json::from_value(serde_json::Value::String(MCP_PROTOCOL_VERSION.to_string()))
.expect("MCP_PROTOCOL_VERSION must be a protocol version rmcp recognises")
}
#[cfg(feature = "test-utils")]
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let transport = rmcp::transport::io::stdio();
let service = EchoServer.serve(transport).await?;
service.waiting().await?;
Ok(())
}
#[cfg(not(feature = "test-utils"))]
fn main() {
eprintln!("mcp-echo-server is a test fixture; rebuild with --features test-utils to run it");
std::process::exit(1);
}