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