Skip to main content

turbomcp_client/client/operations/
tools.rs

1//! Tool operations for MCP client
2//!
3//! This module provides tool-related functionality including listing tools,
4//! calling tools, and processing tool results.
5
6use std::collections::HashMap;
7use std::sync::atomic::Ordering;
8
9use turbomcp_protocol::types::{CallToolRequest, CallToolResult, ListToolsResult, Tool};
10use turbomcp_protocol::{Error, Result};
11
12impl<T: turbomcp_transport::Transport + 'static> super::super::core::Client<T> {
13    /// List all available tools from the MCP server
14    ///
15    /// Returns complete tool definitions with schemas that can be used
16    /// for form generation, validation, and documentation. Tools represent
17    /// executable functions provided by the server.
18    ///
19    /// # Returns
20    ///
21    /// Returns a vector of Tool objects with complete metadata including names,
22    /// descriptions, and input schemas. These schemas can be used to generate
23    /// user interfaces for tool invocation.
24    ///
25    /// # Examples
26    ///
27    /// ```rust,no_run
28    /// # use turbomcp_client::Client;
29    /// # use turbomcp_transport::stdio::StdioTransport;
30    /// # async fn example() -> turbomcp_protocol::Result<()> {
31    /// let mut client = Client::new(StdioTransport::new());
32    /// client.initialize().await?;
33    ///
34    /// let tools = client.list_tools().await?;
35    /// for tool in tools {
36    ///     println!("Tool: {} - {}", tool.name, tool.description.as_deref().unwrap_or("No description"));
37    /// }
38    /// # Ok(())
39    /// # }
40    /// ```
41    pub async fn list_tools(&self) -> Result<Vec<Tool>> {
42        if !self.inner.initialized.load(Ordering::Relaxed) {
43            return Err(Error::invalid_request("Client not initialized"));
44        }
45
46        // Send tools/list request
47        let response: ListToolsResult = self.inner.protocol.request("tools/list", None).await?;
48        Ok(response.tools) // Return full Tool objects with schemas
49    }
50
51    /// List available tool names from the MCP server
52    ///
53    /// Returns only the tool names for cases where full schemas are not needed.
54    /// For most use cases, prefer `list_tools()` which provides complete tool definitions.
55    ///
56    /// # Returns
57    ///
58    /// Returns a vector of tool names available on the server.
59    ///
60    /// # Examples
61    ///
62    /// ```rust,no_run
63    /// # use turbomcp_client::Client;
64    /// # use turbomcp_transport::stdio::StdioTransport;
65    /// # async fn example() -> turbomcp_protocol::Result<()> {
66    /// let mut client = Client::new(StdioTransport::new());
67    /// client.initialize().await?;
68    ///
69    /// let tool_names = client.list_tool_names().await?;
70    /// for name in tool_names {
71    ///     println!("Available tool: {}", name);
72    /// }
73    /// # Ok(())
74    /// # }
75    /// ```
76    pub async fn list_tool_names(&self) -> Result<Vec<String>> {
77        let tools = self.list_tools().await?;
78        Ok(tools.into_iter().map(|tool| tool.name).collect())
79    }
80
81    /// Call a tool on the server
82    ///
83    /// Executes a tool on the server with the provided arguments and returns
84    /// the complete MCP `CallToolResult`.
85    ///
86    /// # Arguments
87    ///
88    /// * `name` - The name of the tool to call
89    /// * `arguments` - Optional arguments to pass to the tool
90    /// * `task` - Optional task metadata for task-augmented requests (MCP 2025-11-25 draft)
91    ///
92    /// # Returns
93    ///
94    /// Returns the complete `CallToolResult` with:
95    /// - `content: Vec<ContentBlock>` - All content blocks (text, image, resource, audio, etc.)
96    /// - `is_error: Option<bool>` - Whether the tool execution resulted in an error
97    /// - `structured_content: Option<serde_json::Value>` - Schema-validated structured output
98    /// - `_meta: Option<serde_json::Value>` - Metadata for client applications (not exposed to LLMs)
99    /// - `task_id: Option<String>` - Task identifier if task-augmented
100    ///
101    /// # Examples
102    ///
103    /// ## Basic Usage
104    ///
105    /// ```rust,no_run
106    /// # use turbomcp_client::Client;
107    /// # use turbomcp_transport::stdio::StdioTransport;
108    /// # use turbomcp_protocol::types::Content;
109    /// # use std::collections::HashMap;
110    /// # async fn example() -> turbomcp_protocol::Result<()> {
111    /// let mut client = Client::new(StdioTransport::new());
112    /// client.initialize().await?;
113    ///
114    /// let mut args = HashMap::new();
115    /// args.insert("input".to_string(), serde_json::json!("test"));
116    ///
117    /// let result = client.call_tool("my_tool", Some(args), None).await?;
118    /// # Ok(())
119    /// # }
120    /// ```
121    pub async fn call_tool(
122        &self,
123        name: &str,
124        arguments: Option<HashMap<String, serde_json::Value>>,
125        task: Option<turbomcp_protocol::types::tasks::TaskMetadata>,
126    ) -> Result<CallToolResult> {
127        if !self.inner.initialized.load(Ordering::Relaxed) {
128            return Err(Error::invalid_request("Client not initialized"));
129        }
130
131        let request_data = CallToolRequest {
132            name: name.to_string(),
133            arguments: Some(arguments.unwrap_or_default()),
134            task,
135            _meta: None,
136        };
137
138        // Core protocol call
139        let result: CallToolResult = self
140            .inner
141            .protocol
142            .request("tools/call", Some(serde_json::to_value(&request_data)?))
143            .await?;
144
145        Ok(result) // Return full CallToolResult - MCP spec compliant!
146    }
147}