Upgrade rmcp to 0.14 (#10718)

- [x] Upgrade rmcp to 0.14
This commit is contained in:
Matthew Zeng 2026-02-08 15:07:53 -08:00 committed by GitHub
parent ef5d26e586
commit 9f1009540b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 193 additions and 100 deletions

13
codex-rs/Cargo.lock generated
View file

@ -754,6 +754,7 @@ checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
dependencies = [
"axum-core",
"bytes",
"form_urlencoded",
"futures-util",
"http 1.4.0",
"http-body",
@ -769,11 +770,13 @@ dependencies = [
"serde_core",
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
@ -792,6 +795,7 @@ dependencies = [
"sync_wrapper",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
@ -7113,11 +7117,12 @@ dependencies = [
[[package]]
name = "rmcp"
version = "0.12.0"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528d42f8176e6e5e71ea69182b17d1d0a19a6b3b894b564678b74cd7cab13cfa"
checksum = "0a621b37a548ff6ab6292d57841eb25785a7f146d89391a19c9f199414bd13da"
dependencies = [
"async-trait",
"axum",
"base64 0.22.1",
"bytes",
"chrono",
@ -7148,9 +7153,9 @@ dependencies = [
[[package]]
name = "rmcp-macros"
version = "0.12.0"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3f81daaa494eb8e985c9462f7d6ce1ab05e5299f48aafd76cdd3d8b060e6f59"
checksum = "6b79ed92303f9262db79575aa8c3652581668e9d136be6fd0b9ededa78954c95"
dependencies = [
"darling 0.23.0",
"proc-macro2",

View file

@ -194,7 +194,7 @@ ratatui-macros = "0.6.0"
regex = "1.12.2"
regex-lite = "0.1.8"
reqwest = "0.12"
rmcp = { version = "0.12.0", default-features = false }
rmcp = { version = "0.14.0", default-features = false }
rustls = { version = "0.23", default-features = false, features = ["ring", "std"] }
runfiles = { git = "https://github.com/dzbarsky/rules_rust", rev = "b56cbaa8465e74127f1ea216f813cd377295ad81" }
schemars = "0.8.22"

View file

@ -288,7 +288,7 @@ impl ServerHandler for AppListMcpServer {
fn list_tools(
&self,
_request: Option<rmcp::model::PaginatedRequestParam>,
_request: Option<rmcp::model::PaginatedRequestParams>,
_context: rmcp::service::RequestContext<rmcp::service::RoleServer>,
) -> impl std::future::Future<Output = Result<ListToolsResult, rmcp::ErrorData>> + Send + '_
{

View file

@ -77,8 +77,8 @@ use futures::prelude::*;
use futures::stream::FuturesOrdered;
use rmcp::model::ListResourceTemplatesResult;
use rmcp::model::ListResourcesResult;
use rmcp::model::PaginatedRequestParam;
use rmcp::model::ReadResourceRequestParam;
use rmcp::model::PaginatedRequestParams;
use rmcp::model::ReadResourceRequestParams;
use rmcp::model::ReadResourceResult;
use rmcp::model::RequestId;
use serde_json;
@ -2501,7 +2501,7 @@ impl Session {
pub async fn list_resources(
&self,
server: &str,
params: Option<PaginatedRequestParam>,
params: Option<PaginatedRequestParams>,
) -> anyhow::Result<ListResourcesResult> {
self.services
.mcp_connection_manager
@ -2514,7 +2514,7 @@ impl Session {
pub async fn list_resource_templates(
&self,
server: &str,
params: Option<PaginatedRequestParam>,
params: Option<PaginatedRequestParams>,
) -> anyhow::Result<ListResourceTemplatesResult> {
self.services
.mcp_connection_manager
@ -2527,7 +2527,7 @@ impl Session {
pub async fn read_resource(
&self,
server: &str,
params: ReadResourceRequestParam,
params: ReadResourceRequestParams,
) -> anyhow::Result<ReadResourceResult> {
self.services
.mcp_connection_manager

View file

@ -45,12 +45,12 @@ use futures::future::Shared;
use rmcp::model::ClientCapabilities;
use rmcp::model::ElicitationCapability;
use rmcp::model::Implementation;
use rmcp::model::InitializeRequestParam;
use rmcp::model::InitializeRequestParams;
use rmcp::model::ListResourceTemplatesResult;
use rmcp::model::ListResourcesResult;
use rmcp::model::PaginatedRequestParam;
use rmcp::model::PaginatedRequestParams;
use rmcp::model::ProtocolVersion;
use rmcp::model::ReadResourceRequestParam;
use rmcp::model::ReadResourceRequestParams;
use rmcp::model::ReadResourceResult;
use rmcp::model::RequestId;
use rmcp::model::Resource;
@ -551,7 +551,8 @@ impl McpConnectionManager {
let mut cursor: Option<String> = None;
loop {
let params = cursor.as_ref().map(|next| PaginatedRequestParam {
let params = cursor.as_ref().map(|next| PaginatedRequestParams {
meta: None,
cursor: Some(next.clone()),
});
let response = match client.list_resources(params, timeout).await {
@ -616,7 +617,8 @@ impl McpConnectionManager {
let mut cursor: Option<String> = None;
loop {
let params = cursor.as_ref().map(|next| PaginatedRequestParam {
let params = cursor.as_ref().map(|next| PaginatedRequestParams {
meta: None,
cursor: Some(next.clone()),
});
let response = match client.list_resource_templates(params, timeout).await {
@ -706,7 +708,7 @@ impl McpConnectionManager {
pub async fn list_resources(
&self,
server: &str,
params: Option<PaginatedRequestParam>,
params: Option<PaginatedRequestParams>,
) -> Result<ListResourcesResult> {
let managed = self.client_by_name(server).await?;
let timeout = managed.tool_timeout;
@ -722,7 +724,7 @@ impl McpConnectionManager {
pub async fn list_resource_templates(
&self,
server: &str,
params: Option<PaginatedRequestParam>,
params: Option<PaginatedRequestParams>,
) -> Result<ListResourceTemplatesResult> {
let managed = self.client_by_name(server).await?;
let client = managed.client.clone();
@ -738,7 +740,7 @@ impl McpConnectionManager {
pub async fn read_resource(
&self,
server: &str,
params: ReadResourceRequestParam,
params: ReadResourceRequestParams,
) -> Result<ReadResourceResult> {
let managed = self.client_by_name(server).await?;
let client = managed.client.clone();
@ -921,7 +923,8 @@ async fn start_server_task(
tx_event: Sender<Event>,
elicitation_requests: ElicitationRequestManager,
) -> Result<ManagedClient, StartupOutcomeError> {
let params = InitializeRequestParam {
let params = InitializeRequestParams {
meta: None,
capabilities: ClientCapabilities {
experimental: None,
roots: None,
@ -931,6 +934,7 @@ async fn start_server_task(
elicitation: Some(ElicitationCapability {
schema_validation: None,
}),
tasks: None,
},
client_info: Implementation {
name: "codex-mcp-client".to_owned(),

View file

@ -8,8 +8,8 @@ use async_trait::async_trait;
use codex_protocol::mcp::CallToolResult;
use rmcp::model::ListResourceTemplatesResult;
use rmcp::model::ListResourcesResult;
use rmcp::model::PaginatedRequestParam;
use rmcp::model::ReadResourceRequestParam;
use rmcp::model::PaginatedRequestParams;
use rmcp::model::ReadResourceRequestParams;
use rmcp::model::ReadResourceResult;
use rmcp::model::Resource;
use rmcp::model::ResourceTemplate;
@ -262,7 +262,8 @@ async fn handle_list_resources(
let payload_result: Result<ListResourcesPayload, FunctionCallError> = async {
if let Some(server_name) = server.clone() {
let params = cursor.clone().map(|value| PaginatedRequestParam {
let params = cursor.clone().map(|value| PaginatedRequestParams {
meta: None,
cursor: Some(value),
});
let result = session
@ -367,7 +368,8 @@ async fn handle_list_resource_templates(
let payload_result: Result<ListResourceTemplatesPayload, FunctionCallError> = async {
if let Some(server_name) = server.clone() {
let params = cursor.clone().map(|value| PaginatedRequestParam {
let params = cursor.clone().map(|value| PaginatedRequestParams {
meta: None,
cursor: Some(value),
});
let result = session
@ -474,7 +476,13 @@ async fn handle_read_resource(
let payload_result: Result<ReadResourcePayload, FunctionCallError> = async {
let result = session
.read_resource(&server, ReadResourceRequestParam { uri: uri.clone() })
.read_resource(
&server,
ReadResourceRequestParams {
meta: None,
uri: uri.clone(),
},
)
.await
.map_err(|err| {
FunctionCallError::RespondToModel(format!("resources/read failed: {err:#}"))
@ -689,6 +697,7 @@ mod tests {
title: None,
description: None,
mime_type: None,
icons: None,
}
.no_annotation()
}

View file

@ -185,7 +185,7 @@ impl ServerHandler for ExecTool {
async fn initialize(
&self,
_request: InitializeRequestParam,
_request: InitializeRequestParams,
_context: RequestContext<RoleServer>,
) -> Result<InitializeResult, McpError> {
Ok(self.get_info())

View file

@ -4,7 +4,7 @@ use codex_core::sandboxing::SandboxPermissions;
use codex_execpolicy::Policy;
use rmcp::ErrorData as McpError;
use rmcp::RoleServer;
use rmcp::model::CreateElicitationRequestParam;
use rmcp::model::CreateElicitationRequestParams;
use rmcp::model::CreateElicitationResult;
use rmcp::model::ElicitationAction;
use rmcp::model::ElicitationSchema;
@ -69,7 +69,8 @@ impl McpEscalationPolicy {
.pause_for(async {
context
.peer
.create_elicitation(CreateElicitationRequestParam {
.create_elicitation(CreateElicitationRequestParams {
meta: None,
message: format!(
"Allow agent to run `{command}` in `{}`?",
workdir.display()

View file

@ -9,7 +9,7 @@ use rmcp::Service;
use rmcp::model::ClientCapabilities;
use rmcp::model::ClientInfo;
use rmcp::model::ClientRequest;
use rmcp::model::CreateElicitationRequestParam;
use rmcp::model::CreateElicitationRequestParams;
use rmcp::model::CreateElicitationResult;
use rmcp::model::CustomRequest;
use rmcp::model::ElicitationAction;
@ -142,7 +142,7 @@ where
pub struct InteractiveClient {
pub elicitations_to_accept: HashSet<String>,
pub elicitation_requests: Arc<Mutex<Vec<CreateElicitationRequestParam>>>,
pub elicitation_requests: Arc<Mutex<Vec<CreateElicitationRequestParams>>>,
}
impl ClientHandler for InteractiveClient {
@ -156,7 +156,7 @@ impl ClientHandler for InteractiveClient {
fn create_elicitation(
&self,
request: CreateElicitationRequestParam,
request: CreateElicitationRequestParams,
_context: rmcp::service::RequestContext<RoleClient>,
) -> impl std::future::Future<Output = Result<CreateElicitationResult, McpError>> + Send + '_
{

View file

@ -15,9 +15,9 @@ use exec_server_test_support::write_default_execpolicy;
use maplit::hashset;
use pretty_assertions::assert_eq;
use rmcp::ServiceExt;
use rmcp::model::CallToolRequestParam;
use rmcp::model::CallToolRequestParams;
use rmcp::model::CallToolResult;
use rmcp::model::CreateElicitationRequestParam;
use rmcp::model::CreateElicitationRequestParams;
use rmcp::model::EmptyResult;
use rmcp::model::ServerResult;
use rmcp::model::object;
@ -64,7 +64,7 @@ prefix_rule(
git_path,
project_root_path.display()
);
let elicitation_requests: Arc<Mutex<Vec<CreateElicitationRequestParam>>> = Default::default();
let elicitation_requests: Arc<Mutex<Vec<CreateElicitationRequestParams>>> = Default::default();
let client = InteractiveClient {
elicitations_to_accept: hashset! { expected_elicitation_message.clone() },
elicitation_requests: elicitation_requests.clone(),
@ -96,7 +96,8 @@ prefix_rule(
let CallToolResult {
content, is_error, ..
} = service
.call_tool(CallToolRequestParam {
.call_tool(CallToolRequestParams {
meta: None,
name: Cow::Borrowed("shell"),
arguments: Some(object(json!(
{
@ -105,6 +106,7 @@ prefix_rule(
"workdir": project_root_path.to_string_lossy(),
}
))),
task: None,
})
.await?;
let tool_call_content = content

View file

@ -9,7 +9,7 @@ use codex_core::default_client::get_codex_user_agent;
use codex_core::protocol::Submission;
use codex_protocol::ThreadId;
use codex_protocol::protocol::SessionSource;
use rmcp::model::CallToolRequestParam;
use rmcp::model::CallToolRequestParams;
use rmcp::model::CallToolResult;
use rmcp::model::ClientNotification;
use rmcp::model::ClientRequest;
@ -115,6 +115,22 @@ impl MessageProcessor {
ClientRequest::CompleteRequest(params) => {
self.handle_complete(params.params);
}
ClientRequest::GetTaskInfoRequest(_) => {
self.handle_unsupported_request(request_id, "tasks/get_info")
.await;
}
ClientRequest::ListTasksRequest(_) => {
self.handle_unsupported_request(request_id, "tasks/list")
.await;
}
ClientRequest::GetTaskResultRequest(_) => {
self.handle_unsupported_request(request_id, "tasks/get_result")
.await;
}
ClientRequest::CancelTaskRequest(_) => {
self.handle_unsupported_request(request_id, "tasks/cancel")
.await;
}
ClientRequest::CustomRequest(custom) => {
let method = custom.method.clone();
self.outgoing
@ -167,7 +183,7 @@ impl MessageProcessor {
async fn handle_initialize(
&mut self,
id: RequestId,
params: rmcp::model::InitializeRequestParam,
params: rmcp::model::InitializeRequestParams,
) {
tracing::info!("initialize -> params: {:?}", params);
@ -256,38 +272,38 @@ impl MessageProcessor {
self.outgoing.send_response(id, json!({})).await;
}
fn handle_list_resources(&self, params: Option<rmcp::model::PaginatedRequestParam>) {
fn handle_list_resources(&self, params: Option<rmcp::model::PaginatedRequestParams>) {
tracing::info!("resources/list -> params: {:?}", params);
}
fn handle_list_resource_templates(&self, params: Option<rmcp::model::PaginatedRequestParam>) {
fn handle_list_resource_templates(&self, params: Option<rmcp::model::PaginatedRequestParams>) {
tracing::info!("resources/templates/list -> params: {:?}", params);
}
fn handle_read_resource(&self, params: rmcp::model::ReadResourceRequestParam) {
fn handle_read_resource(&self, params: rmcp::model::ReadResourceRequestParams) {
tracing::info!("resources/read -> params: {:?}", params);
}
fn handle_subscribe(&self, params: rmcp::model::SubscribeRequestParam) {
fn handle_subscribe(&self, params: rmcp::model::SubscribeRequestParams) {
tracing::info!("resources/subscribe -> params: {:?}", params);
}
fn handle_unsubscribe(&self, params: rmcp::model::UnsubscribeRequestParam) {
fn handle_unsubscribe(&self, params: rmcp::model::UnsubscribeRequestParams) {
tracing::info!("resources/unsubscribe -> params: {:?}", params);
}
fn handle_list_prompts(&self, params: Option<rmcp::model::PaginatedRequestParam>) {
fn handle_list_prompts(&self, params: Option<rmcp::model::PaginatedRequestParams>) {
tracing::info!("prompts/list -> params: {:?}", params);
}
fn handle_get_prompt(&self, params: rmcp::model::GetPromptRequestParam) {
fn handle_get_prompt(&self, params: rmcp::model::GetPromptRequestParams) {
tracing::info!("prompts/get -> params: {:?}", params);
}
async fn handle_list_tools(
&self,
id: RequestId,
params: Option<rmcp::model::PaginatedRequestParam>,
params: Option<rmcp::model::PaginatedRequestParams>,
) {
tracing::trace!("tools/list -> {params:?}");
let result = rmcp::model::ListToolsResult {
@ -302,9 +318,11 @@ impl MessageProcessor {
self.outgoing.send_response(id, result).await;
}
async fn handle_call_tool(&self, id: RequestId, params: CallToolRequestParam) {
async fn handle_call_tool(&self, id: RequestId, params: CallToolRequestParams) {
tracing::info!("tools/call -> params: {:?}", params);
let CallToolRequestParam { name, arguments } = params;
let CallToolRequestParams {
name, arguments, ..
} = params;
match name.as_ref() {
"codex" => self.handle_tool_call_codex(id, arguments).await,
@ -496,14 +514,27 @@ impl MessageProcessor {
});
}
fn handle_set_level(&self, params: rmcp::model::SetLevelRequestParam) {
fn handle_set_level(&self, params: rmcp::model::SetLevelRequestParams) {
tracing::info!("logging/setLevel -> params: {:?}", params);
}
fn handle_complete(&self, params: rmcp::model::CompleteRequestParam) {
fn handle_complete(&self, params: rmcp::model::CompleteRequestParams) {
tracing::info!("completion/complete -> params: {:?}", params);
}
async fn handle_unsupported_request(&self, id: RequestId, method: &str) {
self.outgoing
.send_error(
id,
ErrorData::new(
ErrorCode::METHOD_NOT_FOUND,
format!("method not found: {method}"),
Some(json!({ "method": method })),
),
)
.await;
}
// ---------------------------------------------------------------------
// Notification handlers
// ---------------------------------------------------------------------

View file

@ -13,13 +13,13 @@ use anyhow::Context;
use codex_mcp_server::CodexToolCallParam;
use pretty_assertions::assert_eq;
use rmcp::model::CallToolRequestParam;
use rmcp::model::CallToolRequestParams;
use rmcp::model::ClientCapabilities;
use rmcp::model::CustomNotification;
use rmcp::model::CustomRequest;
use rmcp::model::ElicitationCapability;
use rmcp::model::Implementation;
use rmcp::model::InitializeRequestParam;
use rmcp::model::InitializeRequestParams;
use rmcp::model::JsonRpcMessage;
use rmcp::model::JsonRpcNotification;
use rmcp::model::JsonRpcRequest;
@ -112,7 +112,8 @@ impl McpProcess {
pub async fn initialize(&mut self) -> anyhow::Result<()> {
let request_id = self.next_request_id.fetch_add(1, Ordering::Relaxed);
let params = InitializeRequestParam {
let params = InitializeRequestParams {
meta: None,
capabilities: ClientCapabilities {
elicitation: Some(ElicitationCapability {
schema_validation: None,
@ -120,6 +121,7 @@ impl McpProcess {
experimental: None,
roots: None,
sampling: None,
tasks: None,
},
client_info: Implementation {
name: "elicitation test".into(),
@ -194,12 +196,14 @@ impl McpProcess {
&mut self,
params: CodexToolCallParam,
) -> anyhow::Result<i64> {
let codex_tool_call_params = CallToolRequestParam {
let codex_tool_call_params = CallToolRequestParams {
meta: None,
name: "codex".into(),
arguments: Some(match serde_json::to_value(params)? {
serde_json::Value::Object(map) => map,
_ => unreachable!("params serialize to object"),
}),
task: None,
};
self.send_request(
"tools/call",

View file

@ -5,11 +5,11 @@ use std::sync::Arc;
use rmcp::ErrorData as McpError;
use rmcp::ServiceExt;
use rmcp::handler::server::ServerHandler;
use rmcp::model::CallToolRequestParam;
use rmcp::model::CallToolRequestParams;
use rmcp::model::CallToolResult;
use rmcp::model::JsonObject;
use rmcp::model::ListToolsResult;
use rmcp::model::PaginatedRequestParam;
use rmcp::model::PaginatedRequestParams;
use rmcp::model::ServerCapabilities;
use rmcp::model::ServerInfo;
use rmcp::model::Tool;
@ -73,7 +73,7 @@ impl ServerHandler for TestToolServer {
fn list_tools(
&self,
_request: Option<PaginatedRequestParam>,
_request: Option<PaginatedRequestParams>,
_context: rmcp::service::RequestContext<rmcp::service::RoleServer>,
) -> impl std::future::Future<Output = Result<ListToolsResult, McpError>> + Send + '_ {
let tools = self.tools.clone();
@ -88,7 +88,7 @@ impl ServerHandler for TestToolServer {
async fn call_tool(
&self,
request: CallToolRequestParam,
request: CallToolRequestParams,
_context: rmcp::service::RequestContext<rmcp::service::RoleServer>,
) -> Result<CallToolResult, McpError> {
match request.name.as_ref() {

View file

@ -5,16 +5,16 @@ use std::sync::Arc;
use rmcp::ErrorData as McpError;
use rmcp::ServiceExt;
use rmcp::handler::server::ServerHandler;
use rmcp::model::CallToolRequestParam;
use rmcp::model::CallToolRequestParams;
use rmcp::model::CallToolResult;
use rmcp::model::JsonObject;
use rmcp::model::ListResourceTemplatesResult;
use rmcp::model::ListResourcesResult;
use rmcp::model::ListToolsResult;
use rmcp::model::PaginatedRequestParam;
use rmcp::model::PaginatedRequestParams;
use rmcp::model::RawResource;
use rmcp::model::RawResourceTemplate;
use rmcp::model::ReadResourceRequestParam;
use rmcp::model::ReadResourceRequestParams;
use rmcp::model::ReadResourceResult;
use rmcp::model::Resource;
use rmcp::model::ResourceContents;
@ -171,6 +171,7 @@ impl TestToolServer {
"Template for memo://codex/{slug} resources used in tests.".to_string(),
),
mime_type: Some("text/plain".to_string()),
icons: None,
};
ResourceTemplate::new(raw, None)
}
@ -227,7 +228,7 @@ impl ServerHandler for TestToolServer {
fn list_tools(
&self,
_request: Option<PaginatedRequestParam>,
_request: Option<PaginatedRequestParams>,
_context: rmcp::service::RequestContext<rmcp::service::RoleServer>,
) -> impl std::future::Future<Output = Result<ListToolsResult, McpError>> + Send + '_ {
let tools = self.tools.clone();
@ -242,7 +243,7 @@ impl ServerHandler for TestToolServer {
fn list_resources(
&self,
_request: Option<PaginatedRequestParam>,
_request: Option<PaginatedRequestParams>,
_context: rmcp::service::RequestContext<rmcp::service::RoleServer>,
) -> impl std::future::Future<Output = Result<ListResourcesResult, McpError>> + Send + '_ {
let resources = self.resources.clone();
@ -257,7 +258,7 @@ impl ServerHandler for TestToolServer {
async fn list_resource_templates(
&self,
_request: Option<PaginatedRequestParam>,
_request: Option<PaginatedRequestParams>,
_context: rmcp::service::RequestContext<rmcp::service::RoleServer>,
) -> Result<ListResourceTemplatesResult, McpError> {
Ok(ListResourceTemplatesResult {
@ -269,7 +270,7 @@ impl ServerHandler for TestToolServer {
async fn read_resource(
&self,
ReadResourceRequestParam { uri }: ReadResourceRequestParam,
ReadResourceRequestParams { uri, .. }: ReadResourceRequestParams,
_context: rmcp::service::RequestContext<rmcp::service::RoleServer>,
) -> Result<ReadResourceResult, McpError> {
if uri == MEMO_URI {
@ -291,7 +292,7 @@ impl ServerHandler for TestToolServer {
async fn call_tool(
&self,
request: CallToolRequestParam,
request: CallToolRequestParams,
_context: rmcp::service::RequestContext<rmcp::service::RoleServer>,
) -> Result<CallToolResult, McpError> {
match request.name.as_ref() {
@ -357,7 +358,7 @@ impl ServerHandler for TestToolServer {
impl TestToolServer {
fn parse_call_args<T: for<'de> Deserialize<'de>>(
request: &CallToolRequestParam,
request: &CallToolRequestParams,
tool_name: &'static str,
) -> Result<T, McpError> {
match request.arguments.as_ref() {

View file

@ -17,16 +17,16 @@ use axum::response::Response;
use axum::routing::get;
use rmcp::ErrorData as McpError;
use rmcp::handler::server::ServerHandler;
use rmcp::model::CallToolRequestParam;
use rmcp::model::CallToolRequestParams;
use rmcp::model::CallToolResult;
use rmcp::model::JsonObject;
use rmcp::model::ListResourceTemplatesResult;
use rmcp::model::ListResourcesResult;
use rmcp::model::ListToolsResult;
use rmcp::model::PaginatedRequestParam;
use rmcp::model::PaginatedRequestParams;
use rmcp::model::RawResource;
use rmcp::model::RawResourceTemplate;
use rmcp::model::ReadResourceRequestParam;
use rmcp::model::ReadResourceRequestParams;
use rmcp::model::ReadResourceResult;
use rmcp::model::Resource;
use rmcp::model::ResourceContents;
@ -106,6 +106,7 @@ impl TestToolServer {
"Template for memo://codex/{slug} resources used in tests.".to_string(),
),
mime_type: Some("text/plain".to_string()),
icons: None,
};
ResourceTemplate::new(raw, None)
}
@ -136,7 +137,7 @@ impl ServerHandler for TestToolServer {
fn list_tools(
&self,
_request: Option<PaginatedRequestParam>,
_request: Option<PaginatedRequestParams>,
_context: rmcp::service::RequestContext<rmcp::service::RoleServer>,
) -> impl std::future::Future<Output = Result<ListToolsResult, McpError>> + Send + '_ {
let tools = self.tools.clone();
@ -151,7 +152,7 @@ impl ServerHandler for TestToolServer {
fn list_resources(
&self,
_request: Option<PaginatedRequestParam>,
_request: Option<PaginatedRequestParams>,
_context: rmcp::service::RequestContext<rmcp::service::RoleServer>,
) -> impl std::future::Future<Output = Result<ListResourcesResult, McpError>> + Send + '_ {
let resources = self.resources.clone();
@ -166,7 +167,7 @@ impl ServerHandler for TestToolServer {
async fn list_resource_templates(
&self,
_request: Option<PaginatedRequestParam>,
_request: Option<PaginatedRequestParams>,
_context: rmcp::service::RequestContext<rmcp::service::RoleServer>,
) -> Result<ListResourceTemplatesResult, McpError> {
Ok(ListResourceTemplatesResult {
@ -178,7 +179,7 @@ impl ServerHandler for TestToolServer {
async fn read_resource(
&self,
ReadResourceRequestParam { uri }: ReadResourceRequestParam,
ReadResourceRequestParams { uri, .. }: ReadResourceRequestParams,
_context: rmcp::service::RequestContext<rmcp::service::RoleServer>,
) -> Result<ReadResourceResult, McpError> {
if uri == MEMO_URI {
@ -200,7 +201,7 @@ impl ServerHandler for TestToolServer {
async fn call_tool(
&self,
request: CallToolRequestParam,
request: CallToolRequestParams,
_context: rmcp::service::RequestContext<rmcp::service::RoleServer>,
) -> Result<CallToolResult, McpError> {
match request.name.as_ref() {

View file

@ -4,7 +4,7 @@ use rmcp::ClientHandler;
use rmcp::RoleClient;
use rmcp::model::CancelledNotificationParam;
use rmcp::model::ClientInfo;
use rmcp::model::CreateElicitationRequestParam;
use rmcp::model::CreateElicitationRequestParams;
use rmcp::model::CreateElicitationResult;
use rmcp::model::LoggingLevel;
use rmcp::model::LoggingMessageNotificationParam;
@ -37,7 +37,7 @@ impl LoggingClientHandler {
impl ClientHandler for LoggingClientHandler {
async fn create_elicitation(
&self,
request: CreateElicitationRequestParam,
request: CreateElicitationRequestParams,
context: RequestContext<RoleClient>,
) -> Result<CreateElicitationResult, rmcp::ErrorData> {
(self.send_elicitation)(context.id, request)

View file

@ -10,23 +10,24 @@ use anyhow::Result;
use anyhow::anyhow;
use futures::FutureExt;
use futures::future::BoxFuture;
use oauth2::TokenResponse;
use reqwest::header::HeaderMap;
use rmcp::model::CallToolRequestParam;
use rmcp::model::CallToolRequestParams;
use rmcp::model::CallToolResult;
use rmcp::model::ClientNotification;
use rmcp::model::ClientRequest;
use rmcp::model::CreateElicitationRequestParam;
use rmcp::model::CreateElicitationRequestParams;
use rmcp::model::CreateElicitationResult;
use rmcp::model::CustomNotification;
use rmcp::model::CustomRequest;
use rmcp::model::Extensions;
use rmcp::model::InitializeRequestParam;
use rmcp::model::InitializeRequestParams;
use rmcp::model::InitializeResult;
use rmcp::model::ListResourceTemplatesResult;
use rmcp::model::ListResourcesResult;
use rmcp::model::ListToolsResult;
use rmcp::model::PaginatedRequestParam;
use rmcp::model::ReadResourceRequestParam;
use rmcp::model::PaginatedRequestParams;
use rmcp::model::ReadResourceRequestParams;
use rmcp::model::ReadResourceResult;
use rmcp::model::RequestId;
use rmcp::model::ServerResult;
@ -36,6 +37,7 @@ use rmcp::service::RunningService;
use rmcp::service::{self};
use rmcp::transport::StreamableHttpClientTransport;
use rmcp::transport::auth::AuthClient;
use rmcp::transport::auth::AuthError;
use rmcp::transport::auth::OAuthState;
use rmcp::transport::child_process::TokioChildProcess;
use rmcp::transport::streamable_http_client::StreamableHttpClientTransportConfig;
@ -143,7 +145,7 @@ impl Drop for ProcessGroupGuard {
}
}
pub type Elicitation = CreateElicitationRequestParam;
pub type Elicitation = CreateElicitationRequestParams;
pub type ElicitationResponse = CreateElicitationResult;
/// Interface for sending elicitation requests to the UI and awaiting a response.
@ -254,17 +256,44 @@ impl RmcpClient {
};
let transport = if let Some(initial_tokens) = initial_oauth_tokens.clone() {
let (transport, oauth_persistor) = create_oauth_transport_and_runtime(
match create_oauth_transport_and_runtime(
server_name,
url,
initial_tokens,
initial_tokens.clone(),
store_mode,
default_headers.clone(),
)
.await?;
PendingTransport::StreamableHttpWithOAuth {
transport,
oauth_persistor,
.await
{
Ok((transport, oauth_persistor)) => PendingTransport::StreamableHttpWithOAuth {
transport,
oauth_persistor,
},
Err(err)
if err.downcast_ref::<AuthError>().is_some_and(|auth_err| {
matches!(auth_err, AuthError::NoAuthorizationSupport)
}) =>
{
let access_token = initial_tokens
.token_response
.0
.access_token()
.secret()
.to_string();
warn!(
"OAuth metadata discovery is unavailable for MCP server `{server_name}`; falling back to stored bearer token authentication"
);
let http_config =
StreamableHttpClientTransportConfig::with_uri(url.to_string())
.auth_header(access_token);
let http_client =
apply_default_headers(reqwest::Client::builder(), &default_headers)
.build()?;
let transport =
StreamableHttpClientTransport::with_client(http_client, http_config);
PendingTransport::StreamableHttp { transport }
}
Err(err) => return Err(err),
}
} else {
let mut http_config = StreamableHttpClientTransportConfig::with_uri(url.to_string());
@ -289,7 +318,7 @@ impl RmcpClient {
/// https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle#initialization
pub async fn initialize(
&self,
params: InitializeRequestParam,
params: InitializeRequestParams,
timeout: Option<Duration>,
send_elicitation: SendElicitation,
) -> Result<InitializeResult> {
@ -362,7 +391,7 @@ impl RmcpClient {
pub async fn list_tools(
&self,
params: Option<PaginatedRequestParam>,
params: Option<PaginatedRequestParams>,
timeout: Option<Duration>,
) -> Result<ListToolsResult> {
self.refresh_oauth_if_needed().await;
@ -375,7 +404,7 @@ impl RmcpClient {
pub async fn list_tools_with_connector_ids(
&self,
params: Option<PaginatedRequestParam>,
params: Option<PaginatedRequestParams>,
timeout: Option<Duration>,
) -> Result<ListToolsWithConnectorIdResult> {
self.refresh_oauth_if_needed().await;
@ -415,7 +444,7 @@ impl RmcpClient {
pub async fn list_resources(
&self,
params: Option<PaginatedRequestParam>,
params: Option<PaginatedRequestParams>,
timeout: Option<Duration>,
) -> Result<ListResourcesResult> {
self.refresh_oauth_if_needed().await;
@ -429,7 +458,7 @@ impl RmcpClient {
pub async fn list_resource_templates(
&self,
params: Option<PaginatedRequestParam>,
params: Option<PaginatedRequestParams>,
timeout: Option<Duration>,
) -> Result<ListResourceTemplatesResult> {
self.refresh_oauth_if_needed().await;
@ -443,7 +472,7 @@ impl RmcpClient {
pub async fn read_resource(
&self,
params: ReadResourceRequestParam,
params: ReadResourceRequestParams,
timeout: Option<Duration>,
) -> Result<ReadResourceResult> {
self.refresh_oauth_if_needed().await;
@ -471,9 +500,11 @@ impl RmcpClient {
}
None => None,
};
let rmcp_params = CallToolRequestParam {
let rmcp_params = CallToolRequestParams {
meta: None,
name: name.into(),
arguments,
task: None,
};
let fut = service.call_tool(rmcp_params);
let result = run_with_timeout(fut, timeout, "tools/call").await?;

View file

@ -11,10 +11,10 @@ use rmcp::model::AnnotateAble;
use rmcp::model::ClientCapabilities;
use rmcp::model::ElicitationCapability;
use rmcp::model::Implementation;
use rmcp::model::InitializeRequestParam;
use rmcp::model::InitializeRequestParams;
use rmcp::model::ListResourceTemplatesResult;
use rmcp::model::ProtocolVersion;
use rmcp::model::ReadResourceRequestParam;
use rmcp::model::ReadResourceRequestParams;
use rmcp::model::ResourceContents;
use serde_json::json;
@ -24,8 +24,9 @@ fn stdio_server_bin() -> Result<PathBuf, CargoBinError> {
codex_utils_cargo_bin::cargo_bin("test_stdio_server")
}
fn init_params() -> InitializeRequestParam {
InitializeRequestParam {
fn init_params() -> InitializeRequestParams {
InitializeRequestParams {
meta: None,
capabilities: ClientCapabilities {
experimental: None,
roots: None,
@ -33,6 +34,7 @@ fn init_params() -> InitializeRequestParam {
elicitation: Some(ElicitationCapability {
schema_validation: None,
}),
tasks: None,
},
client_info: Implementation {
name: "codex-test".into(),
@ -111,6 +113,7 @@ async fn rmcp_client_can_list_and_read_resources() -> anyhow::Result<()> {
"Template for memo://codex/{slug} resources used in tests.".to_string(),
),
mime_type: Some("text/plain".to_string()),
icons: None,
}
.no_annotation()
],
@ -119,7 +122,8 @@ async fn rmcp_client_can_list_and_read_resources() -> anyhow::Result<()> {
let read = client
.read_resource(
ReadResourceRequestParam {
ReadResourceRequestParams {
meta: None,
uri: RESOURCE_URI.to_string(),
},
Some(Duration::from_secs(5)),