Skip to main content

systemprompt_models/mcp/
server.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use std::path::{Path, PathBuf};
4
5use crate::ai::ToolModelConfig;
6use crate::auth::{AuthenticatedUser, Permission};
7use crate::mcp::deployment::McpServerType;
8
9pub const RUNNING: &str = "running";
10pub const ERROR: &str = "error";
11pub const STOPPED: &str = "stopped";
12pub const STARTING: &str = "starting";
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct McpServerConfig {
16    pub name: String,
17    pub server_type: McpServerType,
18    pub binary: String,
19    pub enabled: bool,
20    pub display_in_web: bool,
21    pub port: u16,
22    #[serde(
23        serialize_with = "serialize_path",
24        deserialize_with = "deserialize_path"
25    )]
26    pub crate_path: PathBuf,
27    pub display_name: String,
28    pub description: String,
29    pub capabilities: Vec<String>,
30    #[serde(default)]
31    pub schemas: Vec<super::deployment::SchemaDefinition>,
32    pub oauth: super::deployment::OAuthRequirement,
33    #[serde(default)]
34    pub tools: HashMap<String, super::deployment::ToolMetadata>,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub model_config: Option<ToolModelConfig>,
37    #[serde(default)]
38    pub env_vars: Vec<String>,
39    pub version: String,
40    pub host: String,
41    pub module_name: String,
42    pub protocol: String,
43    #[serde(default)]
44    pub remote_endpoint: String,
45}
46
47fn serialize_path<S>(path: &Path, serializer: S) -> Result<S::Ok, S::Error>
48where
49    S: serde::Serializer,
50{
51    path.to_string_lossy().serialize(serializer)
52}
53
54fn deserialize_path<'de, D>(deserializer: D) -> Result<PathBuf, D::Error>
55where
56    D: serde::Deserializer<'de>,
57{
58    let s = String::deserialize(deserializer)?;
59    Ok(PathBuf::from(s))
60}
61
62impl McpServerConfig {
63    pub fn endpoint(&self, api_server_url: &str) -> String {
64        format!("{}/api/v1/mcp/{}/mcp", api_server_url, self.name)
65    }
66
67    pub const fn is_internal(&self) -> bool {
68        matches!(self.server_type, McpServerType::Internal)
69    }
70
71    pub const fn is_external(&self) -> bool {
72        matches!(self.server_type, McpServerType::External)
73    }
74}
75
76/// Authentication state for MCP connections
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub enum McpAuthState {
79    Authenticated(AuthenticatedUser),
80    Anonymous,
81}
82
83impl McpAuthState {
84    pub const fn is_authenticated(&self) -> bool {
85        matches!(self, Self::Authenticated(_))
86    }
87
88    pub const fn is_anonymous(&self) -> bool {
89        matches!(self, Self::Anonymous)
90    }
91
92    pub const fn user(&self) -> Option<&AuthenticatedUser> {
93        match self {
94            Self::Authenticated(user) => Some(user),
95            Self::Anonymous => None,
96        }
97    }
98
99    pub fn has_permission(&self, permission: Permission) -> bool {
100        match self {
101            Self::Authenticated(user) => user.has_permission(permission),
102            Self::Anonymous => permission == Permission::Anonymous,
103        }
104    }
105
106    pub fn username(&self) -> String {
107        match self {
108            Self::Authenticated(user) => user.username.clone(),
109            Self::Anonymous => "anonymous".to_string(),
110        }
111    }
112}