Skip to content

feat(mcp): implement MCP v0.2 — tools, prompts, session state, subscr…#42

Open
CoderMungan wants to merge 3 commits intoActiveMemory:mainfrom
CoderMungan:feat/mcp-v0.2
Open

feat(mcp): implement MCP v0.2 — tools, prompts, session state, subscr…#42
CoderMungan wants to merge 3 commits intoActiveMemory:mainfrom
CoderMungan:feat/mcp-v0.2

Conversation

@CoderMungan
Copy link
Member

…iptions

Add 7 new MCP tools (recall, watch_update, compact, next, check_task_completion, session_event, remind), 5 prompt templates (session-start, add-decision, add-learning, reflect, checkpoint), session state tracking, and resource subscription polling.

Security: add ValidateBoundary to toolAdd and toolComplete.
Convention: replace all literal "\n" with config.NewlineLF.
Docs: update cli/mcp.md with full tool/prompt reference.
Site: rebuild with zensical.

38 tests, all passing.

@CoderMungan CoderMungan force-pushed the feat/mcp-v0.2 branch 2 times, most recently from 97cafb9 to f082b7b Compare March 14, 2026 10:26
Copy link
Member

@josealekhine josealekhine left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contributions @CoderMungan : Solid PR. Clean architecture, good test coverage (38 tests), proper convention alignment (constants in config, descriptions in text.yaml, ValidateBoundary added). Good work.

The v0.2 scope is also: well-defined: 7 new tools, 5 prompts, session state, resource subscriptions.

Here are some things to be brushed off before merge:

Missing Localizable Entries

The tool descriptions are properly externalized to text.yaml via assets.TextDesc(). However, there are ~30+ inline string literals in the new tool handlers and prompts that should be text.yaml entries per the project convention ("All user-facing text is routed through internal/assets with YAML-backed TextDescKeys"):

Tools (tools.go):

  • "boundary violation: %v" — repeated 4x (toolAdd, toolComplete, toolWatchUpdate, toolCompact)
  • "type and content are required"
  • "type is required (start|end)"
  • "unknown event type: %s (use start|end)"
  • "No sessions found."
  • "invalid since date (use YYYY-MM-DD): %v"
  • "failed to find sessions: %v"
  • "Found %d session(s):"
  • "Wrote %s to .context/%s."
  • "Completed: %s"
  • "Review with: ctx status" — repeated 3x
  • "Nothing to compact — context is already clean."
  • "Compacted %d items. This reorganized TASKS.md."
  • "No TASKS.md found."
  • "Next task (#%d): %s"
  • "All tasks completed. No pending work."
  • "Did this complete task #%d: "%s"?"
  • "Session started. Context: %s" / "Session started for %s. Context: %s"
  • "Session ending." / "No pending updates." / "%d pending updates queued."
  • "Session stats: %d tool calls, %d entries added."
  • "No reminders." / "%d reminder(s):"

Prompts (prompts.go):

  • All prompt text is hardcoded (session-start orientation, reflect instructions, checkpoint steps, add-decision/add-learning formatting). These are user-facing instructional text blocks.

The existing v0.1 tools (toolStatus, toolDrift) use assets.TextDesc() for everything — the v0.2 tools break this pattern.

Roadmap Alignment Analysis

  • F.1 (MCP server integration) in TASKS.md is the parent task — this PR delivers it substantially
  • CI compliance from PR #27 noted in TASKS.md mentions: "missing copyright header on server_test.go, missing doc.go for internal/cli/mcp/" — the copyright header appears fixed, but check if doc.go for cli/mcp/ was addressed
  • The decision "Skills stay CLI-based; MCP Prompts are the protocol equivalent" (2026-03-06) is properly reflected — prompts mirror skills, not replace them

Implementation Concerns

  1. doc.go File Organization section — CONVENTIONS.md says "Do NOT include # File Organization sections listing files" in doc.go. The updated internal/mcp/doc.go adds a # File Organization section listing all 7 files. This will fail drift checks.
  2. containsOverlap is naive — The word-matching heuristic (len(w) < 4 skip, matchCount >= 2) will false-positive on common words like "build", "server", "update". Consider using the task number as primary match rather than fuzzy text overlap.
  3. Resource poller goroutine lifecycle — The poll() goroutine is started inside subscribe() while holding p.mu, but poll() then calls checkChanges() which also acquires p.mu. This is safe because subscribe releases the lock before poll() runs its first tick, but worth a comment.
  4. outMu protects writes but not the main loop — The Serve() main loop writes after json.Marshal but the lock only wraps the Write call. If emitNotification fires between marshal and write, ordering is preserved but interleaving is possible. This is fine for line-delimited JSON-RPC but worth noting.
  5. Session state is not thread-safe — sessionState is accessed from the main Serve() goroutine and potentially from prompts/tools, but has no mutex. Since Serve() is single-threaded for request dispatch this is safe ""today"", but fragile if concurrency is added later.
  6. toolCompact reimplements compact logic — It duplicates logic from internal/cli/compact/core rather than calling a shared function. The core.ParseTaskBlocks, core.RemoveBlocksFromLines,
    and archive assembly are inlined. If compact behavior changes, two code paths wil diverge.
  7. Prompt names use hyphens, tool names use underscores — This is consistent with MCP convention (prompts = ctx-session-start, tools = ctx_session_event), but prompt names should probably be constants in config/mcp too, like tool names are, to avoid drift, and to align intent.

Missing Before Merge

  1. Externalize ~30 inline strings to text.yaml + embed.go TextDescKeys — this is the biggest gap vs project convention
  2. Remove the # File Organization section from doc.go per CONVENTIONS.md
  3. Add prompt name constants to internal/config/mcp/mcp.go (currently hardcoded strings in handlePromptsGet switch and promptDefs)
  4. Property descriptions for the new tools use inline strings ("Max sessions to return (default: 5)", etc.) — the v0.1 tools use assets.TextDesc(assets.TextDescKeyMCPToolProp*) for these.
    The v0.2 tools skip this pattern
  5. Verify embed_test.go coverage — the exhaustive text key test should catch missing keys, but it needs the new keys registered to enforce it

What Looks Good

  • ValidateBoundary added to toolAdd and toolComplete (security improvement)
  • sync.Mutex on stdout writes for poller goroutine safety
  • All 7 tool name constants properly placed in config/mcp
  • Text.yaml entries for all 7 tool descriptions
  • 38 tests covering tools, prompts, session state, subscriptions, edge cases
  • Clean separation: session.go for state, prompts.go for prompts, resources.go for subscriptions
  • config.NewlineLF used consistently (no literal \n)

…iptions

Add 7 new MCP tools (recall, watch_update, compact, next,
check_task_completion, session_event, remind), 5 prompt templates
(session-start, add-decision, add-learning, reflect, checkpoint),
session state tracking, and resource subscription polling.

Security: add ValidateBoundary to toolAdd and toolComplete.
Convention: replace all literal "\n" with config.NewlineLF.
Docs: update cli/mcp.md with full tool/prompt reference.
Site: rebuild with zensical.

38 tests, all passing.

Signed-off-by: CoderMungan <codermungan@gmail.com>
- Add MCPTool* constants for 7 new tools in config/mcp
- Add MCPMethod* constants for subscribe/unsubscribe/prompts methods
- Add MCPNotifyResourcesUpdated constant
- Add TextDescKey* constants and text.yaml entries for new tool descriptions
- Replace all hardcoded strings with constant references in tools.go, server.go, resources.go

Signed-off-by: CoderMungan <codermungan@gmail.com>
Address Jose's PR ActiveMemory#42 review:

- Externalize ~55 inline string literals to text.yaml with
  TextDescKey constants in embed.go
- Use assets.TextDesc() for all tool handler messages, property
  descriptions, prompt text, and error strings
- Add 5 MCPPrompt* name constants to config/mcp/mcp.go
- Use MCPSchemaNumber/MCPSchemaBoolean constants for schema types
- Remove File Organization section from doc.go (CONVENTIONS.md)
- Add thread-safety comments for outMu, poller goroutine lifecycle,
  and sessionState access pattern
- Update resources.go subscribe/unsubscribe to use TextDesc()

All 38 tests pass. Build, vet, and lint-docs clean.

Signed-off-by: CoderMungan <codermungan@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants