parent
ef5d26e586
commit
9f1009540b
18 changed files with 193 additions and 100 deletions
13
codex-rs/Cargo.lock
generated
13
codex-rs/Cargo.lock
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 + '_
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 + '_
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// ---------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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?;
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue