ADK MCP SDK
The shared contract between MCP servers and the ADK-Rust Enterprise registry. Every server in the ecosystem implements these traits to enable automatic onboarding, health monitoring, tool governance, and risk classification.
Architecture
What This Crate Provides
Installation
[dependencies]
adk-mcp-sdk = "0.3"
Quick Start
1. Implement HealthCheck
use adk_mcp_sdk::{HealthCheck, HealthStatus};
#[derive(Clone)]
struct MyServer;
#[async_trait::async_trait]
impl HealthCheck for MyServer {
async fn check_health(&self) -> HealthStatus {
// Check your backend connectivity, auth, schema, etc.
HealthStatus {
healthy: true,
message: Some("All systems operational".into()),
latency_ms: Some(5),
}
}
}
2. Create mcp-server.toml
server_id = "mcp_my_server"
display_name = "My MCP Server"
version = "0.3.0"
domain = "platform"
sdk_version = "0.3.0"
risk_level = "medium"
writes_allowed = "gated"
transports = ["stdio"]
credentials = ["vault://my-api-key"]
governance_gates = []
environments = ["development", "staging", "production"]
[[tools]]
name = "list_items"
description = "List items with optional filters"
risk_class = "read_only"
requires_approval = false
credential_bindings = []
[[tools]]
name = "create_item"
description = "Create a new item (gated write)"
risk_class = "internal_write"
requires_approval = true
credential_bindings = ["vault://my-api-key"]
3. Load and Validate Manifest
use adk_mcp_sdk::ServerManifest;
use std::path::Path;
// Load and validate on startup
let manifest = ServerManifest::from_file(Path::new("mcp-server.toml"))?;
let errors = manifest.validate();
if !errors.is_empty() {
for e in &errors {
eprintln!("manifest error: {e}");
}
std::process::exit(1);
}
// From string
let manifest = ServerManifest::from_toml(include_str!("../mcp-server.toml"))?;
// Programmatic
let manifest = ServerManifest {
server_id: "mcp_my_server".into(),
display_name: "My MCP Server".into(),
version: env!("CARGO_PKG_VERSION").into(),
domain: "platform".into(),
transports: vec![adk_mcp_sdk::manifest::Transport::Stdio],
risk_level: adk_mcp_sdk::risk::RiskLevel::Medium,
writes_allowed: adk_mcp_sdk::manifest::WritesAllowed::Gated,
sdk_version: "0.3.0".into(),
tools: vec![],
credentials: vec![],
governance_gates: vec![],
environments: vec!["development".into(), "production".into()],
};
4. Declare Tool Metadata
use adk_mcp_sdk::{RiskClass, ToolMeta};
let tools = vec![
ToolMeta {
name: "search_items".into(),
description: "Search items by query".into(),
risk_class: RiskClass::ReadOnly,
requires_approval: false,
credential_bindings: vec![],
},
ToolMeta {
name: "delete_item".into(),
description: "Delete an item permanently".into(),
risk_class: RiskClass::ExternalWrite,
requires_approval: true,
credential_bindings: vec!["vault://admin-key".into()],
},
];
Risk Classes
The governance engine uses risk classes to determine approval requirements, audit depth, and environment restrictions.
Risk Levels (Server-Level)
Server Manifest Reference
Complete Example: Building an MCP Server
use adk_mcp_sdk::{HealthCheck, HealthStatus};
use rmcp::{handler::server::wrapper::Parameters, schemars, tool, tool_router, ServiceExt, transport::stdio};
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct SearchInput {
pub query: String,
#[serde(default = "default_limit")]
pub limit: u32,
}
fn default_limit() -> u32 { 20 }
#[derive(Clone)]
pub struct MyServer;
#[tool_router]
impl MyServer {
#[tool(description = "Search items by query")]
async fn search(&self, Parameters(input): Parameters<SearchInput>) -> String {
format!("Found results for '{}' (limit {})", input.query, input.limit)
}
}
adk_mcp_sdk::mcp_2026_server! {
server: MyServer,
task_tools: ["search"],
task_ttl_overrides: [("search", 120_000)],
approval_tools: [],
mutating_tools: [],
destructive_tools: [],
idempotent_tools: [],
cache_ttl_ms: 86_400_000,
instructions: "Search the local item catalog. Results are read-only.",
}
#[async_trait::async_trait]
impl HealthCheck for MyServer {
async fn check_health(&self) -> HealthStatus {
HealthStatus { healthy: true, message: None, latency_ms: Some(1) }
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt().with_env_filter("info").init();
let service = MyServer.serve(stdio()).await?;
service.waiting().await?;
Ok(())
}
MCP 2026-07-28 runtime contract
SDK 0.3 provides a shared handler for the new stateless protocol while retaining legacy initialization for MCP 2025-11-25 clients:
- Per-request protocol, client identity, and capability metadata are read from
_metaby rmcp. Protected calls require a non-empty client identity. - Selected operations are returned as SEP-2663 Tasks when the client advertises
Tasks. Clients can get, update, and cancel those tasks. The extension is not
advertised when
task_toolsis empty, and per-tool TTL overrides prevent legitimate long-running work from expiring early. - Manifest-gated tools use SEP-2322 MRTR elicitation. The approval state is HMAC-protected, bound to the caller, tool, and arguments, and expires after two minutes. Legacy clients fail closed for protected tools.
- Tool lists carry SEP-2549
ttlMsand public cache scope. rmcp removes these fields automatically for legacy responses. - JSON object text returned by existing tools is mirrored into
structuredContent;{ "ok": false }is surfaced as a tool-level error. Tool listings include an objectoutputSchema, title, and standard behavior annotations. These annotations remain hints and never replace MRTR or policy enforcement. - Ordinary legacy tool calls continue to work after the initialize handshake.
Set MCP_REQUEST_STATE_KEY to at least 32 high-entropy bytes on every instance
that can receive a retry. All instances in a deployment must share the same key
so an MRTR flow can resume on any replica. Task storage is process-local in this
release; route task follow-ups to the creating instance or replace the manager
with a durable store before scale-to-zero deployment.
The minimum supported Rust version is 1.94.1. CI should test both MCP 2025-11-25 and 2026-07-28 behavior, including an ordinary legacy call, task lifecycle, approval rejection/tamper handling, and cache-hint serialization.
Servers Using This SDK
Creating a New Server
# Clone the template
git clone https://github.com/zavora-ai/mcp-server-template my-server
cd my-server
# Edit Cargo.toml — rename package, update description
# Edit mcp-server.toml — set server_id, tools, risk level
# Edit src/main.rs — implement your tools
# Build and test
cargo build
cargo test
# Publish
cargo publish
The template includes a working #[tool_router(server_handler)] example with HealthCheck, tracing, and stdio transport.
Integration with rmcp
This SDK provides metadata and governance — it does not replace rmcp which provides the MCP protocol transport. A typical server uses both:
[dependencies]
adk-mcp-sdk = "0.3" # Registry and MCP 2026 runtime contract
rmcp = { version = "=3.1.2", features = ["server", "transport-io", "macros"] } # MCP protocol
- rmcp handles: tool routing, JSON-RPC, stdio/HTTP transport, schema generation
- adk-mcp-sdk handles: health monitoring, manifest declaration, risk classification, credential binding
Documentation
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feat/my-feature) - Implement with tests
- Submit a pull request
Contributors
License
Apache-2.0 — see LICENSE for details.
Part of the ADK-Rust Enterprise MCP server ecosystem.
Built with ❤️ by Zavora AI