Skip to main content

systemprompt_models/mcp/
deployment.rs

1use crate::ai::ToolModelConfig;
2use crate::auth::{JwtAudience, Permission};
3use crate::mcp::capabilities::ToolVisibility;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use systemprompt_identifiers::ClientId;
7
8#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
9pub enum McpServerType {
10    #[default]
11    #[serde(rename = "internal")]
12    Internal,
13    #[serde(rename = "external")]
14    External,
15}
16
17impl McpServerType {
18    pub const fn as_str(&self) -> &'static str {
19        match self {
20            Self::Internal => "internal",
21            Self::External => "external",
22        }
23    }
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize, Default)]
27pub struct ToolUiConfig {
28    #[serde(default = "default_resource_uri_template")]
29    pub resource_uri_template: String,
30    #[serde(default = "default_visibility_enum")]
31    pub visibility: Vec<ToolVisibility>,
32}
33
34fn default_resource_uri_template() -> String {
35    "ui://systemprompt/{artifact_id}".to_string()
36}
37
38fn default_visibility_enum() -> Vec<ToolVisibility> {
39    vec![ToolVisibility::Model, ToolVisibility::App]
40}
41
42impl ToolUiConfig {
43    pub fn new() -> Self {
44        Self::default()
45    }
46
47    pub fn with_template(mut self, template: impl Into<String>) -> Self {
48        self.resource_uri_template = template.into();
49        self
50    }
51
52    pub fn model_only(mut self) -> Self {
53        self.visibility = vec![ToolVisibility::Model];
54        self
55    }
56
57    pub fn model_and_app(mut self) -> Self {
58        self.visibility = vec![ToolVisibility::Model, ToolVisibility::App];
59        self
60    }
61
62    pub fn to_meta_json(&self) -> serde_json::Value {
63        serde_json::json!({
64            "ui": {
65                "resourceUri": self.resource_uri_template,
66                "visibility": self.visibility
67            }
68        })
69    }
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize, Default)]
73pub struct ToolMetadata {
74    #[serde(default)]
75    pub terminal_on_success: bool,
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub model_config: Option<ToolModelConfig>,
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub ui: Option<ToolUiConfig>,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct DeploymentConfig {
84    pub deployments: HashMap<String, Deployment>,
85    pub settings: Settings,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct Deployment {
90    #[serde(default, alias = "type")]
91    pub server_type: McpServerType,
92    pub binary: String,
93    pub package: Option<String>,
94    pub port: u16,
95    pub endpoint: String,
96    pub enabled: bool,
97    pub display_in_web: bool,
98    #[serde(default)]
99    pub dev_only: bool,
100    #[serde(default)]
101    pub schemas: Vec<SchemaDefinition>,
102    pub oauth: OAuthRequirement,
103    #[serde(default)]
104    pub tools: HashMap<String, ToolMetadata>,
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub model_config: Option<ToolModelConfig>,
107    #[serde(default)]
108    pub env_vars: Vec<String>,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct SchemaDefinition {
113    pub file: String,
114    pub table: String,
115    pub required_columns: Vec<String>,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct OAuthRequirement {
120    pub required: bool,
121    pub scopes: Vec<Permission>,
122    pub audience: JwtAudience,
123    pub client_id: Option<ClientId>,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct Settings {
128    pub auto_build: bool,
129    pub build_timeout: u64,
130    pub health_check_timeout: u64,
131    #[serde(default = "default_base_port")]
132    pub base_port: u16,
133    #[serde(default = "default_working_dir")]
134    pub working_dir: String,
135}
136
137const fn default_base_port() -> u16 {
138    5000
139}
140
141fn default_working_dir() -> String {
142    "/app".to_string()
143}