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::ContentBlock;
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}