1use axum::{
2 extract::{rejection::JsonRejection, Extension, Path, State},
3 response::{IntoResponse, Json},
4};
5#[cfg(feature = "auth")]
6use soma_auth::AuthContext;
7#[cfg(not(feature = "auth"))]
8pub struct AuthContext {
9 pub sub: String,
10 pub scopes: Vec<String>,
11}
12use serde_json::{json, Value};
13use soma_application::{ApplicationError, GatewayExecuteRequest};
14use soma_http_api::json::{json_body_or_else, JsonBodyOutcome};
15
16use crate::{responses::application_error_status, ApiState};
17
18pub async fn v1_gateway_action(
19 State(state): State<ApiState>,
20 auth: Option<Extension<AuthContext>>,
21 Path(action): Path<String>,
22 body: Result<Json<Value>, JsonRejection>,
23) -> axum::response::Response {
24 let params = match json_body_or_else(body, true, || json!({})) {
25 JsonBodyOutcome::Params(value) => value,
26 JsonBodyOutcome::Response(response) => return response,
27 };
28 let auth = auth.as_ref().map(|Extension(auth)| auth);
29 let scopes = auth.map(|auth| auth.scopes.as_slice()).unwrap_or_default();
30 let context = state.execution_context(auth.map(|auth| auth.sub.as_str()), scopes);
31
32 match state
33 .application()
34 .gateway_execute(
35 GatewayExecuteRequest {
36 action: action.clone(),
37 params,
38 },
39 context,
40 )
41 .await
42 {
43 Ok(response) => Json(response.output).into_response(),
44 Err(error) => gateway_error_response(&action, error),
45 }
46}
47
48fn gateway_error_response(action: &str, error: ApplicationError) -> axum::response::Response {
49 let status = application_error_status(&error);
50 let kind = match error.code.as_str() {
51 "admin_required" | "not_exposed" => "authorization",
52 "invalid_param"
53 | "unknown_action"
54 | "spawn_validation_failed"
55 | "upstream_exists"
56 | "upstream_missing"
57 | "invalid_config"
58 | "unknown_upstream" => "validation",
59 "unsupported_transport" => "unsupported",
60 "response_too_large" => "limits",
61 "store_not_mounted" => "configuration",
62 _ => "runtime",
63 };
64 (
65 status,
66 Json(json!({
67 "isError": true,
68 "schema_version": "mcp.gateway.error.v1",
69 "code": error.code,
70 "kind": kind,
71 "tool": "gateway",
72 "action": action,
73 "remediation": error.remediation,
74 })),
75 )
76 .into_response()
77}
78
79#[cfg(test)]
80#[path = "gateway_tests.rs"]
81mod tests;