rmcp_usb/
lib.rs

1use nusb::list_devices;
2use rmcp::{
3    handler::server::{router::tool::ToolRouter, ServerHandler},
4    model::*,
5    ErrorData as McpError,
6};
7
8#[derive(Debug)]
9pub struct UsbServer {
10    pub tool_router: ToolRouter<Self>,
11}
12
13impl Default for UsbServer {
14    fn default() -> Self {
15        Self::new()
16    }
17}
18
19impl UsbServer {
20    pub fn new() -> Self {
21        Self {
22            tool_router: Self::tool_router(),
23        }
24    }
25}
26
27#[rmcp::tool_router]
28impl UsbServer {
29    #[rmcp::tool(description = "List all connected USB devices with vendor/product info")]
30    pub async fn get_usb_devices(&self) -> Result<CallToolResult, McpError> {
31        let devices = list_devices()
32            .map_err(|e| McpError::internal_error(format!("Failed to list USB devices: {}", e), None))?;
33
34        let mut result = String::from("USB Devices:\n\n");
35        let mut count = 0;
36
37        for device in devices {
38            count += 1;
39
40            // Get manufacturer and product strings if available
41            let manufacturer = device.manufacturer_string().unwrap_or_default();
42            let product = device.product_string().unwrap_or_default();
43            let serial = device.serial_number().unwrap_or_default();
44
45            // Display name: prefer product name, fall back to vendor:product IDs
46            let display_name = if !product.is_empty() {
47                product.to_string()
48            } else {
49                format!("Device {:04x}:{:04x}", device.vendor_id(), device.product_id())
50            };
51
52            result.push_str(&format!("{}. {}\n", count, display_name));
53
54            if !manufacturer.is_empty() {
55                result.push_str(&format!("   Manufacturer: {}\n", manufacturer));
56            }
57
58            result.push_str(&format!("   Vendor ID: {:04x}, Product ID: {:04x}\n",
59                device.vendor_id(), device.product_id()));
60
61            if !serial.is_empty() {
62                result.push_str(&format!("   Serial: {}\n", serial));
63            }
64
65            // Bus and device info
66            result.push_str(&format!("   Bus: {}, Device: {}\n",
67                device.bus_number(), device.device_address()));
68
69            result.push('\n');
70        }
71
72        if count == 0 {
73            result.push_str("No USB devices found.\n");
74        } else {
75            result.push_str(&format!("Total: {} USB devices\n", count));
76        }
77
78        Ok(CallToolResult::success(vec![Content::text(result)]))
79    }
80}
81
82#[rmcp::tool_handler]
83impl ServerHandler for UsbServer {
84    fn get_info(&self) -> ServerInfo {
85        ServerInfo {
86            protocol_version: ProtocolVersion::V_2024_11_05,
87            capabilities: ServerCapabilities::builder()
88                .enable_tools()
89                .build(),
90            server_info: Implementation::from_build_env(),
91            instructions: Some("Cross-platform USB device information server".into()),
92        }
93    }
94}