use crate::browser::{BrowserSession, ConnectionOptions};
use log::debug;
use rmcp::{
ServerHandler,
handler::server::router::tool::ToolRouter,
model::{ServerCapabilities, ServerInfo},
tool_handler,
};
use std::sync::Arc;
#[derive(Clone)]
pub struct BrowserServer {
session: Arc<BrowserSession>,
tool_router: ToolRouter<Self>,
}
impl BrowserServer {
fn from_session(session: BrowserSession) -> Self {
Self {
session: Arc::new(session),
tool_router: Self::tool_router(),
}
}
pub fn new() -> Result<Self, String> {
let session =
BrowserSession::new().map_err(|e| format!("Failed to launch browser: {}", e))?;
Ok(Self::from_session(session))
}
pub fn with_options(options: crate::browser::LaunchOptions) -> Result<Self, String> {
let session = BrowserSession::launch(options)
.map_err(|e| format!("Failed to launch browser: {}", e))?;
Ok(Self::from_session(session))
}
pub fn connect(options: ConnectionOptions) -> Result<Self, String> {
let session = BrowserSession::connect(options)
.map_err(|e| format!("Failed to connect browser session: {}", e))?;
Ok(Self::from_session(session))
}
pub(crate) fn session(&self) -> &BrowserSession {
self.session.as_ref()
}
}
impl Default for BrowserServer {
fn default() -> Self {
Self::new().expect("Failed to create default browser server")
}
}
impl Drop for BrowserServer {
fn drop(&mut self) {
debug!("BrowserServer dropped");
}
}
#[tool_handler(router = self.tool_router)]
impl ServerHandler for BrowserServer {
fn get_info(&self) -> ServerInfo {
server_info()
}
}
fn server_info() -> ServerInfo {
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
.with_instructions("Browser-use MCP Server")
}
#[cfg(test)]
mod tests {
use super::server_info;
#[test]
fn test_server_info_enables_tools_and_instructions() {
let info = server_info();
assert!(
info.instructions
.as_deref()
.unwrap_or_default()
.contains("Browser-use MCP Server")
);
assert!(info.capabilities.tools.is_some());
}
}