mcp_stdio_proxy/
mcp_error.rs1use axum::{
2 Json,
3 response::{IntoResponse, Response},
4};
5use http::StatusCode;
6use mcp_common::t;
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9
10#[derive(Error, Debug)]
14pub enum AppError {
15 #[error("{0}")]
17 ServiceNotFound(String),
18
19 #[error("{0}")]
21 ServiceRestartCooldown(String),
22
23 #[error("{0}")]
25 ServiceStartupInProgress(String),
26
27 #[error("{0}")]
29 ServiceStartupFailed(String),
30
31 #[error("{0}")]
33 BackendConnection(String),
34
35 #[error("{0}")]
37 ConfigParse(String),
38
39 #[error("{0}")]
41 McpServerError(String),
42
43 #[error("{0}")]
45 SerdeJsonError(String),
46
47 #[error("{0}")]
49 IoError(String),
50
51 #[error("{0}")]
53 RouteNotFound(String),
54
55 #[error("{0}")]
57 InvalidParameter(String),
58}
59
60impl AppError {
61 pub fn service_not_found(service: impl Into<String>) -> Self {
67 Self::ServiceNotFound(
68 t!(
69 "errors.mcp_proxy.service_not_found",
70 service = service.into()
71 )
72 .to_string(),
73 )
74 }
75
76 pub fn service_restart_cooldown(service: impl Into<String>) -> Self {
78 Self::ServiceRestartCooldown(
79 t!(
80 "errors.mcp_proxy.service_restart_cooldown",
81 service = service.into()
82 )
83 .to_string(),
84 )
85 }
86
87 pub fn service_startup_in_progress(service: impl Into<String>) -> Self {
89 Self::ServiceStartupInProgress(
90 t!(
91 "errors.mcp_proxy.service_startup_in_progress",
92 service = service.into()
93 )
94 .to_string(),
95 )
96 }
97
98 pub fn service_startup_failed(mcp_id: impl Into<String>, reason: impl Into<String>) -> Self {
100 Self::ServiceStartupFailed(
101 t!(
102 "errors.mcp_proxy.service_startup_failed",
103 mcp_id = mcp_id.into(),
104 reason = reason.into()
105 )
106 .to_string(),
107 )
108 }
109
110 pub fn backend_connection(detail: impl Into<String>) -> Self {
112 Self::BackendConnection(
113 t!(
114 "errors.mcp_proxy.backend_connection",
115 detail = detail.into()
116 )
117 .to_string(),
118 )
119 }
120
121 pub fn config_parse(detail: impl Into<String>) -> Self {
123 Self::ConfigParse(t!("errors.mcp_proxy.config_parse", detail = detail.into()).to_string())
124 }
125
126 pub fn mcp_server_error(detail: impl Into<String>) -> Self {
128 Self::McpServerError(
129 t!("errors.mcp_proxy.mcp_server_error", detail = detail.into()).to_string(),
130 )
131 }
132
133 pub fn json_serialization(detail: impl Into<String>) -> Self {
135 Self::SerdeJsonError(
136 t!(
137 "errors.mcp_proxy.json_serialization",
138 detail = detail.into()
139 )
140 .to_string(),
141 )
142 }
143
144 pub fn io_error(detail: impl Into<String>) -> Self {
146 Self::IoError(t!("errors.mcp_proxy.io_error", detail = detail.into()).to_string())
147 }
148
149 pub fn route_not_found(path: impl Into<String>) -> Self {
151 Self::RouteNotFound(t!("errors.mcp_proxy.route_not_found", path = path.into()).to_string())
152 }
153
154 pub fn invalid_parameter(detail: impl Into<String>) -> Self {
156 Self::InvalidParameter(
157 t!("errors.mcp_proxy.invalid_parameter", detail = detail.into()).to_string(),
158 )
159 }
160
161 pub fn error_code(&self) -> &'static str {
167 match self {
168 Self::ServiceNotFound(_) => "0001",
169 Self::ServiceRestartCooldown(_) => "0002",
170 Self::ServiceStartupInProgress(_) => "0003",
171 Self::ServiceStartupFailed { .. } => "0004",
172 Self::BackendConnection(_) => "0005",
173 Self::ConfigParse(_) => "0006",
174 Self::McpServerError(_) => "0007",
175 Self::SerdeJsonError(_) => "0008",
176 Self::IoError(_) => "0009",
177 Self::RouteNotFound(_) => "0010",
178 Self::InvalidParameter(_) => "0011",
179 }
180 }
181
182 pub fn status_code(&self) -> StatusCode {
184 match self {
185 Self::ServiceNotFound(_) => StatusCode::NOT_FOUND,
186 Self::ServiceRestartCooldown(_) => StatusCode::TOO_MANY_REQUESTS,
187 Self::ServiceStartupInProgress(_) => StatusCode::SERVICE_UNAVAILABLE,
188 Self::ServiceStartupFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
189 Self::BackendConnection(_) => StatusCode::BAD_GATEWAY,
190 Self::ConfigParse(_) => StatusCode::BAD_REQUEST,
191 Self::McpServerError(_) => StatusCode::INTERNAL_SERVER_ERROR,
192 Self::SerdeJsonError(_) => StatusCode::BAD_REQUEST,
193 Self::IoError(_) => StatusCode::INTERNAL_SERVER_ERROR,
194 Self::RouteNotFound(_) => StatusCode::NOT_FOUND,
195 Self::InvalidParameter(_) => StatusCode::BAD_REQUEST,
196 }
197 }
198}
199
200impl From<anyhow::Error> for AppError {
205 fn from(err: anyhow::Error) -> Self {
206 Self::mcp_server_error(err.to_string())
207 }
208}
209
210impl From<serde_json::Error> for AppError {
211 fn from(err: serde_json::Error) -> Self {
212 Self::json_serialization(err.to_string())
213 }
214}
215
216impl From<std::io::Error> for AppError {
217 fn from(err: std::io::Error) -> Self {
218 Self::io_error(err.to_string())
219 }
220}
221
222#[derive(Debug, Serialize, Deserialize)]
223pub struct ErrorOutput {
224 pub code: String,
225 pub error: String,
226}
227
228impl ErrorOutput {
229 pub fn with_code(code: &'static str, error: impl Into<String>) -> Self {
230 Self {
231 code: code.to_string(),
232 error: error.into(),
233 }
234 }
235}
236
237impl IntoResponse for AppError {
238 fn into_response(self) -> Response<axum::body::Body> {
239 (
240 self.status_code(),
241 Json(ErrorOutput::with_code(self.error_code(), self.to_string())),
242 )
243 .into_response()
244 }
245}