feat: 3-tool simplification Phase 1 — unified brain_recall aliases#111
feat: 3-tool simplification Phase 1 — unified brain_recall aliases#111
Conversation
…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>
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
📝 WalkthroughWalkthroughThe changes consolidate MCP memory retrieval tools under Changes
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (4)
src/brainlayer/mcp/__init__.pysrc/brainlayer/mcp/search_handler.pytests/test_3tool_aliases.pytests/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 usepaths.py:get_db_path()for database path resolution instead of hardcoding paths
Preserve verbatim content for message types:ai_code,stack_trace,user_messageduring classification and chunking
Skipnoisecontent entirely; summarizebuild_log; extract structure only fromdir_listingduring chunking
Use AST-aware chunking via tree-sitter; never split stack traces; mask large tool output
Implement retry logic onSQLITE_BUSYerrors; each worker must use its own database connection
Override enrichment backend viaBRAINLAYER_ENRICH_BACKENDenvironment variable (valid values:ollama,mlx,groq); default to Groq
Configure enrichment rate viaBRAINLAYER_ENRICH_RATEenvironment variable (default: 0.2 = 12 RPM)
Checkpoint WAL before and after bulk database operations:PRAGMA wal_checkpoint(FULL)
Drop FTS triggers before bulk deletes fromchunkstable; 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; useinclude_archived=Trueparameter to show history
Lint and format code withruff 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.pytests/test_3tool_aliases.pysrc/brainlayer/mcp/search_handler.pysrc/brainlayer/mcp/__init__.py
**/*test*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use
pytestfor testing
Files:
tests/test_smart_search_entity_dedup.pytests/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.pytests/test_3tool_aliases.pysrc/brainlayer/mcp/search_handler.pysrc/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
Nonebefore calling_infer_recall_mode(lines 430-432) correctly ensures that only explicitly-setdays/limitparameters trigger "sessions" mode inference, matching pre-existing behavior.
437-459: Search mode delegation correctly validates and forwards all parameters.The validation that
queryis required (line 438-439) and the comprehensive parameter forwarding to_brain_searchensures consistent behavior whether called directly or via thebrain_recalldispatcher.
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)fromentity_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:
- Asserts
brain_expandreturnsisError: Truewith a deprecation message (lines 231-232)- 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:
isError is Truefor all deprecated tools- 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_recallas the unified retrieval interface with auto-routingbrain_storefor persistence with auto-detectionbrain_digestfor 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_typeformode=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_expand→brain_recall(chunk_id='...')orbrain_search(chunk_id='...')brain_update→brain_store(supersedes='...')orbrain_supersede/brain_archivebrain_tags→brain_recall(tag='...')orbrain_store(tags=[...])Based on learnings: "BrainBar Swift daemon tools
brain_update,brain_expand,brain_tagsare 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
| 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" |
There was a problem hiding this comment.
🧹 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:
-
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. -
Edge case coverage: The
_COMMON_WORDSfrozenset 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.
Summary
brain_recallgainsmode=searchandmode=entitywith smart auto-detection from query text (capitalized proper nouns → entity, "stats"/"how many" → stats, default → search)brain_searchandbrain_entitybecome thin aliases delegating tobrain_recallbrain_update,brain_expand,brain_tagsnow returnisError: truewith deprecation messages (were previously broken stubs returning fake success)searchandentityAcceptance Criteria
brain_recallgainsmodeparameter:search(default),context,entity,summary,statsbrain_recall({ query: "topic" })behaves identically to currentbrain_search("topic")brain_recall({ query: "BrainLayer", mode: "entity" })behaves identically to currentbrain_entity("BrainLayer")brain_searchandbrain_entitybecome thin aliases delegating tobrain_recalltools/liststill exposes all tools (backward compatible)brain_update,brain_expand,brain_tagsstubs returnisError: truewith deprecation messageTest plan
pytest tests/test_3tool_aliases.py -v— 35/35 passbrain_recall(query="test")via MCP client returns search resultsbrain_recall(query="BrainLayer", mode="entity")returns entity infoWorker: 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_recallin init.py and search_handler.py now acceptssearchandentitymodes, plus aqueryparam and rich filters (entity_type, tag, date range, sentiment, etc.).brain_searchandbrain_entitybecome thin aliases incall_tool, delegating to_brain_recallwith the appropriate mode and forwarding all params._smart_detect_modeheuristic 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, andbrain_tagsnow returnisError=Truedeprecation messages instead of executing prior behavior.brain_update,brain_expand, orbrain_tagswill receive errors rather than results after this change.Macroscope summarized 1cfc1b6.
Summary by CodeRabbit
New Features
brain_recalltool with dedicated search and entity lookup modesDeprecations
brain_expand,brain_update, andbrain_tagstools as deprecatedChanges
brain_searchandbrain_entitynow operate as simplified aliases delegating to the primarybrain_recalltool