Skip to main content

metis_mcp_server/tools/
initialize_project.rs

1use metis_core::application::services::workspace::initialization::WorkspaceInitializationService;
2use crate::formatting::ToolOutput;
3use rust_mcp_sdk::{
4    macros::{mcp_tool, JsonSchema},
5    schema::{schema_utils::CallToolError, CallToolResult},
6};
7use serde::{Deserialize, Serialize};
8use std::path::Path;
9
10#[mcp_tool(
11    name = "initialize_project",
12    description = "Initialize a new Metis project by creating a 'metis' subdirectory at the specified path. Sets up project configuration including short code generation (format: PREFIX-TYPE-NNNN).",
13    idempotent_hint = true,
14    destructive_hint = false,
15    open_world_hint = false,
16    read_only_hint = false
17)]
18#[derive(Debug, Serialize, Deserialize, JsonSchema)]
19pub struct InitializeProjectTool {
20    /// Path where the '.metis' subdirectory will be created (e.g., "/path/to/my-project" creates "/path/to/my-project/.metis/")
21    pub project_path: String,
22    /// Optional project prefix for document short codes, up to 6 characters (e.g., "PROJ", "ACME", "TEST"). If not provided, defaults to "PROJ"
23    pub prefix: Option<String>,
24}
25
26impl InitializeProjectTool {
27    pub async fn call_tool(&self) -> std::result::Result<CallToolResult, CallToolError> {
28        let project_path = Path::new(&self.project_path);
29
30        // Derive project name from the directory name
31        let project_name = project_path
32            .file_name()
33            .and_then(|name| name.to_str())
34            .unwrap_or("Metis Project");
35
36        // Use the WorkspaceInitializationService to handle all the setup
37        let result = WorkspaceInitializationService::initialize_workspace_with_prefix(
38            project_path,
39            project_name,
40            self.prefix.as_deref(),
41        )
42        .await
43        .map_err(|e| CallToolError::new(e))?;
44
45        // Get the configured prefix to include in response, limiting to 6 characters
46        let configured_prefix = {
47            let prefix = self.prefix.as_deref().unwrap_or("PROJ").to_uppercase();
48            if prefix.len() > 6 {
49                prefix.chars().take(6).collect()
50            } else {
51                prefix
52            }
53        };
54
55        let output = ToolOutput::new()
56            .header("Project Initialized")
57            .success(&format!(
58                "Initialized Metis workspace at {}",
59                result.metis_dir.display()
60            ))
61            .table(
62                &["Field", "Value"],
63                vec![
64                    vec![
65                        "Metis Directory".to_string(),
66                        result.metis_dir.to_string_lossy().to_string(),
67                    ],
68                    vec![
69                        "Database".to_string(),
70                        result.database_path.to_string_lossy().to_string(),
71                    ],
72                    vec![
73                        "Vision".to_string(),
74                        result.vision_path.to_string_lossy().to_string(),
75                    ],
76                    vec!["Project Prefix".to_string(), configured_prefix],
77                ],
78            )
79            .build_result();
80
81        Ok(output)
82    }
83}