wacht 0.1.0-beta.5

Official Rust SDK for the Wacht platform, providing type-safe API client and authentication middleware
Documentation
use crate::{
    client::WachtClient,
    error::{Error, Result},
    models::{
        CreateMcpServerRequest, McpServer, McpServerCreateResponse, McpServerDiscoveryResponse,
        PaginatedResponse, UpdateMcpServerRequest,
    },
};
use serde::Serialize;

#[derive(Debug, Clone, Default, Serialize)]
pub struct ListMcpServersOptions {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub limit: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub offset: Option<i32>,
}

#[derive(Debug, Clone)]
pub struct McpServersApi {
    client: WachtClient,
}
impl McpServersApi {
    pub(crate) fn new(client: WachtClient) -> Self {
        Self { client }
    }
    pub fn fetch_mcp_servers(&self) -> FetchMcpServersBuilder {
        FetchMcpServersBuilder::new(self.client.clone())
    }
    pub fn fetch_mcp_server(&self, mcp_server_id: impl Into<String>) -> FetchMcpServerBuilder {
        FetchMcpServerBuilder::new(self.client.clone(), mcp_server_id)
    }
    pub fn create_mcp_server(&self, request: CreateMcpServerRequest) -> CreateMcpServerBuilder {
        CreateMcpServerBuilder::new(self.client.clone(), request)
    }
    pub fn update_mcp_server(
        &self,
        mcp_server_id: impl Into<String>,
        request: UpdateMcpServerRequest,
    ) -> UpdateMcpServerBuilder {
        UpdateMcpServerBuilder::new(self.client.clone(), mcp_server_id, request)
    }
    pub fn delete_mcp_server(&self, mcp_server_id: impl Into<String>) -> DeleteMcpServerBuilder {
        DeleteMcpServerBuilder::new(self.client.clone(), mcp_server_id)
    }
    pub fn discover_mcp_server(&self, endpoint: impl Into<String>) -> DiscoverMcpServerBuilder {
        DiscoverMcpServerBuilder::new(self.client.clone(), endpoint)
    }
}

fn api_error(status: reqwest::StatusCode, prefix: &str, body: String) -> Error {
    Error::api_from_text(status, prefix, &body)
}

pub struct FetchMcpServersBuilder {
    client: WachtClient,
    options: ListMcpServersOptions,
}
impl FetchMcpServersBuilder {
    pub fn new(client: WachtClient) -> Self {
        Self {
            client,
            options: ListMcpServersOptions::default(),
        }
    }
    pub fn limit(mut self, limit: i32) -> Self {
        self.options.limit = Some(limit);
        self
    }
    pub fn offset(mut self, offset: i32) -> Self {
        self.options.offset = Some(offset);
        self
    }
    pub async fn send(self) -> Result<PaginatedResponse<McpServer>> {
        let response = self
            .client
            .http_client()
            .get(format!("{}/ai/mcp-servers", self.client.config().base_url))
            .query(&self.options)
            .send()
            .await?;
        let status = response.status();
        if status.is_success() {
            Ok(response.json().await?)
        } else {
            Err(api_error(
                status,
                "Failed to list MCP servers",
                response.text().await?,
            ))
        }
    }
}

pub struct FetchMcpServerBuilder {
    client: WachtClient,
    mcp_server_id: String,
}
impl FetchMcpServerBuilder {
    pub fn new(client: WachtClient, mcp_server_id: impl Into<String>) -> Self {
        Self {
            client,
            mcp_server_id: mcp_server_id.into(),
        }
    }
    pub async fn send(self) -> Result<McpServer> {
        let response = self
            .client
            .http_client()
            .get(format!(
                "{}/ai/mcp-servers/{}",
                self.client.config().base_url,
                self.mcp_server_id
            ))
            .send()
            .await?;
        let status = response.status();
        if status.is_success() {
            Ok(response.json().await?)
        } else {
            Err(api_error(
                status,
                "Failed to fetch MCP server",
                response.text().await?,
            ))
        }
    }
}

pub struct CreateMcpServerBuilder {
    client: WachtClient,
    request: CreateMcpServerRequest,
}
impl CreateMcpServerBuilder {
    pub fn new(client: WachtClient, request: CreateMcpServerRequest) -> Self {
        Self { client, request }
    }
    pub async fn send(self) -> Result<McpServerCreateResponse> {
        let response = self
            .client
            .http_client()
            .post(format!("{}/ai/mcp-servers", self.client.config().base_url))
            .json(&self.request)
            .send()
            .await?;
        let status = response.status();
        if status.is_success() {
            Ok(response.json().await?)
        } else {
            Err(api_error(
                status,
                "Failed to create MCP server",
                response.text().await?,
            ))
        }
    }
}

pub struct UpdateMcpServerBuilder {
    client: WachtClient,
    mcp_server_id: String,
    request: UpdateMcpServerRequest,
}
impl UpdateMcpServerBuilder {
    pub fn new(
        client: WachtClient,
        mcp_server_id: impl Into<String>,
        request: UpdateMcpServerRequest,
    ) -> Self {
        Self {
            client,
            mcp_server_id: mcp_server_id.into(),
            request,
        }
    }
    pub async fn send(self) -> Result<McpServer> {
        let response = self
            .client
            .http_client()
            .patch(format!(
                "{}/ai/mcp-servers/{}",
                self.client.config().base_url,
                self.mcp_server_id
            ))
            .json(&self.request)
            .send()
            .await?;
        let status = response.status();
        if status.is_success() {
            Ok(response.json().await?)
        } else {
            Err(api_error(
                status,
                "Failed to update MCP server",
                response.text().await?,
            ))
        }
    }
}

pub struct DeleteMcpServerBuilder {
    client: WachtClient,
    mcp_server_id: String,
}
impl DeleteMcpServerBuilder {
    pub fn new(client: WachtClient, mcp_server_id: impl Into<String>) -> Self {
        Self {
            client,
            mcp_server_id: mcp_server_id.into(),
        }
    }
    pub async fn send(self) -> Result<()> {
        let response = self
            .client
            .http_client()
            .delete(format!(
                "{}/ai/mcp-servers/{}",
                self.client.config().base_url,
                self.mcp_server_id
            ))
            .send()
            .await?;
        let status = response.status();
        if status.is_success() {
            Ok(())
        } else {
            Err(api_error(
                status,
                "Failed to delete MCP server",
                response.text().await?,
            ))
        }
    }
}

#[derive(Serialize)]
struct DiscoverMcpServerRequest {
    endpoint: String,
}

pub struct DiscoverMcpServerBuilder {
    client: WachtClient,
    endpoint: String,
}
impl DiscoverMcpServerBuilder {
    pub fn new(client: WachtClient, endpoint: impl Into<String>) -> Self {
        Self {
            client,
            endpoint: endpoint.into(),
        }
    }
    pub async fn send(self) -> Result<McpServerDiscoveryResponse> {
        let response = self
            .client
            .http_client()
            .post(format!(
                "{}/ai/mcp-servers/discover",
                self.client.config().base_url
            ))
            .json(&DiscoverMcpServerRequest {
                endpoint: self.endpoint,
            })
            .send()
            .await?;
        let status = response.status();
        if status.is_success() {
            Ok(response.json().await?)
        } else {
            Err(api_error(
                status,
                "Failed to discover MCP server auth",
                response.text().await?,
            ))
        }
    }
}