#![allow(clippy::redundant_closure)]
#![allow(clippy::io_other_error)]
pub mod config;
pub mod error;
pub mod error_utils;
pub mod server;
pub mod tools;
pub use config::MetisServerConfig;
pub use error::{McpServerError, Result};
pub use server::MetisServerHandler;
use anyhow::Result as AnyhowResult;
use rust_mcp_sdk::{
mcp_server::server_runtime,
schema::{
Implementation, InitializeResult, ServerCapabilities, ServerCapabilitiesTools,
LATEST_PROTOCOL_VERSION,
},
McpServer, StdioTransport, TransportOptions,
};
use tracing::info;
fn find_metis_log_path() -> Option<String> {
let current_dir = std::env::current_dir().ok()?;
let mut current = current_dir;
loop {
let metis_dir = current.join("metis");
let metis_db = metis_dir.join(".metis.db");
if metis_dir.is_dir() && metis_db.exists() {
return Some(
metis_dir
.join("metis-mcp-server.log")
.to_string_lossy()
.to_string(),
);
}
if let Some(parent) = current.parent() {
current = parent.to_path_buf();
} else {
break;
}
}
None
}
pub async fn run() -> AnyhowResult<()> {
let _ = if let Some(log_path) = find_metis_log_path() {
let log_file = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(&log_path)?;
tracing_subscriber::fmt()
.with_writer(log_file)
.with_ansi(false)
.try_init()
} else {
tracing_subscriber::fmt()
.with_writer(std::io::stderr)
.with_ansi(false)
.with_max_level(tracing::Level::WARN)
.try_init()
};
let config = MetisServerConfig::from_env()?;
info!("Starting Metis MCP Server");
let server_details = InitializeResult {
server_info: Implementation {
name: "Metis Documentation Management System".to_string(),
version: "0.1.0".to_string(),
title: Some("Metis MCP Server".to_string()),
},
capabilities: ServerCapabilities {
tools: Some(ServerCapabilitiesTools { list_changed: None }),
..Default::default()
},
meta: None,
instructions: Some(
r#"# Metis Flight Levels Documentation Management
## Overview
Metis 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.
## Document Hierarchy (Flight Levels)
Create documents in this order, with each level building on the previous:
1. **Vision** (Level 3) - Overall purpose and direction
- Always start here - defines why the project exists
- Phases: draft → review → published
2. **Strategy** (Level 2) - How to achieve the vision
- Must reference Vision as parent
- Phases: shaping → design → ready → active → completed
3. **Initiative** (Level 1) - Concrete projects implementing strategies
- Must reference Strategy as parent
- Phases: discovery → design → ready → decompose → active → completed
4. **Task** (Level 0) - Individual work items
- Must reference Initiative as parent
- Phases: todo → doing → completed
5. **ADR** (Architectural Decision Record) - Technical decisions
- Can exist at any level, no parent required
- Phases: draft → discussion → decided → superseded
## Direct Path Usage
All tools use `project_path` pointing to the directory containing `.metis.db`:
- Initialize: `{"project_path": "/path/to/project", "project_name": "my-project"}`
- Create doc: `{"project_path": "/path/to/project", "document_type": "vision", "title": "Project Vision"}`
- Update: `{"project_path": "/path/to/project", "document_path": "vision.md", ...}`
## Essential Workflow
1. **Start**: `initialize_project` creates `.metis.db` and initial structure
2. **Build hierarchy**: Create Vision → Strategies → Initiatives → Tasks
3. **Progress**: Use `validate_exit_criteria` before `transition_phase`
4. **Update**: Use `update_*` tools for incremental changes
5. **Query**: Use `list_documents` and `search_documents` to explore
## Phase Transition Rules
- Always validate exit criteria before transitioning: `validate_exit_criteria`
- Use `transition_phase` only when ready to progress
- Phase progression is generally linear (no skipping)
- Force transitions with `force: true` only when necessary
## Best Practices
- Create documents in hierarchical order (Vision before Strategy, etc.)
- Define clear exit criteria for each phase
- Use `blocked_by` to track dependencies
- Validate documents after creation/updates
- Keep documents focused on their level's scope
- Update parent documents when children complete
## Common Patterns
- Start with Vision, then 2-3 key Strategies
- Each Strategy typically has 3-5 Initiatives
- Each Initiative usually has 5-15 Tasks
- Create ADRs for significant technical decisions
- Review hierarchy regularly as work progresses"#.to_string(),
),
protocol_version: LATEST_PROTOCOL_VERSION.to_string(),
};
let transport = StdioTransport::new(TransportOptions::default())
.map_err(|e| anyhow::anyhow!("Failed to create transport: {}", e))?;
let handler = MetisServerHandler::new(config);
let server = server_runtime::create_server(server_details, transport, handler);
info!("MCP Server starting on stdio transport");
server
.start()
.await
.map_err(|e| anyhow::anyhow!("MCP server failed to start: {}", e))?;
Ok(())
}