Codex Handbook
rmcp-client/tests/resources.rs 138 lines
use std::ffi::OsString;use std::path::PathBuf;use std::sync::Arc;use std::time::Duration;use codex_rmcp_client::ElicitationAction;use codex_rmcp_client::ElicitationResponse;use codex_rmcp_client::LocalStdioServerLauncher;use codex_rmcp_client::RmcpClient;use codex_utils_cargo_bin::CargoBinError;use futures::FutureExt as _;use rmcp::model::AnnotateAble;use rmcp::model::ClientCapabilities;use rmcp::model::ElicitationCapability;use rmcp::model::FormElicitationCapability;use rmcp::model::Implementation;use rmcp::model::InitializeRequestParams;use rmcp::model::ListResourceTemplatesResult;use rmcp::model::ProtocolVersion;use rmcp::model::ReadResourceRequestParams;use rmcp::model::ResourceContents;use serde_json::json;const RESOURCE_URI: &str = "memo://codex/example-note";fn stdio_server_bin() -> Result<PathBuf, CargoBinError> {    codex_utils_cargo_bin::cargo_bin("test_stdio_server")}fn init_params() -> InitializeRequestParams {    let mut capabilities = ClientCapabilities::default();    capabilities.elicitation = Some(ElicitationCapability {        form: Some(FormElicitationCapability {            schema_validation: None,        }),        url: None,    });    InitializeRequestParams::new(        capabilities,        Implementation::new("codex-test", "0.0.0-test").with_title("Codex rmcp resource test"),    )    .with_protocol_version(ProtocolVersion::V_2025_06_18)}#[tokio::test(flavor = "multi_thread", worker_threads = 1)]async fn rmcp_client_can_list_and_read_resources() -> anyhow::Result<()> {    let client = RmcpClient::new_stdio_client(        stdio_server_bin()?.into(),        Vec::<OsString>::new(),        /*env*/ None,        &[],        /*cwd*/ None,        Arc::new(LocalStdioServerLauncher::new(std::env::current_dir()?)),    )    .await?;    client        .initialize(            init_params(),            Some(Duration::from_secs(5)),            Box::new(|_, _| {                async {                    Ok(ElicitationResponse {                        action: ElicitationAction::Accept,                        content: Some(json!({})),                        meta: None,                    })                }                .boxed()            }),        )        .await?;    let list = client        .list_resources(/*params*/ None, Some(Duration::from_secs(5)))        .await?;    let memo = list        .resources        .iter()        .find(|resource| resource.uri == RESOURCE_URI)        .expect("memo resource present");    assert_eq!(        memo,        &rmcp::model::RawResource {            uri: RESOURCE_URI.to_string(),            name: "example-note".to_string(),            title: Some("Example Note".to_string()),            description: Some("A sample MCP resource exposed for integration tests.".to_string()),            mime_type: Some("text/plain".to_string()),            size: None,            icons: None,            meta: None,        }        .no_annotation()    );    let templates = client        .list_resource_templates(/*params*/ None, Some(Duration::from_secs(5)))        .await?;    assert_eq!(        templates,        ListResourceTemplatesResult {            meta: None,            next_cursor: None,            resource_templates: vec![                rmcp::model::RawResourceTemplate {                    uri_template: "memo://codex/{slug}".to_string(),                    name: "codex-memo".to_string(),                    title: Some("Codex Memo".to_string()),                    description: Some(                        "Template for memo://codex/{slug} resources used in tests.".to_string(),                    ),                    mime_type: Some("text/plain".to_string()),                    icons: None,                }                .no_annotation()            ],        }    );    let read = client        .read_resource(            ReadResourceRequestParams::new(RESOURCE_URI),            Some(Duration::from_secs(5)),        )        .await?;    let text = read.contents.first().expect("resource contents present");    assert_eq!(        text,        &ResourceContents::TextResourceContents {            uri: RESOURCE_URI.to_string(),            mime_type: Some("text/plain".to_string()),            text: "This is a sample MCP resource served by the rmcp test server.".to_string(),            meta: None,        }    );    Ok(())}