Publish an MCP server
ADK-Rust consumes MCP servers through McpToolset, but it does not wrap every
server-side SDK API. Server authors use the official rmcp SDK re-exported by
adk_tool::mcp::rmcp so client and server protocol types stay aligned.
For a standalone server crate, depending directly on the same rmcp release is
also appropriate:
[dependencies]
rmcp = { version = "2.2", features = ["transport-io", "schemars"] }
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
Minimal stdio server
use rmcp::{
ServerHandler,
handler::server::{router::tool::ToolRouter, wrapper::Parameters},
model::{ServerCapabilities, ServerInfo},
schemars, tool, tool_handler, tool_router,
};
use serde::Deserialize;
#[derive(Debug, Deserialize, schemars::JsonSchema)]
struct LookupInput {
order_id: String,
}
#[derive(Debug, Clone)]
struct OrderServer {
tool_router: ToolRouter<Self>,
}
#[tool_router]
impl OrderServer {
#[tool(description = "Read one order by its public order ID")]
async fn read_order(
&self,
Parameters(input): Parameters<LookupInput>,
) -> String {
format!("Order {} is ready for investigation", input.order_id)
}
}
#[tool_handler]
impl ServerHandler for OrderServer {
fn get_info(&self) -> ServerInfo {
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
.with_instructions("Read-only order investigation tools")
}
}
let service = rmcp::ServiceExt::serve(
OrderServer { tool_router: OrderServer::tool_router() },
rmcp::transport::io::stdio(),
).await?;
service.waiting().await?;
The deterministic examples/mcp_manager fixture uses this server shape and is
compiled and executed by the MCP verification gates.
Publish honest capabilities
Only advertise a capability when the handler implements it. A client uses the initialization response to decide whether it may call resources, prompts, completion, elicitation, subscriptions, or tasks.
For task-capable tools, declare the tool's task support and advertise
tasks.requests.tools.call. The client may reject a required task tool when the
server did not negotiate task support.
Tools are security boundaries
Descriptions and JSON Schema help the model form a call; they are not input validation or authorization. A server must:
- validate every input independently of the model;
- resolve identity and tenant scope at the server boundary;
- authorize the specific action and resource;
- separate read operations from consequential writes;
- avoid returning secrets or unbounded data;
- make retries and idempotency explicit for side effects; and
- record enough evidence to explain the outcome.
Choose a transport
Use stdio when the client owns the local child process. Use Streamable HTTP when the server is an independently deployed service. Remote deployments also need authentication, request limits, session policy, observability, and an application-level health probe.
See the official rmcp documentation for
server routers, resources, prompts, custom handlers, transports, authorization,
and extension APIs.