[MCP] Introduce an experimental official rust sdk based mcp client (#4252)

The [official Rust
SDK](57fc428c57)
has come a long way since we first started our mcp client implementation
5 months ago and, today, it is much more complete than our own
stdio-only implementation.

This PR introduces a new config flag `experimental_use_rmcp_client`
which will use a new mcp client powered by the sdk instead of our own.

To keep this PR simple, I've only implemented the same stdio MCP
functionality that we had but will expand on it with future PRs.

---------

Co-authored-by: pakrym-oai <pakrym@openai.com>
This commit is contained in:
Gabriel Peal
2025-09-26 10:13:37 -07:00
committed by GitHub
parent ea095e30c1
commit e555a36c6a
17 changed files with 1136 additions and 62 deletions

View File

@@ -0,0 +1,134 @@
use rmcp::ClientHandler;
use rmcp::RoleClient;
use rmcp::model::CancelledNotificationParam;
use rmcp::model::ClientInfo;
use rmcp::model::CreateElicitationRequestParam;
use rmcp::model::CreateElicitationResult;
use rmcp::model::ElicitationAction;
use rmcp::model::LoggingLevel;
use rmcp::model::LoggingMessageNotificationParam;
use rmcp::model::ProgressNotificationParam;
use rmcp::model::ResourceUpdatedNotificationParam;
use rmcp::service::NotificationContext;
use rmcp::service::RequestContext;
use tracing::debug;
use tracing::error;
use tracing::info;
use tracing::warn;
#[derive(Debug, Clone)]
pub(crate) struct LoggingClientHandler {
client_info: ClientInfo,
}
impl LoggingClientHandler {
pub(crate) fn new(client_info: ClientInfo) -> Self {
Self { client_info }
}
}
impl ClientHandler for LoggingClientHandler {
// TODO (CODEX-3571): support elicitations.
async fn create_elicitation(
&self,
request: CreateElicitationRequestParam,
_context: RequestContext<RoleClient>,
) -> Result<CreateElicitationResult, rmcp::ErrorData> {
info!(
"MCP server requested elicitation ({}). Elicitations are not supported yet. Declining.",
request.message
);
Ok(CreateElicitationResult {
action: ElicitationAction::Decline,
content: None,
})
}
async fn on_cancelled(
&self,
params: CancelledNotificationParam,
_context: NotificationContext<RoleClient>,
) {
info!(
"MCP server cancelled request (request_id: {}, reason: {:?})",
params.request_id, params.reason
);
}
async fn on_progress(
&self,
params: ProgressNotificationParam,
_context: NotificationContext<RoleClient>,
) {
info!(
"MCP server progress notification (token: {:?}, progress: {}, total: {:?}, message: {:?})",
params.progress_token, params.progress, params.total, params.message
);
}
async fn on_resource_updated(
&self,
params: ResourceUpdatedNotificationParam,
_context: NotificationContext<RoleClient>,
) {
info!("MCP server resource updated (uri: {})", params.uri);
}
async fn on_resource_list_changed(&self, _context: NotificationContext<RoleClient>) {
info!("MCP server resource list changed");
}
async fn on_tool_list_changed(&self, _context: NotificationContext<RoleClient>) {
info!("MCP server tool list changed");
}
async fn on_prompt_list_changed(&self, _context: NotificationContext<RoleClient>) {
info!("MCP server prompt list changed");
}
fn get_info(&self) -> ClientInfo {
self.client_info.clone()
}
async fn on_logging_message(
&self,
params: LoggingMessageNotificationParam,
_context: NotificationContext<RoleClient>,
) {
let LoggingMessageNotificationParam {
level,
logger,
data,
} = params;
let logger = logger.as_deref();
match level {
LoggingLevel::Emergency
| LoggingLevel::Alert
| LoggingLevel::Critical
| LoggingLevel::Error => {
error!(
"MCP server log message (level: {:?}, logger: {:?}, data: {})",
level, logger, data
);
}
LoggingLevel::Warning => {
warn!(
"MCP server log message (level: {:?}, logger: {:?}, data: {})",
level, logger, data
);
}
LoggingLevel::Notice | LoggingLevel::Info => {
info!(
"MCP server log message (level: {:?}, logger: {:?}, data: {})",
level, logger, data
);
}
LoggingLevel::Debug => {
debug!(
"MCP server log message (level: {:?}, logger: {:?}, data: {})",
level, logger, data
);
}
}
}
}