모델 컨텍스트 프로토콜 (MCP)
MCP은 AI 애플리케이션이 다른 프로세스나 서비스가 소유한 기능을 검색하고 사용할 수 있는 표준 방법을 제공합니다. 서버는 다음을 게시할 수 있습니다.
- 작업을 수행하는 도구;
- 읽을 수 있는 컨텍스트를 반환하는 리소스;
- 재사용 가능한 메시지 템플릿을 제공하는 프롬프트; 그리고
- 클라이언트가 프롬프트 또는 리소스 인수를 채우는 데 도움이 되는 완성 제안.
ADK-Rust은 일반적으로 MCP 클라이언트입니다. McpToolset은 검색된
MCP 도구를 일반적인 ADK-Rust Tool 값으로 변환하므로, LlmAgent이
이를 선택하고 호출할 수 있습니다. 프레임워크는 리소스, 프롬프트, 완성,
구독, 정보 요청 및 협상된 작업 수명 주기도 제공합니다. MCP 서버 작성과
고급 프로토콜 작업을 위해 ADK-Rust은 자신이 사용하는 정확한 rmcp
SDK 버전을 다시 내보냅니다.
ADK-Rust 2는 공식 Rust SDK인 rmcp 3.1를 사용합니다. 클라이언트는
MCP 2025-11-25을 알리고 레거시 핸드셰이크를 통해 2026-07-28 서버에
연결합니다. examples/mcp_protocol_revisions은 번들로 제공되는 서버를 대상으로 두 개정 버전과
SEP-2663 작업 확장을 모두 보여 줍니다.
아키텍처
두 개의 별도 계층이 있습니다.
McpToolset는 초기화된 MCP 클라이언트 연결 하나를 관리합니다. 서버의 기능을 검색하고 이를 ADK-Rust에 맞게 조정합니다.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?;
빌더는 요청 시간 초과, 사용자 지정 헤더, 전달자 토큰, 사용자 지정 API-키 헤더를 적용하고 HTTP 세션이 만료될 때 제한된 복구를 수행합니다.
OAuth2Config은 고정된 OAuth 2.0 클라이언트 자격 증명 토큰 요청을
구현합니다. 알려진 토큰 엔드포인트가 있는 서버에 유용합니다. 이는
완전한 MCP 인증 흐름이 아닙니다. 보호된 리소스 메타데이터 검색,
인증 서버 검색, 브라우저 인증, PKCE 또는 리소스 표시자 협상을 수행하지
않습니다. 배포 환경에 해당 흐름이 필요한 경우 rmcp의 인증
APIs 또는 외부 ID 구성 요소를 사용하세요.
정보 요청
MCP 서버에는 도구 인수에 포함되지 않은 정보가 필요할 수 있습니다. 이 경우 서버는 클라이언트에 정보 요청을 다시 보낼 수 있습니다. 애플리케이션은 이 요청을 사용자에게 표시하는 방법과 이를 수락, 거부 또는 취소할지 여부를 결정합니다.
let toolset = McpToolset::with_elicitation_handler(
transport,
Arc::new(MyElicitationHandler),
).await?;
ADK-Rust는 폼 및 URL 수집을 모두 제공합니다. 핸들러 오류 또는 패닉은 거부로 변환되므로 MCP 연결을 계속 사용할 수 있습니다. 결과 값을 검증하고, 중요한 요청을 수락하기 전에 애플리케이션에서 동의 규칙을 적용하세요.
완전한 서버 및 대화형 클라이언트는 examples/mcp_elicitation를 참조하세요.
장시간 실행되는 MCP 작업
MCP 2025-11-25은 도구 호출을 프로토콜 작업으로 전환할 수 있습니다. ADK-Rust은 서버가 tasks.requests.tools.call을 협상하고 도구가 필수 또는 선택적 작업 지원을 선언한 경우에만 작업 흐름을 사용합니다.
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는 다음을 수행합니다.
- 공식 작업 메타데이터와 함께
tools/call를 전송합니다. - 생성된 작업을 수신합니다.
- 서버가 제안한 간격을 사용하여
tasks/get을 폴링합니다. tasks/result를 통해 최종 페이로드를 읽습니다.- 로컬 시간 제한 또는 폴링 한도에 도달하면
tasks/cancel를 호출합니다.
일반적인 ADK 도구 호출에는 누락된 입력을 제공하기 위한 프로토콜 중립적인 재개 채널이 아직 없으므로 input_required은 유형이 지정된 오류로 반환됩니다. 이 상호작용은 해당 워크플로를 소유한 측에서 명시적으로 설계하세요.
기능 맵
| MCP 기능 | ADK-Rust 2 표면 | 참고 |
|---|---|---|
| 도구 탐색 및 호출 | McpToolset, Toolset | 원시 스키마, 멀티모달 및 구조화된 결과 보존 |
| 도구 필터링 | with_filter, with_tools | 모델에 노출하기 전에 필터링 |
| 리소스 및 템플릿 | list/read 메서드 | 이전 서버에서 메서드를 찾을 수 없음 처리 |
| 프롬프트 | list/get 메서드 | 타입이 지정된 인수 맵 |
| 자동 완성 | 프롬프트/리소스 자동 완성 메서드 | 공식 CompletionInfo 반환 |
| 리소스 구독 | subscribe/unsubscribe 메서드 | 알림을 처리하려면 적절한 클라이언트 핸들러가 필요 |
| 유도 | ElicitationHandler | 폼 및 URL 모드 |
| 작업 | McpTaskConfig | 협상된 도구 호출 작업 수명 주기 |
| 로컬 stdio | TokioChildProcess | 직접 또는 관리자 소유 |
| 스트리밍 가능한 HTTP | McpHttpClientBuilder | 시간 초과, 헤더, 인증 주입, 세션 복구 |
| 동적 로컬 레지스트리 | 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 검색 및 사용자 권한 부여 흐름은 아닙니다.
이러한 제한 사항은 배포 결정을 명확하게 유지하기 위해 명시되어 있습니다.