Skip to main content

systemprompt_models/mcp/
server.rs

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