构建 MCP 客户端

当 ADK-Rust 应用连接到服务器、读取其发布的目录,并将选定的能力提供给 agent 或工作流时,它就是一个 MCP 客户端。

安装

[dependencies]
adk-tool = { version = "2.1.0", features = ["mcp"] }

对于远程 Streamable HTTP:

adk-tool = { version = "2.1.0", features = ["mcp", "http-transport"] }

本地 stdio 连接

use adk_tool::{
    McpToolset,
    mcp::rmcp::{ServiceExt, transport::TokioChildProcess},
};
use std::sync::Arc;
use tokio::process::Command;

let command = Command::new("./target/release/company-mcp");
let client = ().serve(TokioChildProcess::new(command)?).await?;

let toolset = McpToolset::new(client)
    .with_name("company_tools")
    .with_tools(&["find_customer", "read_order", "request_refund"]);

let shutdown = toolset.cancellation_token().await;

let agent = LlmAgentBuilder::new("support")
    .model(model)
    .toolset(Arc::new(toolset))
    .build()?;

// Run the agent, then close the client-owned MCP session.
shutdown.cancel();

在生产环境中使用绝对二进制路径。避免在部署配置中使用 latest 等软件包标签,因为这会导致构建和事故恢复无法复现。

工具发现与筛选

McpToolset 会将每个已发布的 MCP 工具转换为一个 ADK-Rust Tool。它会保持服务器的输入和输出 schema 不变。所选模型提供商在构建请求时会对 schema 的副本进行规范化。

该适配器还会保留 MCP 工具注解。readOnlyHint 表示 ADK 工具是只读的,并且支持并发;idempotentHint 允许在重新连接后安全重放,但其本身不会使工具具备自动并行分发的资格。缺少提示会使这两种行为均保持禁用。

重要: MCP 注解是服务器发布的提示。仅对位于应用信任边界内的服务器使用自动重放和分发元数据。

let reviewed = McpToolset::new(client).with_filter(|name| {
    matches!(name, "read_order" | "read_policy" | "request_replacement")
});

筛选控制模型可见性,但不能替代工具执行时的授权。

资源、提示和补全

use serde_json::json;

let resources = toolset.list_resources().await?;
let templates = toolset.list_resource_templates().await?;
let policy = toolset.read_resource("company://policy/refunds").await?;

let prompts = toolset.list_prompts().await?;
let prompt = toolset
    .get_prompt(
        "investigate_order",
        Some(serde_json::Map::from_iter([
            ("order_id".to_string(), json!("ORD-1042")),
        ])),
    )
    .await?;

let suggestions = toolset
    .complete_prompt_argument("investigate_order", "order_id", "ORD-", None)
    .await?;

资源模板补全使用 complete_resource_argument。不实现列表操作的服务器在以 MCP MethodNotFound 响应时会返回空列表;其他协议和传输故障仍会作为错误处理。

资源订阅

use adk_tool::{AutoDeclineElicitationHandler, McpToolset, ResourceNotificationHandler};
use std::sync::Arc;

struct ResourceUpdates;

#[async_trait::async_trait]
impl ResourceNotificationHandler for ResourceUpdates {
    async fn handle_resource_updated(
        &self,
        uri: &str,
    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        println!("Resource changed: {uri}");
        Ok(())
    }

    async fn handle_resource_list_changed(
        &self,
    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        println!("The resource catalog changed");
        Ok(())
    }
}

let toolset = McpToolset::with_handlers(
    transport,
    Arc::new(AutoDeclineElicitationHandler),
    Arc::new(ResourceUpdates),
).await?;

toolset.subscribe_resource("company://inventory/sku-42").await?;
toolset.unsubscribe_resource("company://inventory/sku-42").await?;

McpToolset 在有界连接刷新后恢复活动订阅。 McpServerManager 还会在受管理的进程重启期间保留订阅。 处理程序错误和崩溃会被记录,而不会终止 MCP 连接。 对于可流式 HTTP,请在调用 connect_with_elicitation 之前使用 McpHttpClientBuilder::with_resource_notification_handler 配置相同的处理程序。

信息请求

信息请求允许服务器在处理工具调用时请求信息。 应用程序决定如何呈现请求,以及接受、拒绝还是取消请求。

let toolset = McpToolset::with_elicitation_handler(
    transport,
    Arc::new(MyElicitationHandler),
).await?;

ADK-Rust 宣布支持表单和 URL 信息请求。处理程序失败或崩溃会变为拒绝, 从而保留 MCP 会话。应用程序仍必须验证接受的值并应用同意策略。

请参阅 examples/mcp_elicitation,了解完整的客户端和服务器配对示例。

协商任务

use adk_tool::McpTaskConfig;
use std::time::Duration;

let toolset = McpToolset::new(client).with_task_support(
    McpTaskConfig::enabled()
        .poll_interval(Duration::from_secs(1))
        .timeout(Duration::from_secs(120))
        .max_attempts(120),
);

任务模式根据两个协商事实进行选择:

  1. 服务器宣布支持 tasks.requests.tools.call;以及
  2. 工具声明任务支持为必需或可选。

ADK-Rust 通过 tools/call 发送任务元数据,接收创建的任务,轮询 tasks/get,读取 tasks/result,并在超出本地限制时调用 tasks/cancel。 由于普通的 ADK 工具调用目前尚未提供协议中立的任务恢复输入通道, input_required 会作为类型化错误返回。

远程可流式 HTTP

use adk_tool::{McpAuth, McpHttpClientBuilder};
use std::time::Duration;

let toolset = McpHttpClientBuilder::new("https://mcp.example.com/mcp")
    .with_auth(McpAuth::bearer(std::env::var("MCP_TOKEN")?))
    .header("X-Tenant-ID", "tenant-42")
    .timeout(Duration::from_secs(30))
    .reinit_on_expired_session(true)
    .connect()
    .await?;

构建器支持 bearer token、自定义 API-key 标头以及固定的 OAuth 2.0 client credentials。在选择身份验证流程之前,请参阅安全性和授权

构建 MCP 客户端 - ADK-Rust 文档 | ADK-Rust