Skip to main content

mcp_servers/coding/
error.rs

1//! Error types for coding tools
2//!
3//! This module provides structured error types for all coding tool operations,
4//! replacing the previous `Result<T, String>` pattern with proper `thiserror` enums.
5
6use std::io;
7use thiserror::Error;
8
9use crate::lsp::error::LspError;
10use crate::workspace_paths::EmptyFilePathError;
11
12pub use crate::file_ops::FileError;
13
14#[doc = include_str!("../docs/coding_error.md")]
15#[derive(Debug, Error)]
16pub enum CodingError {
17    /// File operation errors (read, write, edit)
18    #[error(transparent)]
19    File(#[from] FileError),
20
21    /// Bash command execution errors
22    #[error(transparent)]
23    Bash(#[from] BashError),
24
25    /// Web fetch errors
26    #[error(transparent)]
27    WebFetch(#[from] WebFetchError),
28
29    /// Web search errors
30    #[error(transparent)]
31    WebSearch(#[from] WebSearchError),
32
33    /// LSP tool errors
34    #[error(transparent)]
35    Lsp(#[from] LspError),
36
37    /// Tool not configured/available
38    #[error("{0} is not configured")]
39    NotConfigured(&'static str),
40
41    /// A required file-path argument was empty
42    #[error(transparent)]
43    EmptyFilePath(#[from] EmptyFilePathError),
44
45    /// An existing file must be read before it may be overwritten
46    #[error(
47        "Safety check failed: File '{0}' already exists. You must use read_file on it before overwriting. This prevents accidental data loss."
48    )]
49    NotReadBeforeOverwrite(String),
50
51    /// A file must be read before it may be edited
52    #[error(
53        "Safety check failed: You must use read_file on '{0}' before editing it. This ensures you understand the current file contents before making changes."
54    )]
55    NotReadBeforeEdit(String),
56
57    /// Failed to check whether the target file exists
58    #[error("Failed to check existence of {path}: {source}")]
59    ExistsCheckFailed { path: String, source: io::Error },
60}
61
62/// Renders the error as the text content of an errored MCP tool result.
63impl rmcp::model::IntoContents for CodingError {
64    fn into_contents(self) -> Vec<rmcp::model::ContentBlock> {
65        vec![rmcp::model::ContentBlock::text(self.to_string())]
66    }
67}
68
69/// Errors related to bash command execution
70#[derive(Debug, Error)]
71pub enum BashError {
72    /// Command is forbidden (e.g., rm without flags)
73    #[error("{0}")]
74    Forbidden(String),
75
76    /// Timeout exceeds maximum allowed
77    #[error("Timeout cannot exceed 600000ms (10 minutes)")]
78    TimeoutTooLarge,
79
80    /// Failed to spawn process
81    #[error("Failed to execute command '{command}': {reason}")]
82    SpawnFailed { command: String, reason: String },
83}
84
85/// Errors related to web fetch operations
86#[derive(Debug, Clone, Error)]
87pub enum WebFetchError {
88    /// Invalid URL format
89    #[error("Invalid URL: {0}")]
90    InvalidUrl(#[source] url::ParseError),
91
92    /// HTTP request failed
93    #[error("Request failed: {0}")]
94    RequestFailed(String),
95
96    /// Request timed out
97    #[error("Request timed out after {0}ms")]
98    Timeout(u64),
99}
100
101/// Errors related to web search operations
102#[derive(Debug, Clone, Error)]
103pub enum WebSearchError {
104    /// Invalid search query
105    #[error("Invalid search query: {0}")]
106    InvalidQuery(String),
107
108    /// API request failed
109    #[error("API request failed: {0}")]
110    ApiError(String),
111
112    /// Rate limit exceeded
113    #[error("Rate limit exceeded: {message}")]
114    RateLimited { message: String, retry_after: Option<std::time::Duration> },
115
116    /// Request timed out
117    #[error("Request timed out after {0}ms")]
118    Timeout(u64),
119
120    /// Configuration error
121    #[error("Configuration error: {0}")]
122    ConfigError(String),
123
124    /// Failed to parse API response
125    #[error("Failed to parse API response: {0}")]
126    ParseError(String),
127}