handoff-mcp-server 0.7.2 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Cargo.lock CHANGED
@@ -144,7 +144,7 @@ dependencies = [
144
144
 
145
145
  [[package]]
146
146
  name = "handoff-mcp"
147
- version = "0.7.2"
147
+ version = "0.7.3"
148
148
  dependencies = [
149
149
  "anyhow",
150
150
  "chrono",
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "handoff-mcp"
3
- version = "0.7.2"
3
+ version = "0.7.3"
4
4
  edition = "2021"
5
5
  description = "MCP server that gives AI coding agents persistent memory across sessions"
6
6
  license = "MIT"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "handoff-mcp-server",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "MCP server that gives AI coding agents persistent memory across sessions",
5
5
  "license": "MIT",
6
6
  "author": "AlphaElements <66808803+alphaelements@users.noreply.github.com>",
@@ -7,8 +7,7 @@ use crate::storage::config::read_config;
7
7
  use crate::storage::ensure_handoff_exists;
8
8
  use crate::storage::git::capture_git_state;
9
9
  use crate::storage::sessions::{
10
- close_active_sessions, close_open_sessions, enforce_history_limit, write_open_session,
11
- SessionData,
10
+ close_active_sessions, enforce_history_limit, write_open_session, SessionData,
12
11
  };
13
12
  use crate::storage::tasks::*;
14
13
 
@@ -60,7 +59,6 @@ pub fn handle(arguments: &Value) -> Result<String> {
60
59
  })?;
61
60
 
62
61
  close_active_sessions(&sessions_dir)?;
63
- let closed = close_open_sessions(&sessions_dir)?;
64
62
  let git_state = capture_git_state(&project_dir)?;
65
63
  let now = Utc::now().to_rfc3339();
66
64
 
@@ -117,12 +115,10 @@ pub fn handle(arguments: &Value) -> Result<String> {
117
115
  };
118
116
  enforce_history_limit(&sessions_dir, history_limit)?;
119
117
 
120
- let _ = closed;
121
118
  session_saved = true;
122
119
  } else if let Some(raw_notes) = arguments.get("raw_notes").and_then(|v| v.as_str()) {
123
120
  if !raw_notes.is_empty() {
124
121
  close_active_sessions(&sessions_dir)?;
125
- let closed = close_open_sessions(&sessions_dir)?;
126
122
  let git_state = capture_git_state(&project_dir)?;
127
123
  let now = Utc::now().to_rfc3339();
128
124
 
@@ -162,7 +158,6 @@ pub fn handle(arguments: &Value) -> Result<String> {
162
158
  };
163
159
  enforce_history_limit(&sessions_dir, history_limit)?;
164
160
 
165
- let _ = closed;
166
161
  session_saved = true;
167
162
  }
168
163
  }
@@ -9,9 +9,9 @@ use crate::storage::config::read_config;
9
9
  use crate::storage::ensure_handoff_exists;
10
10
  use crate::storage::git::capture_git_state;
11
11
  use crate::storage::sessions::{
12
- close_active_sessions, close_open_sessions, close_session_by_id, enforce_history_limit,
13
- generate_session_id, pause_active_sessions, pause_session_by_id, read_active_sessions,
14
- write_open_session, SessionData,
12
+ close_active_sessions, close_session_by_id, enforce_history_limit, generate_session_id,
13
+ pause_active_sessions, pause_session_by_id, read_active_sessions, write_session_with_status,
14
+ SessionData,
15
15
  };
16
16
 
17
17
  pub fn handle(arguments: &Value) -> Result<String> {
@@ -32,6 +32,17 @@ pub fn handle(arguments: &Value) -> Result<String> {
32
32
  .get("pause_active")
33
33
  .and_then(|v| v.as_bool())
34
34
  .unwrap_or(false);
35
+ let session_status = arguments
36
+ .get("session_status")
37
+ .and_then(|v| v.as_str())
38
+ .unwrap_or("open");
39
+
40
+ if session_status != "open" && session_status != "active" {
41
+ anyhow::bail!(
42
+ "Invalid session_status '{}': must be 'open' or 'active'",
43
+ session_status
44
+ );
45
+ }
35
46
 
36
47
  let mut total_paused = 0usize;
37
48
  if let Some(id) = pause_id {
@@ -50,8 +61,7 @@ pub fn handle(arguments: &Value) -> Result<String> {
50
61
  0
51
62
  }
52
63
  } else if pause_id.is_some() || pause_all {
53
- let closed_open = close_open_sessions(&sessions_dir)?;
54
- closed_open.len()
64
+ 0
55
65
  } else {
56
66
  let active = read_active_sessions(&sessions_dir)?;
57
67
  if active.len() > 1 {
@@ -64,8 +74,7 @@ pub fn handle(arguments: &Value) -> Result<String> {
64
74
  );
65
75
  }
66
76
  let closed_active = close_active_sessions(&sessions_dir)?;
67
- let closed_open = close_open_sessions(&sessions_dir)?;
68
- closed_active.len() + closed_open.len()
77
+ closed_active.len()
69
78
  };
70
79
 
71
80
  let git_state = capture_git_state(&project_dir)?;
@@ -89,7 +98,7 @@ pub fn handle(arguments: &Value) -> Result<String> {
89
98
  environment: arguments.get("environment").cloned(),
90
99
  };
91
100
 
92
- let path = write_open_session(&sessions_dir, &data)?;
101
+ let path = write_session_with_status(&sessions_dir, &data, session_status)?;
93
102
 
94
103
  let history_limit = if config_path.exists() {
95
104
  read_config(&config_path)
package/src/mcp/tools.rs CHANGED
@@ -57,9 +57,15 @@ pub fn all_tool_definitions() -> Vec<ToolDefinition> {
57
57
  "type": "string",
58
58
  "description": "One-line summary of this session"
59
59
  },
60
+ "session_status": {
61
+ "type": "string",
62
+ "description": "Status of the new session: 'open' (default) = saved but not activated, allows multiple open sessions; 'active' = immediately active for this session.",
63
+ "enum": ["open", "active"],
64
+ "default": "open"
65
+ },
60
66
  "close_session_id": {
61
67
  "type": "string",
62
- "description": "Session ID to close. If omitted (and no pause options set), all active and open sessions are closed."
68
+ "description": "Session ID to close. If omitted (and no pause options set), active sessions are closed."
63
69
  },
64
70
  "pause_session_id": {
65
71
  "type": "string",
@@ -321,8 +321,24 @@ pub fn enforce_history_limit(sessions_dir: &Path, limit: u32) -> Result<u32> {
321
321
  Ok(removed)
322
322
  }
323
323
 
324
- // Backward-compatible aliases for tests and migration
325
- #[doc(hidden)]
326
- pub fn write_active_session(sessions_dir: &Path, data: &SessionData) -> Result<PathBuf> {
327
- write_open_session(sessions_dir, data)
324
+ pub fn write_session_with_status(
325
+ sessions_dir: &Path,
326
+ data: &SessionData,
327
+ status: &str,
328
+ ) -> Result<PathBuf> {
329
+ let mut data = data.clone();
330
+ if data.id.is_none() {
331
+ data.id = Some(generate_session_id());
332
+ }
333
+
334
+ let ts_part = compact_timestamp(&data);
335
+ let base = generate_session_filename(&data.summary, &ts_part);
336
+ let filename = format!("{base}.{status}.json");
337
+ let path = sessions_dir.join(&filename);
338
+
339
+ let content = serde_json::to_string_pretty(&data).context("Failed to serialize session")?;
340
+ std::fs::write(&path, content)
341
+ .with_context(|| format!("Failed to write session: {}", path.display()))?;
342
+
343
+ Ok(path)
328
344
  }