1#![allow(clippy::redundant_closure)]
2#![allow(clippy::io_other_error)]
3
4pub mod config;
5pub mod error;
6pub mod error_utils;
7pub mod server;
8pub mod tools;
9
10pub use config::MetisServerConfig;
11pub use error::{McpServerError, Result};
12pub use server::MetisServerHandler;
13
14use anyhow::Result as AnyhowResult;
15use rust_mcp_sdk::{
16 mcp_server::server_runtime,
17 schema::{
18 Implementation, InitializeResult, ServerCapabilities, ServerCapabilitiesTools,
19 LATEST_PROTOCOL_VERSION,
20 },
21 McpServer, StdioTransport, TransportOptions,
22};
23use tracing::info;
24
25fn find_metis_log_path() -> Option<String> {
26 let current_dir = std::env::current_dir().ok()?;
27 let mut current = current_dir;
28
29 loop {
31 let metis_dir = current.join("metis");
32 let metis_db = metis_dir.join(".metis.db");
33
34 if metis_dir.is_dir() && metis_db.exists() {
36 return Some(
37 metis_dir
38 .join("metis-mcp-server.log")
39 .to_string_lossy()
40 .to_string(),
41 );
42 }
43
44 if let Some(parent) = current.parent() {
46 current = parent.to_path_buf();
47 } else {
48 break;
50 }
51 }
52
53 None
54}
55
56pub async fn run() -> AnyhowResult<()> {
58 let _ = if let Some(log_path) = find_metis_log_path() {
61 let log_file = std::fs::OpenOptions::new()
63 .create(true)
64 .append(true)
65 .open(&log_path)?;
66
67 tracing_subscriber::fmt()
68 .with_writer(log_file)
69 .with_ansi(false)
70 .try_init()
71 } else {
72 tracing_subscriber::fmt()
74 .with_writer(std::io::stderr)
75 .with_ansi(false)
76 .with_max_level(tracing::Level::WARN)
77 .try_init()
78 };
79
80 let config = MetisServerConfig::from_env()?;
82
83 info!("Starting Metis MCP Server");
84
85 let server_details = InitializeResult {
87 server_info: Implementation {
88 name: "Metis Documentation Management System".to_string(),
89 version: "0.1.0".to_string(),
90 title: Some("Metis MCP Server".to_string()),
91 },
92 capabilities: ServerCapabilities {
93 tools: Some(ServerCapabilitiesTools { list_changed: None }),
94 ..Default::default()
95 },
96 meta: None,
97 instructions: Some(
98 r#"# Metis Flight Levels Documentation Management
99
100## Overview
101Metis implements a hierarchical document management system based on Flight Levels methodology. You manage projects by creating and transitioning documents through defined phases using direct file paths.
102
103## Document Hierarchy (Flight Levels)
104Create documents in this order, with each level building on the previous:
105
1061. **Vision** (Level 3) - Overall purpose and direction
107 - Always start here - defines why the project exists
108 - Phases: draft → review → published
109
1102. **Strategy** (Level 2) - How to achieve the vision
111 - Must reference Vision as parent
112 - Phases: shaping → design → ready → active → completed
113
1143. **Initiative** (Level 1) - Concrete projects implementing strategies
115 - Must reference Strategy as parent
116 - Phases: discovery → design → ready → decompose → active → completed
117
1184. **Task** (Level 0) - Individual work items
119 - Must reference Initiative as parent
120 - Phases: todo → doing → completed
121
1225. **ADR** (Architectural Decision Record) - Technical decisions
123 - Can exist at any level, no parent required
124 - Phases: draft → discussion → decided → superseded
125
126## Direct Path Usage
127All tools use `project_path` pointing to the directory containing `.metis.db`:
128- Initialize: `{"project_path": "/path/to/project", "project_name": "my-project"}`
129- Create doc: `{"project_path": "/path/to/project", "document_type": "vision", "title": "Project Vision"}`
130- Update: `{"project_path": "/path/to/project", "document_path": "vision.md", ...}`
131
132## Essential Workflow
1331. **Start**: `initialize_project` creates `.metis.db` and initial structure
1342. **Build hierarchy**: Create Vision → Strategies → Initiatives → Tasks
1353. **Progress**: Use `validate_exit_criteria` before `transition_phase`
1364. **Update**: Use `update_*` tools for incremental changes
1375. **Query**: Use `list_documents` and `search_documents` to explore
138
139## Phase Transition Rules
140- Always validate exit criteria before transitioning: `validate_exit_criteria`
141- Use `transition_phase` only when ready to progress
142- Phase progression is generally linear (no skipping)
143- Force transitions with `force: true` only when necessary
144
145## Best Practices
146- Create documents in hierarchical order (Vision before Strategy, etc.)
147- Define clear exit criteria for each phase
148- Use `blocked_by` to track dependencies
149- Validate documents after creation/updates
150- Keep documents focused on their level's scope
151- Update parent documents when children complete
152
153## Common Patterns
154- Start with Vision, then 2-3 key Strategies
155- Each Strategy typically has 3-5 Initiatives
156- Each Initiative usually has 5-15 Tasks
157- Create ADRs for significant technical decisions
158- Review hierarchy regularly as work progresses"#.to_string(),
159 ),
160 protocol_version: LATEST_PROTOCOL_VERSION.to_string(),
161 };
162
163 let transport = StdioTransport::new(TransportOptions::default())
165 .map_err(|e| anyhow::anyhow!("Failed to create transport: {}", e))?;
166
167 let handler = MetisServerHandler::new(config);
169
170 let server = server_runtime::create_server(server_details, transport, handler);
172
173 info!("MCP Server starting on stdio transport");
174 server
175 .start()
176 .await
177 .map_err(|e| anyhow::anyhow!("MCP server failed to start: {}", e))?;
178
179 Ok(())
180}