feat(mcp): save reminder on inactivity + session activity score#178
Conversation
When no mem_save has been called for 10+ minutes in a session, append a nudge to mem_search and mem_context responses reminding the agent to persist decisions and discoveries. On mem_session_summary, append an activity score showing tool calls vs saves so agents and users can identify sessions with potential context loss. Implementation: - SessionActivity tracker with per-project state (mutex-protected) - Unified session ID resolution via defaultSessionID(project) - ClearSession on session end to prevent unbounded map growth - handleCapturePassive participates in activity tracking - Conditional pluralization in score output Closes #177
There was a problem hiding this comment.
Pull request overview
Adds server-side session activity tracking to nudge agents to call mem_save after inactivity and to surface a session activity score in mem_session_summary.
Changes:
- Introduces
SessionActivityto track tool calls/saves per (project-scoped) session and generate inactivity nudges + activity scores. - Wires activity tracking into MCP handlers (
mem_search,mem_context,mem_save, session start/end/summary, passive capture). - Adds unit + integration tests validating nudges, activity score formatting, session clearing, and concurrency behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| internal/mcp/mcp.go | Wires SessionActivity into MCP server/tool handlers; appends nudges and activity score; clears tracking on session end. |
| internal/mcp/activity.go | New mutex-protected activity tracker providing RecordToolCall, RecordSave, NudgeIfNeeded, ActivityScore, ClearSession. |
| internal/mcp/mcp_test.go | Updates handler tests for new dependency and adds integration tests for nudge/score/clear behavior. |
| internal/mcp/activity_test.go | Adds focused unit tests for timing, pluralization, idle suppression, clearing, and concurrent access. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Determine the project for this session to clean up activity tracking | ||
| project := cfg.DefaultProject | ||
| if p, _ := req.GetArguments()["project"].(string); p != "" { | ||
| project = p | ||
| } | ||
| project, _ = store.NormalizeProject(project) | ||
| activity.ClearSession(defaultSessionID(project)) |
There was a problem hiding this comment.
Activity cleanup in handleSessionEnd derives project from the request (or cfg.DefaultProject) rather than the session being ended. Existing clients may not send project, and store.EndSession is a no-op for missing IDs, so this can clear the wrong project's activity (or clear activity even when no session existed). Consider looking up the session's project via s.GetSession(id) (before/after ending) and only calling activity.ClearSession(defaultSessionID(sess.Project)) when the session exists.
| s := newMCPTestStore(t) | ||
|
|
||
| // Seed a memory to search for | ||
| s.CreateSession("s1", "myproject", "") |
There was a problem hiding this comment.
This test calls s.CreateSession(...) without checking the returned error. If setup fails, the test may pass/fail for the wrong reason; please assert err == nil.
| s.CreateSession("s1", "myproject", "") | |
| if err := s.CreateSession("s1", "myproject", ""); err != nil { | |
| t.Fatalf("CreateSession: %v", err) | |
| } |
| s.AddObservation(store.AddObservationParams{ | ||
| SessionID: "s1", | ||
| Type: "manual", | ||
| Title: "test memory", | ||
| Content: "some content", | ||
| Project: "myproject", | ||
| }) |
There was a problem hiding this comment.
This test ignores the return values from s.AddObservation(...) (it returns an ID and error). Please capture and assert the error to avoid false positives if seeding fails.
| s.AddObservation(store.AddObservationParams{ | |
| SessionID: "s1", | |
| Type: "manual", | |
| Title: "test memory", | |
| Content: "some content", | |
| Project: "myproject", | |
| }) | |
| _, err := s.AddObservation(store.AddObservationParams{ | |
| SessionID: "s1", | |
| Type: "manual", | |
| Title: "test memory", | |
| Content: "some content", | |
| Project: "myproject", | |
| }) | |
| if err != nil { | |
| t.Fatalf("AddObservation: %v", err) | |
| } |
| } | ||
|
|
||
| // Create session in store so EndSession works | ||
| s.CreateSession("real-session-id", project, "") |
There was a problem hiding this comment.
This test calls s.CreateSession(...) without checking the returned error. Please assert the error so the test can't silently proceed with invalid setup.
| s.CreateSession("real-session-id", project, "") | |
| if _, err := s.CreateSession("real-session-id", project, ""); err != nil { | |
| t.Fatalf("CreateSession setup failed: %v", err) | |
| } |
…Gentleman-Programming#178) When no mem_save has been called for 10+ minutes in a session, append a nudge to mem_search and mem_context responses reminding the agent to persist decisions and discoveries. On mem_session_summary, append an activity score showing tool calls vs saves so agents and users can identify sessions with potential context loss. Implementation: - SessionActivity tracker with per-project state (mutex-protected) - Unified session ID resolution via defaultSessionID(project) - ClearSession on session end to prevent unbounded map growth - handleCapturePassive participates in activity tracking - Conditional pluralization in score output Closes Gentleman-Programming#177
Summary
mem_savehas been called for 10+ minutes in a session, a nudge is appended tomem_searchandmem_contextresponsesmem_session_summary, an activity score (tool calls vs saves) is appended so agents can identify sessions with potential context lossSessionActivity(mutex-protected, cleaned up on session end)Why
AI agents have instructions to save memories proactively, but saving is an opt-in action that competes with the main task flow. When the agent is in an intense execution loop, it forgets to call
mem_save. The server now nudges the agent instead of relying on the agent to remember.Implementation
internal/mcp/activity.go—SessionActivitytracker withRecordToolCall,RecordSave,NudgeIfNeeded,ActivityScore,ClearSessiondefaultSessionID(project)across all handlershandleSearch,handleContext,handleSave,handleSessionStart,handleSessionEnd,handleSessionSummary,handleCapturePassivenowfunction for deterministic testingTest plan
go test -racepassesgo test ./...— 10/10 packages passCloses #177