모델 컨텍스트 프로토콜 (MCP)
MCP는 AI 애플리케이션이 다른 프로세스나 서비스가 소유한 기능을 검색하고 사용하는 표준 방법을 제공합니다. 서버는 다음을 게시할 수 있습니다:
- tools는 작업을 수행합니다;
- resources는 읽을 수 있는 컨텍스트를 반환합니다;
- prompts는 재사용 가능한 메시지 템플릿을 제공합니다; 그리고
- completion 제안은 클라이언트가 prompt 또는 resource 인수를 채우는 데 도움을 줍니다.
ADK-Rust은(는) 일반적으로 MCP 클라이언트입니다. McpToolset은(는) 발견된 MCP tools를 일반 ADK-Rust Tool 값으로 변환하여 LlmAgent이(가) 이를 선택하고 호출할 수 있도록 합니다. 이 프레임워크는 또한 리소스, 프롬프트, 완료, 구독, 유도 및 협상된 작업 수명 주기를 노출합니다. MCP 서버 작성 및 고급 프로토콜 작업을 위해 ADK-Rust은(는) 사용하는 정확한 rmcp SDK 버전을 다시 내보냅니다.
ADK-Rust 2는 현재 rmcp 2.2를 사용하며, 이는 MCP 2025-11-25 사양에 맞춰진 공식 Rust SDK입니다.
아키텍처
두 개의 개별 레이어가 있습니다:
McpToolset은(는) 하나의 초기화된 MCP 클라이언트 연결을 소유합니다. 이는 서버의 기능을 검색하고 ADK-Rust에 맞게 조정합니다.McpServerManager은(는) 로컬 stdio 서버의 변경 가능한 레지스트리를 소유합니다. 이는 해당 연결을 시작하고, 모니터링하고, 다시 시작하고, 업데이트하고, 활성화하고, 비활성화하고, 유지하고, 집계합니다.
관리자는 도구 승인을 부여하지 않습니다. 호환 가능한 구성을 읽을 때 autoApprove을(를) 보존하지만, 애플리케이션은 일반적인 ADK-Rust 인증 및 승인 정책을 적용해야 합니다.
설치
로컬 stdio MCP 지원은 선택 사항입니다:
[dependencies]
adk-tool = { version = "2.0.0", features = ["mcp"] }
원격 서비스에 연결할 때 스트리밍 가능한 HTTP을(를) 추가하십시오:
adk-tool = { version = "2.0.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 server가 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?;
이전 server가 resource 또는 prompt 목록을 구현하지 않을 경우 편의 메서드는 빈 목록을 반환합니다. 선언된 resource 또는 prompt에 대한 작업은 원격 호출이 실패할 경우 오류를 반환합니다.
동적 서버 관리
애플리케이션이 하나의 정적 연결 대신 로컬 MCP child processes 집합을 필요로 할 때 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 client-credentials 토큰 요청을 구현합니다. 이는 알려진 토큰 엔드포인트를 가진 서버에 유용합니다. 이는 완전한 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을 호출합니다.
input_required는 일반적인 ADK tool call이 누락된 입력을 제공하기 위한 프로토콜 중립적인 재개 채널을 아직 가지고 있지 않기 때문에 유형화된 오류로 반환됩니다. 소유 워크플로우에서 해당 상호작용을 명시적으로 설계하십시오.
기능 맵
| MCP 기능 | ADK-Rust 2 표면 | 참고 |
|---|---|---|
| 도구 검색 및 호출 | McpToolset, Toolset | 원시 스키마; 다중 모드 및 구조화된 결과 보존 |
| 도구 필터링 | with_filter, with_tools | 모델에 노출하기 전에 필터링 |
| 리소스 및 템플릿 | list/read 메서드 | 이전 서버를 위해 처리된 Method-not-found |
| 프롬프트 | list/get 메서드 | 타입이 지정된 인자 맵 |
| 완료 | 프롬프트/리소스 완료 메서드 | 공식 CompletionInfo 반환 |
| 리소스 구독 | 구독/구독 취소 메서드 | 알림에는 적절한 클라이언트 핸들러가 필요합니다 |
| 도출 | ElicitationHandler | 형식 및 URL 모드 |
| 작업 | McpTaskConfig | 협상된 도구 호출 작업 수명 주기 |
| 로컬 stdio | TokioChildProcess | 직접 또는 관리자 소유 |
| 스트리밍 가능 HTTP | McpHttpClientBuilder | 타임아웃, 헤더, 인증 주입, 세션 복구 |
| 동적 로컬 레지스트리 | McpServerManager | 추가/업데이트/활성화/비활성화/제거/저장/모니터링/재시작 |
| 서버 작성 및 확장 | adk_tool::mcp::rmcp | 고급 사용을 위한 정확한 SDK 재내보내기 |
| 샘플링, 루트, 로깅 | 호환성 기능 / rmcp | Deprecated upstream (SEP-2577을 통해) |
경계 선택
기능이 동일한 프로세스 및 릴리스에 속하는 경우 Rust FunctionTool를 사용합니다. 다른 프로그램, 팀, 언어, 보안 경계 또는 배포가 기능을 소유하고 자체 계약을 게시해야 하는 경우 MCP를 사용합니다.
프로덕션 배포의 경우:
- 가장 작은 유용한 도구 세트를 노출합니다;
- 읽기 전용 작업과 결과적인 작업을 분리합니다;
- 명령줄 인수 및 커밋된
mcp.json파일에서 비밀을 제외합니다; - 원격 HTTP 서버를 인증하고 자격 증명 범위를 좁게 설정합니다;
- 도구 설명 및 서버에서 반환된 콘텐츠를 신뢰할 수 없는 입력으로 처리합니다;
- 도구 실행 주변의 ADK-Rust 권한 부여 및 승인을 유지합니다;
- 연결, 도구 및 작업 시간 초과를 제한합니다; 그리고
- 도구 호출, 승인, 오류 및 서버 수명 주기 변경 사항을 기록합니다.
현재 제한 사항
McpServerManager는 로컬 stdio 자식 프로세스를 관리합니다. 원격 HTTP 서비스는McpHttpClientBuilder및 애플리케이션 소유의 구성을 사용합니다.- 관리자 상태 확인은 닫힌 MCP 연결을 감지합니다. 이들은 비즈니스 수준의 상태 도구를 호출하지 않습니다.
- 레지스트리 변형은 자식이 MCP 핸드셰이크를 완료하는 동안 직렬화됩니다.
autoApprove는 구성 호환성이지, 권한 부여 강제가 아닙니다.- 내장된 OAuth 도우미는 클라이언트 자격 증명이지, 완전한 MCP OAuth 발견 및 사용자 권한 부여 흐름이 아닙니다.
이러한 제한은 배포 결정이 명시적으로 유지되도록 명시되어 있습니다.