tooltest_core/validation/
listing.rs

1use std::fmt;
2use std::future::Future;
3
4use rmcp::model::Tool;
5
6use crate::schema::parse_list_tools;
7use crate::{HttpConfig, SchemaConfig, SchemaError, SessionDriver, SessionError, StdioConfig};
8
9/// Errors emitted while listing tools.
10#[derive(Debug)]
11pub enum ListToolsError {
12    /// Failed to communicate with the MCP endpoint.
13    Session(SessionError),
14    /// MCP payload failed schema validation.
15    Schema(SchemaError),
16}
17
18impl fmt::Display for ListToolsError {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        match self {
21            ListToolsError::Session(error) => write!(f, "session error: {error:?}"),
22            ListToolsError::Schema(error) => write!(f, "schema error: {error}"),
23        }
24    }
25}
26
27impl std::error::Error for ListToolsError {}
28
29impl From<SessionError> for ListToolsError {
30    fn from(error: SessionError) -> Self {
31        ListToolsError::Session(error)
32    }
33}
34
35impl From<SchemaError> for ListToolsError {
36    fn from(error: SchemaError) -> Self {
37        ListToolsError::Schema(error)
38    }
39}
40
41/// Lists tools from an HTTP MCP endpoint using the provided configuration.
42pub async fn list_tools_http(
43    config: &HttpConfig,
44    schema: &SchemaConfig,
45) -> Result<Vec<Tool>, ListToolsError> {
46    list_tools_with_connector(config.clone(), schema, |config| async move {
47        SessionDriver::connect_http(&config).await
48    })
49    .await
50}
51
52/// Lists tools from a stdio MCP endpoint using the provided configuration.
53pub async fn list_tools_stdio(
54    config: &StdioConfig,
55    schema: &SchemaConfig,
56) -> Result<Vec<Tool>, ListToolsError> {
57    list_tools_with_connector(config.clone(), schema, |config| async move {
58        SessionDriver::connect_stdio(&config).await
59    })
60    .await
61}
62
63/// Lists tools from an active session using MCP schema validation.
64pub async fn list_tools_with_session(
65    session: &SessionDriver,
66    schema: &SchemaConfig,
67) -> Result<Vec<Tool>, ListToolsError> {
68    let tools = session.list_tools().await?;
69    let payload = serde_json::to_value(&rmcp::model::ListToolsResult {
70        tools,
71        next_cursor: None,
72        meta: None,
73    })
74    .expect("list tools serialize");
75    let parsed = parse_list_tools(payload, schema)?;
76    Ok(parsed.tools)
77}
78
79pub(crate) async fn list_tools_with_connector<T, F, Fut>(
80    config: T,
81    schema: &SchemaConfig,
82    connector: F,
83) -> Result<Vec<Tool>, ListToolsError>
84where
85    F: FnOnce(T) -> Fut,
86    Fut: Future<Output = Result<SessionDriver, SessionError>>,
87{
88    let session = connector(config).await?;
89    list_tools_with_session(&session, schema).await
90}