1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! Error types and conversions for MCP responses.
use rmcp::model::{Content, ErrorCode, ErrorData, IntoContents};
use serde_json::json;
use std::borrow::Cow;
/// Error type for all wtp-mcp operations.
#[derive(Debug, thiserror::Error)]
pub enum WtpMcpError {
/// The `wtp` binary could not be located.
#[error("wtp binary not found: {message}")]
BinaryNotFound {
/// Details about the lookup failure.
message: String,
},
/// A `wtp` command exited with a non-zero status.
#[error("wtp command failed (exit {exit_code}): {message}")]
CommandFailed {
/// Exit code returned by the command.
exit_code: i32,
/// Human-readable summary of the failure.
message: String,
/// Standard error captured from the command.
stderr: String,
},
/// Failed to parse `wtp` output into structured data.
#[error("failed to parse wtp output: {message}")]
ParseError {
/// Summary of the parse failure.
message: String,
/// Raw output that failed to parse.
raw_output: String,
},
/// Operation blocked by security policy.
#[error("policy violation: {message}")]
PolicyViolation {
/// Policy violation details.
message: String,
},
/// Failed to download a required artifact (such as `wtp`).
#[error("download failed: {message}")]
DownloadFailed {
/// Summary of the download failure.
message: String,
},
/// Configuration is invalid or inconsistent.
#[error("configuration error: {message}")]
ConfigError {
/// Summary of the configuration error.
message: String,
},
/// Failed to read a config file from disk.
#[error("failed to read config file at {path}: {source}")]
ConfigRead {
/// Path to the config file.
path: std::path::PathBuf,
/// Underlying IO error.
#[source]
source: std::io::Error,
},
/// Failed to parse a config file.
#[error("failed to parse config file at {path}: {source}")]
ConfigParse {
/// Path to the config file.
path: std::path::PathBuf,
/// Underlying TOML parse error.
#[source]
source: toml::de::Error,
},
/// Unhandled IO error from filesystem or process operations.
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}
impl From<WtpMcpError> for ErrorData {
fn from(err: WtpMcpError) -> Self {
let code = match &err {
WtpMcpError::BinaryNotFound { .. } => ErrorCode::INTERNAL_ERROR,
WtpMcpError::CommandFailed { .. } => ErrorCode::INTERNAL_ERROR,
WtpMcpError::ParseError { .. } => ErrorCode::PARSE_ERROR,
WtpMcpError::PolicyViolation { .. } => ErrorCode::INVALID_REQUEST,
WtpMcpError::DownloadFailed { .. } => ErrorCode::INTERNAL_ERROR,
WtpMcpError::ConfigError { .. } => ErrorCode::INVALID_PARAMS,
WtpMcpError::ConfigRead { .. } => ErrorCode::INTERNAL_ERROR,
WtpMcpError::ConfigParse { .. } => ErrorCode::INVALID_PARAMS,
WtpMcpError::Io(_) => ErrorCode::INTERNAL_ERROR,
};
let data = match &err {
WtpMcpError::CommandFailed {
exit_code, stderr, ..
} => Some(json!({
"exit_code": exit_code,
"stderr": stderr,
})),
WtpMcpError::ParseError { raw_output, .. } => Some(json!({
"raw_output": raw_output,
})),
_ => None,
};
ErrorData {
code,
message: Cow::Owned(err.to_string()),
data,
}
}
}
impl IntoContents for WtpMcpError {
fn into_contents(self) -> Vec<Content> {
vec![Content::text(self.to_string())]
}
}
/// Convenience alias for `WtpMcpError`.
pub type Error = WtpMcpError;
/// Convenience result alias for wtp-mcp operations.
pub type Result<T> = std::result::Result<T, WtpMcpError>;