Skip to main content

git_paw/mcp/tools/
session.rs

1//! Session-state tools: `get_session_status`, `get_session_summary`,
2//! `get_learnings`.
3//!
4//! These read the active/most-recent session receipt and the learnings file.
5//! A null session and empty learning sections are returned (not errors) when
6//! no session is active.
7
8use std::collections::BTreeMap;
9
10use rmcp::handler::server::wrapper::Json;
11use rmcp::{schemars, tool, tool_router};
12use serde::Serialize;
13
14use crate::mcp::query;
15use crate::mcp::server::GitPawMcpServer;
16
17/// Response for `get_session_status`.
18#[derive(Serialize, schemars::JsonSchema)]
19pub struct SessionStatusResponse {
20    /// Active session snapshot, or null.
21    pub session: Option<query::session::SessionSnapshot>,
22}
23
24/// Compact session summary.
25#[derive(Serialize, schemars::JsonSchema)]
26pub struct SessionSummary {
27    /// Session name.
28    pub name: String,
29    /// Session status.
30    pub status: String,
31    /// Number of registered agents.
32    pub agent_count: usize,
33    /// Counts of agents per live status label.
34    pub agents_by_status: BTreeMap<String, usize>,
35}
36
37/// Response for `get_session_summary`.
38#[derive(Serialize, schemars::JsonSchema)]
39pub struct SummaryResponse {
40    /// Session summary, or null.
41    pub summary: Option<SessionSummary>,
42}
43
44/// Response for `get_learnings`.
45#[derive(Serialize, schemars::JsonSchema)]
46pub struct LearningsResponse {
47    /// Parsed learning sections.
48    pub sections: Vec<query::learnings::LearningSection>,
49}
50
51#[tool_router(router = session_router, vis = "pub(crate)")]
52impl GitPawMcpServer {
53    /// `get_session_status` — the active session snapshot.
54    #[tool(
55        description = "Return the active session snapshot: name, mode, status, agent count, broker \
56                       URL, pause state, and per-agent status (live from the broker when reachable). \
57                       { \"session\": null } when no session is active."
58    )]
59    pub(crate) fn get_session_status(&self) -> Json<SessionStatusResponse> {
60        Json(SessionStatusResponse {
61            session: query::session::session_status(&self.ctx),
62        })
63    }
64
65    /// `get_session_summary` — a compact one-object session summary.
66    #[tool(
67        description = "Return a compact summary of the current session (name, status, agent count, \
68                       and per-status agent counts), or { \"summary\": null } when none is active."
69    )]
70    pub(crate) fn get_session_summary(&self) -> Json<SummaryResponse> {
71        let summary = query::session::session_status(&self.ctx).map(|s| {
72            let mut by_status: BTreeMap<String, usize> = BTreeMap::new();
73            for a in &s.agents {
74                if !a.status.is_empty() {
75                    *by_status.entry(a.status.clone()).or_default() += 1;
76                }
77            }
78            SessionSummary {
79                name: s.name,
80                status: s.status,
81                agent_count: s.agent_count,
82                agents_by_status: by_status,
83            }
84        });
85        Json(SummaryResponse { summary })
86    }
87
88    /// `get_learnings` — parsed session-learnings sections.
89    #[tool(
90        description = "Parse .git-paw/session-learnings.md into structured sections, each with a \
91                       category and its entries. Returns the canonical sections as empty arrays \
92                       when no learnings file exists."
93    )]
94    pub(crate) fn get_learnings(&self) -> Json<LearningsResponse> {
95        Json(LearningsResponse {
96            sections: query::learnings::learnings(&self.ctx),
97        })
98    }
99}