13
crate::{account::lib::is_logged_in, client, token::get_smb_token::get_smb_token},
14
anyhow::{anyhow, Result},
15
rmcp::{
16
- handler::server::ServerHandler,
16
+ handler::server::{wrapper::Parameters, ServerHandler},
17
model::{CallToolResult, ContentBlock, Implementation, ServerCapabilities, ServerInfo},
18
tool, tool_handler, tool_router,
19
transport::stdio,
20
ErrorData, ServiceExt,
21
},
22
+ schemars::JsonSchema,
23
+ serde::Deserialize,
24
smbcloud_auth::me::me,
25
smbcloud_network::environment::Environment,
26
+ smbcloud_networking_project::{
27
+ crud_project_deployment_read::get_deployments, crud_project_read::get_project,
28
+ crud_project_read::get_projects,
29
+ },
30
};
31
32
/// The smbCloud MCP server. Holds the environment selected on the command line
39
pub fn new(environment: Environment) -> Self {
40
Self { environment }
41
}
36
-}
42
38
-#[tool_router]
39
-impl SmbMcpServer {
40
- #[tool(description = "Get the authenticated smbCloud user's account info. \
41
- Requires a prior `smb login`; returns the user as JSON.")]
42
- async fn me(&self) -> Result<CallToolResult, ErrorData> {
43
+ /// Resolve the stored auth token, mapping "not logged in" and read failures
44
+ /// to MCP errors. Every tool that hits the API goes through this.
45
+ fn access_token(&self) -> Result<String, ErrorData> {
46
if !is_logged_in(self.environment) {
47
return Err(ErrorData::invalid_request(
48
"Not logged in. Run `smb login` first.",
49
None,
50
));
51
}
49
- let token = get_smb_token(self.environment)
50
- .map_err(|e| ErrorData::internal_error(e.to_string(), None))?;
52
+ get_smb_token(self.environment).map_err(|e| ErrorData::internal_error(e.to_string(), None))
53
+ }
54
+}
55
+
56
+/// Serialize any JSON-able value into a single-content successful tool result.
57
+fn json_result<T: serde::Serialize>(value: &T) -> Result<CallToolResult, ErrorData> {
58
+ Ok(CallToolResult::success(vec![ContentBlock::json(value)?]))
59
+}
60
+
61
+#[derive(Debug, Deserialize, JsonSchema)]
62
+struct ProjectShowArgs {
63
+ /// The project ID to show.
64
+ id: String,
65
+}
66
+
67
+#[derive(Debug, Deserialize, JsonSchema)]
68
+struct DeploymentsArgs {
69
+ /// The project ID whose deployments to list.
70
+ project_id: i32,
71
+}
72
+
73
+#[tool_router]
74
+impl SmbMcpServer {
75
+ #[tool(description = "Get the authenticated smbCloud user's account info. \
76
+ Requires a prior `smb login`; returns the user as JSON.")]
77
+ async fn me(&self) -> Result<CallToolResult, ErrorData> {
78
+ let token = self.access_token()?;
79
let user = me(self.environment, client(), &token)
80
.await
81
.map_err(|e| ErrorData::internal_error(e.to_string(), None))?;
54
- let content = ContentBlock::json(&user)?;
55
- Ok(CallToolResult::success(vec![content]))
82
+ json_result(&user)
83
+ }
84
+
85
+ #[tool(description = "List the authenticated user's smbCloud projects as a JSON array.")]
86
+ async fn project_list(&self) -> Result<CallToolResult, ErrorData> {
87
+ let token = self.access_token()?;
88
+ let projects = get_projects(self.environment, client(), token)
89
+ .await
90
+ .map_err(|e| ErrorData::internal_error(e.to_string(), None))?;
91
+ json_result(&projects)
92
+ }
93
+
94
+ #[tool(description = "Show a single smbCloud project by ID, returned as JSON.")]
95
+ async fn project_show(
96
+ &self,
97
+ Parameters(args): Parameters<ProjectShowArgs>,
98
+ ) -> Result<CallToolResult, ErrorData> {
99
+ let token = self.access_token()?;
100
+ let project = get_project(self.environment, client(), token, args.id)
101
+ .await
102
+ .map_err(|e| ErrorData::internal_error(e.to_string(), None))?;
103
+ json_result(&project)
104
+ }
105
+
106
+ #[tool(description = "List deployments for a project by project ID, returned as a JSON array.")]
107
+ async fn deployments(
108
+ &self,
109
+ Parameters(args): Parameters<DeploymentsArgs>,
110
+ ) -> Result<CallToolResult, ErrorData> {
111
+ let token = self.access_token()?;
112
+ let deployments = get_deployments(self.environment, client(), token, args.project_id)
113
+ .await
114
+ .map_err(|e| ErrorData::internal_error(e.to_string(), None))?;
115
+ json_result(&deployments)
116
}
117
}
118