模型上下文协议(MCP)

文档导航: 概述与架构 · 客户端 · 动态管理器 · 服务器编写 · 安全性 · 测试

MCP 为 AI 应用提供了一种标准方式,用于发现和使用由其他进程或服务拥有的能力。服务器可以发布:

  • 执行操作的 工具;
  • 返回可读上下文的 资源;
  • 提供可复用消息模板的 提示;以及
  • 帮助客户端填写提示或资源参数的 补全建议。

ADK-Rust 通常是 MCP 客户端。McpToolset 会将发现的 MCP 工具转换为普通的 ADK-Rust Tool 值,使 LlmAgent 能够选择并调用它们。该框架还提供资源、提示、补全、订阅、引导请求以及协商后的任务生命周期。对于 MCP 服务器编写和高级协议工作,ADK-Rust 会重新导出其使用的确切 rmcp SDK 版本。

ADK-Rust 2 使用 rmcp 3.1,即官方 Rust SDK。客户端会公布 MCP 2025-11-25,并通过其旧版握手连接到 2026-07-28 服务器。examples/mcp_protocol_revisions 演示了两个修订版本,以及针对捆绑服务器的 SEP-2663 任务扩展。

架构

Rendering architecture…

这里有两个独立的层:

  1. McpToolset 负责一个已初始化的 MCP 客户端连接。它发现服务器的能力,并将其适配到 ADK-Rust。
  2. McpServerManager 负责本地 stdio 服务器的动态注册表。它启动、监控、重启、更新、启用、禁用、持久化并聚合这些连接。

管理器不会授予工具批准权限。读取兼容配置时,它会保留 autoApprove,但应用程序必须应用其常规的 ADK-Rust 授权和批准策略。

安装

本地 stdio MCP 支持需要选择启用:

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

连接远程服务时添加 Streamable HTTP:

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

旧版采样回调需要单独启用 mcp-sampling 特性。MCP 项目已通过 SEP-2577 弃用采样、根目录和日志记录;仅在维护兼容部署时使用这些 APIs。

连接一个本地服务器

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 agent = LlmAgentBuilder::new("support")
    .model(model)
    .toolset(Arc::new(toolset.clone()))
    .build()?;

// Keep the token when the application owns the process lifecycle.
let shutdown = toolset.cancellation_token().await;
// ... run the agent ...
shutdown.cancel();

McpToolset 会保持服务器的输入和输出模式不变。每个模型适配器在构建模型请求时, 都会针对其提供商规范化一份副本。 这样,同一个 MCP 服务器就能与 Gemini、OpenAI、Anthropic 以及其他 提供商协同工作,而不会破坏源模式。

将协议用于工具之外

use serde_json::json;

let resources = toolset.list_resources().await?;
let templates = toolset.list_resource_templates().await?;
let contents = 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?;

toolset.subscribe_resource("company://inventory/sku-42").await?;
// ... receive notifications in a custom ClientHandler ...
toolset.unsubscribe_resource("company://inventory/sku-42").await?;

当较旧的服务器未实现资源或提示列表功能时,便捷方法会返回空列表。 对已声明资源或提示执行操作时,如果远程调用失败,则会返回错误。

动态管理服务器

当应用程序需要管理一组本地 MCP 子进程,而不是一个静态连接时,请使用 McpServerManager。

use adk_tool::mcp::manager::{McpServerConfig, McpServerManager};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;

let manager = Arc::new(McpServerManager::from_json_file("mcp.json")?
    .with_name("product_mcp_servers")
    .with_health_check_interval(Duration::from_secs(15))
    .with_grace_period(Duration::from_secs(2)));

let outcomes = manager.start_all().await;
for (server_id, outcome) in outcomes {
    if let Err(error) = outcome {
        eprintln!("{server_id} did not start: {error}");
    }
}
manager.start_monitoring();

let agent = LlmAgentBuilder::new("operator")
    .model(model)
    .toolset(manager.clone())
    .build()?;

运行时注册表支持:

manager.add_server("billing".into(), billing_config).await?;
manager.start_server("billing").await?;

manager.update_server("billing", replacement_config).await?;
manager.disable_server("billing").await?;
manager.enable_server("billing").await?;

manager.save_json_file("mcp.json").await?;
manager.remove_server("billing").await?;
manager.shutdown().await?;

当两个服务器发布同名工具时,聚合工具集会将两个名称都添加前缀 {server_id}__{tool_name}。 唯一名称保持不变。

健康监视器检测到已关闭的 MCP 连接。配置的 RestartPolicy 通过指数退避控制有界重试。这是连接监控,而不是应用层健康检查:需要验证服务器后端数据库或外部 API 时,请使用领域工具或单独的服务探针。

运行确定性示例:

cargo run --manifest-path examples/mcp_manager/Cargo.toml

它会启动一个真实的 Rust MCP 子服务器,并测试发现、工具调用、 运行时添加/启用/更新/禁用/移除、配置持久化和关闭流程。它不会下载软件包,也不需要 API 密钥。

远程可流式 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 令牌、自定义 API-key 标头,以及在 HTTP 会话过期时进行有界恢复。

OAuth2Config 实现固定的 OAuth 2.0 客户端凭据令牌请求。 它适用于具有已知令牌端点的服务器。但它不是完整的 MCP 授权流程:不会执行受保护资源元数据发现、 授权服务器发现、浏览器授权、PKCE 或资源指示器协商。部署需要该流程时,请使用 rmcp 的授权 APIs,或使用外部 身份组件。

引导式信息收集

MCP 服务器可能需要工具参数中未包含的信息。在 这种情况下,它可以向客户端发送引导式信息收集请求。应用程序决定如何向用户显示该请求,以及是否接受、拒绝或 取消该请求。

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

ADK-Rust 同时支持表单和 URL 引导式收集。处理程序错误或崩溃会被转换为拒绝,以便 MCP 连接保持可用。在接受具有后果的请求之前,请在应用程序中验证返回值并应用用户同意规则。

有关完整的服务器和交互式客户端,请参阅 examples/mcp_elicitation。

长时间运行的 MCP 任务

MCP 2025-11-25 可以将工具调用转为协议任务。只有当服务器协商了 tasks.requests.tools.call,并且工具声明支持必需或可选任务时,ADK-Rust 才会使用任务流程。

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),
);

在任务模式下,ADK-Rust:

  1. 使用官方任务元数据发送 tools/call;
  2. 接收创建的任务;
  3. 按照服务器建议的间隔轮询 tasks/get;
  4. 通过 tasks/result 读取最终载荷;以及
  5. 在达到本地超时或轮询次数限制时调用 tasks/cancel。

input_required 会作为类型化错误返回,因为普通的 ADK 工具调用目前还没有用于提供缺失输入的协议中立恢复通道。请在所属工作流中明确设计该交互。

能力映射

MCP 能力ADK-Rust 2 表面注释
工具发现与调用McpToolset, Toolset保留原始模式、多模态结果和结构化结果
工具筛选with_filter, with_tools在向模型公开之前进行筛选
资源和模板列出/读取方法为旧版服务器处理方法未找到错误
提示列出/获取方法类型化参数映射
补全提示/资源补全方法返回官方 CompletionInfo
资源订阅订阅/取消订阅方法通知需要适当的客户端处理程序
启发ElicitationHandler表单和 URL 模式
任务McpTaskConfig协商式工具调用任务生命周期
本地标准输入输出TokioChildProcess直接拥有或由管理器拥有
可流式传输的 HTTPMcpHttpClientBuilder超时、标头、身份验证注入、会话恢复
动态本地注册表McpServerManager添加/更新/启用/禁用/移除/保存/监控/重启
服务器编写与扩展adk_tool::mcp::rmcp用于高级用途的精确 SDK 再导出
采样、根目录、日志记录兼容性功能 / rmcp上游已通过 SEP-2577 弃用

选择边界

当能力属于同一进程和发布版本时,使用 Rust FunctionTool。当另一个程序、团队、语言、安全边界或部署拥有该能力,并且应发布自己的契约时,使用 MCP。

对于生产部署:

  • 暴露最小的实用工具集;
  • 分离只读操作和有重大影响的操作;
  • 不要将机密放入命令行参数和已提交的 mcp.json 文件中;
  • 对远程 HTTP 服务器进行身份验证,并严格限定凭据的使用范围;
  • 将工具描述和服务器返回的内容视为不受信任的输入;
  • 在工具执行周围保留 ADK-Rust 授权和审批;
  • 限制连接、工具和任务的超时时间;以及
  • 记录工具调用、审批、错误和服务器生命周期变更。

当前限制

  • McpServerManager 管理本地 stdio 子进程。远程 HTTP 服务使用 McpHttpClientBuilder 和由应用管理的配置。
  • 管理器健康检查会检测已关闭的 MCP 连接;但不会调用业务级健康工具。
  • 注册表变更会在子进程完成其 MCP 握手期间进行序列化。
  • autoApprove 是配置兼容性机制,而不是授权强制机制。
  • 内置的 OAuth 辅助工具用于客户端凭据,并不包含完整的 MCP OAuth 发现和用户授权流程。

明确说明这些限制,是为了确保部署决策保持清晰。

参考资料