use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use anyhow::Result;
use rmcp::model::{CacheScope, DiscoverResult, ServerCapabilities, ServerInfo};
use rmcp::service::{MaybeSendFuture, RequestContext, RoleServer};
use rmcp::{tool_handler, ServerHandler, ServiceExt};
use serde::{Deserialize, Serialize};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::UnixStream;
use crate::graph::edges::EdgeKind;
use crate::graph::Graph;
use super::tools::MatiServer;
use super::types::{MemBootstrapParams, MemGetParams, MemQueryParams};
use crate::mcp::{daemon_lifecycle, dispatch_v2, handlers, metadata, metrics, protocol};
#[derive(Debug)]
pub(crate) enum ProxyDaemonResult {
Ok(serde_json::Value),
NotRunning,
StaleSocket,
Unresponsive,
}
const DISCOVER_TTL_MS: u64 = 3_600_000;
const LIST_TOOLS_TTL_MS: u64 = 3_600_000;
#[tool_handler(router = self.tool_router)]
impl ServerHandler for MatiServer {
fn get_info(&self) -> ServerInfo {
ServerInfo::new(
ServerCapabilities::builder()
.enable_tools()
.enable_tool_list_changed()
.build(),
)
.with_instructions(
"mati is a persistent engineering knowledge store for the current \
codebase. Use mem_get for direct record lookup, mem_query for \
search and graph traversal, mem_bootstrap for session context, \
and mem_set for writing knowledge records.",
)
}
fn discover(
&self,
_context: RequestContext<RoleServer>,
) -> impl std::future::Future<Output = Result<DiscoverResult, rmcp::ErrorData>> + MaybeSendFuture + '_
{
let result = DiscoverResult::from_server_info(
self.supported_protocol_versions().into_owned(),
self.get_info(),
)
.with_ttl_ms(DISCOVER_TTL_MS)
.with_cache_scope(CacheScope::Public);
std::future::ready(Ok(result))
}
async fn list_tools(
&self,
_request: Option<rmcp::model::PaginatedRequestParams>,
context: RequestContext<RoleServer>,
) -> Result<rmcp::model::ListToolsResult, rmcp::ErrorData> {
let supports_cache_hints = context
.protocol_version()
.is_some_and(|version| version >= rmcp::model::ProtocolVersion::V_2026_07_28);
let mut result = rmcp::model::ListToolsResult::with_all_items(self.tool_router.list_all());
if supports_cache_hints {
result = result
.with_ttl_ms(LIST_TOOLS_TTL_MS)
.with_cache_scope(CacheScope::Public);
}
Ok(result)
}
}
mod dispatch;
mod promotion;
mod proxy;
mod socket;
#[cfg(test)]
mod shutdown_tests;
#[cfg(test)]
mod tests;
pub(crate) use dispatch::socket_dispatch;
pub use promotion::{IDLE_CHECK_INTERVAL_SECS, IDLE_SHUTDOWN_SECS};
pub use proxy::serve;
pub(crate) use proxy::{proxy_daemon_result, proxy_daemon_result_no_spawn, proxy_daemon_v2};
use socket::build_v1_dispatch_ctx;
#[cfg(test)]
use socket::PROTOCOL_VERSION;
pub use socket::{
socket_handle_connection, Shutdown, AUTO_DRAIN_TIMEOUT, MAX_CONCURRENT_CONNECTIONS,
UNIX_SOCK_PATH_MAX,
};
pub(crate) use socket::{SocketRequest, SocketResponse};