Skip to content

feat: 3-tool simplification Phase 1 — unified brain_recall aliases#111

Merged
EtanHey merged 1 commit intomainfrom
feat/3tool-phase1-aliases
Mar 27, 2026
Merged

feat: 3-tool simplification Phase 1 — unified brain_recall aliases#111
EtanHey merged 1 commit intomainfrom
feat/3tool-phase1-aliases

Conversation

@EtanHey
Copy link
Copy Markdown
Owner

@EtanHey EtanHey commented Mar 26, 2026

Summary

  • brain_recall gains mode=search and mode=entity with smart auto-detection from query text (capitalized proper nouns → entity, "stats"/"how many" → stats, default → search)
  • brain_search and brain_entity become thin aliases delegating to brain_recall
  • brain_update, brain_expand, brain_tags now return isError: true with deprecation messages (were previously broken stubs returning fake success)
  • MCP instructions field condensed: 3 primary tool descriptions in ~40 words each
  • Mode completions updated to include search and entity

Acceptance Criteria

  • brain_recall gains mode parameter: search (default), context, entity, summary, stats
  • Smart routing: capitalized proper noun → entity; "stats"/"how many" → stats; default → search
  • brain_recall({ query: "topic" }) behaves identically to current brain_search("topic")
  • brain_recall({ query: "BrainLayer", mode: "entity" }) behaves identically to current brain_entity("BrainLayer")
  • brain_search and brain_entity become thin aliases delegating to brain_recall
  • MCP tools/list still exposes all tools (backward compatible)
  • MCP instructions field updated: 3 tool descriptions in <50 words each
  • brain_update, brain_expand, brain_tags stubs return isError: true with deprecation message
  • All existing tests pass (1143 passed, 0 regressions)
  • 35 new tests covering mode routing, smart detection, deprecation errors, edge cases
  • Zero breaking changes

Test plan

  • pytest tests/test_3tool_aliases.py -v — 35/35 pass
  • Full suite: 1143 passed, 2 skipped, 1 xfailed, 1 pre-existing flaky (event loop ordering)
  • Manual: verify brain_recall(query="test") via MCP client returns search results
  • Manual: verify brain_recall(query="BrainLayer", mode="entity") returns entity info

Worker: brainlayer-worker-A-R1 | Mission: 3-tool aliases

🤖 Generated with Claude Code

Note

Unify brain_search and brain_entity as aliases for brain_recall in the MCP tool layer

  • brain_recall in init.py and search_handler.py now accepts search and entity modes, plus a query param and rich filters (entity_type, tag, date range, sentiment, etc.).
  • brain_search and brain_entity become thin aliases in call_tool, delegating to _brain_recall with the appropriate mode and forwarding all params.
  • A new _smart_detect_mode heuristic in search_handler.py auto-infers mode from query text when no explicit mode is given (capitalized proper nouns → entity, stat keywords → context, else search).
  • brain_update, brain_expand, and brain_tags now return isError=True deprecation messages instead of executing prior behavior.
  • Behavioral Change: any caller using brain_update, brain_expand, or brain_tags will receive errors rather than results after this change.

Macroscope summarized 1cfc1b6.

Summary by CodeRabbit

  • New Features

    • Enhanced brain_recall tool with dedicated search and entity lookup modes
    • Added advanced filtering options including file path, content type, source, tags, date range, sentiment, and entity-specific queries
    • Intelligent mode detection automatically infers query intent when mode isn't explicitly specified
  • Deprecations

    • Marked brain_expand, brain_update, and brain_tags tools as deprecated
  • Changes

    • brain_search and brain_entity now operate as simplified aliases delegating to the primary brain_recall tool

…h + entity modes

brain_recall gains mode=search and mode=entity, with smart auto-detection
from query text. brain_search and brain_entity become thin aliases that
delegate to brain_recall. brain_update, brain_expand, brain_tags now return
isError: true with deprecation messages. MCP instructions condensed to 3
primary tools. 35 new tests, zero regressions on 1143 existing tests.

Worker: brainlayer-worker-A-R1 | Mission: 3-tool aliases

Co-Authored-By: brainClaude (worker) <noreply@anthropic.com>
Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

The changes consolidate MCP memory retrieval tools under brain_recall as the primary interface, expanding its mode enum to include "search" and "entity". brain_search and brain_entity become thin aliases delegating to brain_recall. Legacy tools (brain_expand, brain_update, brain_tags) return error stubs. Smart mode detection logic routes to appropriate handlers based on query content and explicit mode parameters.

Changes

Cohort / File(s) Summary
MCP Tool Consolidation
src/brainlayer/mcp/__init__.py, src/brainlayer/mcp/search_handler.py
Updated brain_recall tool definition with expanded mode enum (["search","entity","context","sessions","operations","plan","summary","stats"]), added query parameter and many filtering fields (file_path, chunk_id, content_type, source, tag, intent, importance_min, date_from, date_to, sentiment, entity_type, entity_id, num_results, detail). Introduced _smart_detect_mode() heuristic for automatic mode inference. Refactored control flow: brain_search and brain_entity now delegate to _brain_recall with appropriate mode. Replaced brain_expand, brain_update, brain_tags handlers with deprecated error stubs.
Test Suite for Consolidation
tests/test_3tool_aliases.py
Comprehensive Phase 1 test suite (+452 lines) validating smart mode detection routing, _brain_recall delegation to _brain_search and _brain_entity, backward compatibility for legacy modes, deprecation behavior of replaced tools, and tool schema verification.
Test Updates
tests/test_smart_search_entity_dedup.py
Updated brain_expand test to verify deprecation behavior (isError: true) and replaced second assertion to use brain_search with query parameter instead.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant call_tool as call_tool Router
    participant brain_recall as brain_recall Handler
    participant smart_detect as _smart_detect_mode
    participant _brain_search as _brain_search
    participant _brain_entity as _brain_entity
    participant other as Legacy Handlers<br/>(context, sessions, etc.)

    Client->>call_tool: call_tool(tool, args)
    alt tool == "brain_search" or "brain_entity"
        call_tool->>brain_recall: _brain_recall(mode=explicit, ...)
    else tool == "brain_recall"
        call_tool->>brain_recall: _brain_recall(mode, query, ...)
    end

    activate brain_recall
    alt mode explicitly set to "search" or "entity"
        brain_recall->>brain_recall: use explicit mode
    else mode is None and query provided
        brain_recall->>smart_detect: _smart_detect_mode(query, None)
        smart_detect-->>brain_recall: inferred mode
    else fallback
        brain_recall->>brain_recall: use legacy mode inference
    end

    alt mode == "search"
        brain_recall->>_brain_search: _brain_search(query, filters...)
        _brain_search-->>brain_recall: results
    else mode == "entity"
        brain_recall->>_brain_entity: _brain_entity(query, entity_type)
        _brain_entity-->>brain_recall: entity data
    else legacy modes
        brain_recall->>other: delegate to handler
        other-->>brain_recall: results
    end
    
    brain_recall-->>call_tool: CallToolResult
    deactivate brain_recall
    call_tool-->>Client: response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 Three tools were many, now one does it all,
With brain_recall answering the memory call,
Smart detection finds modes from the queries we send,
While deprecated paths gracefully come to an end. 🧠✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: 3-tool simplification Phase 1 — unified brain_recall aliases' accurately captures the primary change: consolidating brain_recall, brain_search, and brain_entity into a unified API with brain_search/entity as aliases, directly reflecting the main architectural improvement in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/3tool-phase1-aliases

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/brainlayer/mcp/search_handler.py`:
- Around line 338-374: In _smart_detect_mode, multi-word queries like "The
BrainLayer" get classified as entity because the _COMMON_WORDS check only
applies to single-word queries; update the logic to also reject phrases whose
first word is a common leading capitalized word: after computing words and
before returning "entity", if words[0] is in _COMMON_WORDS (or its
lowercase/normalized variant), return "search" so phrases that start with "The",
"How", etc. don't incorrectly route to entity; keep the existing single-word
special-case intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5f557936-648f-42e7-aff7-3654186a1c30

📥 Commits

Reviewing files that changed from the base of the PR and between 52f9c4a and 1cfc1b6.

📒 Files selected for processing (4)
  • src/brainlayer/mcp/__init__.py
  • src/brainlayer/mcp/search_handler.py
  • tests/test_3tool_aliases.py
  • tests/test_smart_search_entity_dedup.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Flag risky DB or concurrency changes explicitly and do not hand-wave lock behavior
Enforce one-write-at-a-time concurrency constraint; reads are safe but brain_digest is write-heavy and must not run in parallel with other MCP work
Run pytest before claiming behavior changed safely; current test suite has 929 tests

**/*.py: All scripts and CLI must use paths.py:get_db_path() for database path resolution instead of hardcoding paths
Preserve verbatim content for message types: ai_code, stack_trace, user_message during classification and chunking
Skip noise content entirely; summarize build_log; extract structure only from dir_listing during chunking
Use AST-aware chunking via tree-sitter; never split stack traces; mask large tool output
Implement retry logic on SQLITE_BUSY errors; each worker must use its own database connection
Override enrichment backend via BRAINLAYER_ENRICH_BACKEND environment variable (valid values: ollama, mlx, groq); default to Groq
Configure enrichment rate via BRAINLAYER_ENRICH_RATE environment variable (default: 0.2 = 12 RPM)
Checkpoint WAL before and after bulk database operations: PRAGMA wal_checkpoint(FULL)
Drop FTS triggers before bulk deletes from chunks table; recreate after operation to avoid performance degradation
Batch deletes in 5-10K chunk sizes with WAL checkpoint every 3 batches
Default search queries must exclude lifecycle-managed chunks; use include_archived=True parameter to show history
Lint and format code with ruff check src/ && ruff format src/
Session dedup coordination: SessionStart writes injected chunk_ids to /tmp/brainlayer_session_{id}.json; UserPromptSubmit skips already-injected chunks
Skip auto-search for prompts containing 'handoff' or 'session-handoff' keywords

Files:

  • tests/test_smart_search_entity_dedup.py
  • tests/test_3tool_aliases.py
  • src/brainlayer/mcp/search_handler.py
  • src/brainlayer/mcp/__init__.py
**/*test*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use pytest for testing

Files:

  • tests/test_smart_search_entity_dedup.py
  • tests/test_3tool_aliases.py
🧠 Learnings (2)
📓 Common learnings
Learnt from: EtanHey
Repo: EtanHey/brainlayer PR: 0
File: :0-0
Timestamp: 2026-03-17T01:04:22.497Z
Learning: Applies to src/brainlayer/mcp/**/*.py and brain-bar/Sources/BrainBar/MCPRouter.swift: The 8 required MCP tools are `brain_search`, `brain_store`, `brain_recall`, `brain_entity`, `brain_expand`, `brain_update`, `brain_digest`, `brain_tags`. `brain_tags` is the 8th tool, replacing `brain_get_person`, as defined in the Phase B spec merged in PR `#72`. The Python MCP server already implements `brain_tags`. Legacy `brainlayer_*` aliases must be maintained for backward compatibility.
Learnt from: CR
Repo: EtanHey/brainlayer PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:46:16.139Z
Learning: BrainBar Swift daemon tools `brain_update`, `brain_expand`, `brain_tags` are STUB implementations returning fake success and saving nothing; do not rely on these tools
📚 Learning: 2026-03-17T01:04:22.497Z
Learnt from: EtanHey
Repo: EtanHey/brainlayer PR: 0
File: :0-0
Timestamp: 2026-03-17T01:04:22.497Z
Learning: Applies to src/brainlayer/mcp/**/*.py and brain-bar/Sources/BrainBar/MCPRouter.swift: The 8 required MCP tools are `brain_search`, `brain_store`, `brain_recall`, `brain_entity`, `brain_expand`, `brain_update`, `brain_digest`, `brain_tags`. `brain_tags` is the 8th tool, replacing `brain_get_person`, as defined in the Phase B spec merged in PR `#72`. The Python MCP server already implements `brain_tags`. Legacy `brainlayer_*` aliases must be maintained for backward compatibility.

Applied to files:

  • tests/test_smart_search_entity_dedup.py
  • tests/test_3tool_aliases.py
  • src/brainlayer/mcp/search_handler.py
  • src/brainlayer/mcp/__init__.py
🔇 Additional comments (13)
src/brainlayer/mcp/search_handler.py (3)

426-433: Backward-compatible inference logic preserves existing behavior.

The conversion of default values to None before calling _infer_recall_mode (lines 430-432) correctly ensures that only explicitly-set days/limit parameters trigger "sessions" mode inference, matching pre-existing behavior.


437-459: Search mode delegation correctly validates and forwards all parameters.

The validation that query is required (line 438-439) and the comprehensive parameter forwarding to _brain_search ensures consistent behavior whether called directly or via the brain_recall dispatcher.


461-466: Entity mode delegation is correct and matches handler signature.

The lazy import pattern avoids potential circular dependencies. The call signature (query=query, entity_type=entity_type) correctly matches _brain_entity(query: str, entity_type: str | None = None) from entity_handler.py (lines 16-42 in context snippet).

tests/test_smart_search_entity_dedup.py (1)

227-239: Test correctly updated to verify deprecation behavior and migration path.

The test now properly:

  1. Asserts brain_expand returns isError: True with a deprecation message (lines 231-232)
  2. Verifies the equivalent functionality via brain_search(query="expand", chunk_id=stored["id"]) (lines 235-239)

This ensures the migration path is documented and tested.

tests/test_3tool_aliases.py (2)

1-20: Comprehensive test suite covering all Phase 1 changes.

Excellent test organization with clear separation:

  • Smart mode detection rules
  • Mode-specific routing validation
  • Backward compatibility for legacy modes
  • Deprecation error assertions
  • MCP tool schema verification

The test structure follows pytest conventions with descriptive class and method names.


278-306: Deprecation tests verify error behavior and migration guidance.

The tests correctly assert:

  1. isError is True for all deprecated tools
  2. Deprecation messages contain migration guidance to recommended alternatives

This ensures MCP clients receive clear feedback when using deprecated tools.

src/brainlayer/mcp/__init__.py (7)

85-95: Server instructions clearly communicate the 3-tool consolidation.

The updated instructions effectively convey:

  • brain_recall as the unified retrieval interface with auto-routing
  • brain_store for persistence with auto-detection
  • brain_digest for deep ingestion
  • Clear deprecation notice for old tools

This helps LLM clients understand the preferred API surface.


536-681: brain_recall schema comprehensively documents all modes and parameters.

The expanded schema:

  • Clearly documents auto-detection behavior in the description
  • Adds all necessary filter parameters for mode=search
  • Includes entity_type for mode=entity
  • Maintains backward-compatible parameters (hours, days, limit, session_id, plan_name)

The mode enum at line 568 correctly includes all 8 modes: search, entity, context, sessions, operations, plan, summary, stats.


1040-1064: brain_search correctly delegates to brain_recall with mode="search".

The thin alias pattern is well-implemented:

  • Explicitly sets mode="search" (line 1044)
  • Forwards all filter parameters from the schema
  • Maintains timeout wrapper for read operations

1154-1209: Deprecation stubs correctly return errors with clear migration guidance.

The change from fake success to explicit errors (isError=True) is an improvement:

  • Previous behavior (per learnings): These tools were stub implementations "returning fake success and saving nothing"
  • New behavior: Returns explicit error with actionable migration instructions

Migration paths are well-documented:

  • brain_expandbrain_recall(chunk_id='...') or brain_search(chunk_id='...')
  • brain_updatebrain_store(supersedes='...') or brain_supersede/brain_archive
  • brain_tagsbrain_recall(tag='...') or brain_store(tags=[...])

Based on learnings: "BrainBar Swift daemon tools brain_update, brain_expand, brain_tags are STUB implementations returning fake success and saving nothing; do not rely on these tools."


1169-1177: brain_entity correctly delegates to brain_recall with mode="entity".

The delegation passes only the relevant parameters for entity mode:

  • mode="entity"
  • query (required)
  • entity_type (optional filter)

This matches the entity handler's signature and keeps the alias thin.


1016-1020: Mode completions correctly include all modes.

The completion list at line 1017 includes all 8 modes with the new primary modes (search, entity) listed first for discoverability, followed by legacy modes (context, sessions, etc.).


1113-1142: brain_recall call_tool handler forwards all parameters correctly.

All Phase 1 parameters are forwarded:

  • Legacy parameters: mode, project, hours, days, limit, session_id, plan_name
  • New search parameters: query, file_path, chunk_id, content_type, source, tag, intent, importance_min, date_from, date_to, sentiment, entity_id, num_results, before, after, max_results, detail
  • New entity parameter: entity_type

Comment on lines +338 to +374
def _smart_detect_mode(query: str | None, mode: str | None) -> str:
"""Detect the best recall mode from query text when mode is not explicit.

Heuristics:
- "stats" / "how many" / "count" → stats
- "context" / "right now" / "working on" → context
- Capitalized proper-noun pattern (e.g. "BrainLayer", "Etan Heyman") → entity
- Default → search
"""
if mode is not None:
return mode
if not query:
return "context"

q_lower = query.lower().strip()

# Stats signals
if any(sig in q_lower for sig in ("stats", "how many", "count", "statistics", "total chunks")):
return "stats"

# Context signals
if any(sig in q_lower for sig in ("what am i working on", "right now", "current context", "what's active")):
return "context"

# Entity signals: query is a short capitalized proper-noun phrase (1-3 words, all start uppercase)
words = query.strip().split()
if 1 <= len(words) <= 3 and all(w[0].isupper() for w in words if len(w) > 0):
# Single-word common English words that aren't entities
_COMMON_WORDS = frozenset({
"The", "How", "What", "When", "Where", "Why", "Who", "Which",
"Find", "Search", "Get", "Show", "List", "Tell", "Give",
})
if len(words) == 1 and words[0] in _COMMON_WORDS:
return "search"
return "entity"

return "search"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Smart mode detection logic is well-structured with clear heuristics.

The _smart_detect_mode function correctly implements the documented routing rules. A few observations:

  1. Line 364: The entity detection condition all(w[0].isupper() for w in words if len(w) > 0) safely handles empty strings via the length check.

  2. Edge case coverage: The _COMMON_WORDS frozenset handles common capitalized words that shouldn't trigger entity mode.

One minor edge case: a query like "The BrainLayer" (2 words, both capitalized) would match the entity pattern and bypass the common-word check since _COMMON_WORDS only gates single-word queries. This may produce unexpected entity routing for phrases starting with "The".

💡 Optional: Extend common-word filtering to multi-word phrases
     # Entity signals: query is a short capitalized proper-noun phrase (1-3 words, all start uppercase)
     words = query.strip().split()
     if 1 <= len(words) <= 3 and all(w[0].isupper() for w in words if len(w) > 0):
         # Single-word common English words that aren't entities
         _COMMON_WORDS = frozenset({
             "The", "How", "What", "When", "Where", "Why", "Who", "Which",
             "Find", "Search", "Get", "Show", "List", "Tell", "Give",
         })
         if len(words) == 1 and words[0] in _COMMON_WORDS:
             return "search"
+        # Multi-word phrases starting with common words like "The" are likely not entities
+        if len(words) > 1 and words[0] in _COMMON_WORDS:
+            return "search"
         return "entity"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/brainlayer/mcp/search_handler.py` around lines 338 - 374, In
_smart_detect_mode, multi-word queries like "The BrainLayer" get classified as
entity because the _COMMON_WORDS check only applies to single-word queries;
update the logic to also reject phrases whose first word is a common leading
capitalized word: after computing words and before returning "entity", if
words[0] is in _COMMON_WORDS (or its lowercase/normalized variant), return
"search" so phrases that start with "The", "How", etc. don't incorrectly route
to entity; keep the existing single-word special-case intact.

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.

1 participant