vtcode_core/mcp/
errors.rs1use anyhow::anyhow;
12use std::fmt;
13
14pub type McpResult<T> = anyhow::Result<T>;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum ErrorCode {
19 ToolNotFound = 1,
21 ToolInvocationFailed = 2,
23 ProviderNotFound = 11,
25 ProviderUnavailable = 12,
27 SchemaInvalid = 21,
29 ConfigurationError = 31,
31 InitializationTimeout = 32,
33}
34
35impl ErrorCode {
36 pub fn code(&self) -> String {
38 format!("MCP_E{:03}", *self as u32)
39 }
40
41 pub fn name(&self) -> &'static str {
43 match self {
44 Self::ToolNotFound => "ToolNotFound",
45 Self::ToolInvocationFailed => "ToolInvocationFailed",
46 Self::ProviderNotFound => "ProviderNotFound",
47 Self::ProviderUnavailable => "ProviderUnavailable",
48 Self::SchemaInvalid => "SchemaInvalid",
49 Self::ConfigurationError => "ConfigurationError",
50 Self::InitializationTimeout => "InitializationTimeout",
51 }
52 }
53
54 pub fn user_guidance(&self) -> &'static str {
56 match self {
57 Self::ToolNotFound => {
58 "Check that the tool name is correct and the MCP provider is running."
59 }
60 Self::ToolInvocationFailed => {
61 "The MCP tool returned an error. Check the tool's arguments and provider logs."
62 }
63 Self::ProviderNotFound => {
64 "Verify the provider name in vtcode.toml or .mcp.json matches a configured MCP server."
65 }
66 Self::ProviderUnavailable => {
67 "The MCP server may be down. Check that the command/endpoint is reachable."
68 }
69 Self::SchemaInvalid => {
70 "The tool's input schema does not match expected format. Check the MCP server implementation."
71 }
72 Self::ConfigurationError => {
73 "Review the MCP section of vtcode.toml or .mcp.json for syntax errors."
74 }
75 Self::InitializationTimeout => {
76 "The MCP server took too long to start. Increase startup_timeout_ms or check the server process."
77 }
78 }
79 }
80}
81
82impl fmt::Display for ErrorCode {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(f, "{}", self.code())
85 }
86}
87
88pub fn tool_not_found(name: &str) -> anyhow::Error {
92 anyhow!(
93 "[{}] MCP tool '{}' not found",
94 ErrorCode::ToolNotFound.code(),
95 name
96 )
97}
98
99pub fn provider_not_found(name: &str) -> anyhow::Error {
103 anyhow!(
104 "[{}] MCP provider '{}' not found",
105 ErrorCode::ProviderNotFound.code(),
106 name
107 )
108}
109
110pub fn provider_unavailable(name: &str) -> anyhow::Error {
114 anyhow!(
115 "[{}] MCP provider '{}' is unavailable or failed to initialize",
116 ErrorCode::ProviderUnavailable.code(),
117 name
118 )
119}
120
121pub fn schema_invalid(reason: &str) -> anyhow::Error {
125 anyhow!(
126 "[{}] MCP tool schema is invalid: {}",
127 ErrorCode::SchemaInvalid.code(),
128 reason
129 )
130}
131
132pub fn tool_invocation_failed(provider: &str, tool: &str, reason: &str) -> anyhow::Error {
136 anyhow!(
137 "[{}] Failed to invoke tool '{}' on provider '{}': {}",
138 ErrorCode::ToolInvocationFailed.code(),
139 tool,
140 provider,
141 reason
142 )
143}
144
145pub fn initialization_timeout(timeout_secs: u64) -> anyhow::Error {
149 anyhow!(
150 "[{}] MCP initialization timeout after {} seconds",
151 ErrorCode::InitializationTimeout.code(),
152 timeout_secs
153 )
154}
155
156pub fn configuration_error(reason: &str) -> anyhow::Error {
160 anyhow!(
161 "[{}] MCP configuration error: {}",
162 ErrorCode::ConfigurationError.code(),
163 reason
164 )
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn test_error_codes_format() {
173 assert_eq!(ErrorCode::ToolNotFound.code(), "MCP_E001");
174 assert_eq!(ErrorCode::ToolInvocationFailed.code(), "MCP_E002");
175 assert_eq!(ErrorCode::ProviderNotFound.code(), "MCP_E011");
176 assert_eq!(ErrorCode::ProviderUnavailable.code(), "MCP_E012");
177 assert_eq!(ErrorCode::SchemaInvalid.code(), "MCP_E021");
178 assert_eq!(ErrorCode::ConfigurationError.code(), "MCP_E031");
179 assert_eq!(ErrorCode::InitializationTimeout.code(), "MCP_E032");
180 }
181
182 #[test]
183 fn test_error_names() {
184 assert_eq!(ErrorCode::ToolNotFound.name(), "ToolNotFound");
185 assert_eq!(ErrorCode::ProviderNotFound.name(), "ProviderNotFound");
186 assert_eq!(
187 ErrorCode::InitializationTimeout.name(),
188 "InitializationTimeout"
189 );
190 }
191
192 #[test]
193 fn test_error_messages_with_codes() {
194 let err = tool_not_found("missing_tool");
195 let msg = err.to_string();
196 assert!(msg.contains("[MCP_E001]"));
197 assert!(msg.contains("missing_tool"));
198 assert!(msg.contains("not found"));
199
200 let err = provider_not_found("missing_provider");
201 let msg = err.to_string();
202 assert!(msg.contains("[MCP_E011]"));
203 assert!(msg.contains("missing_provider"));
204
205 let err = initialization_timeout(15);
206 let msg = err.to_string();
207 assert!(msg.contains("[MCP_E032]"));
208 assert!(msg.contains("15 seconds"));
209
210 let err = tool_invocation_failed(
211 "claude",
212 vtcode_config::constants::tools::LIST_FILES,
213 "timeout",
214 );
215 let msg = err.to_string();
216 assert!(msg.contains("[MCP_E002]"));
217 assert!(msg.contains(vtcode_config::constants::tools::LIST_FILES));
218 assert!(msg.contains("timeout"));
219 }
220
221 #[test]
222 fn test_error_code_display() {
223 assert_eq!(ErrorCode::ToolNotFound.to_string(), "MCP_E001");
224 assert_eq!(ErrorCode::ProviderUnavailable.to_string(), "MCP_E012");
225 }
226}