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 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 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 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}