Skip to content

feat: add Core-4 MCP tool palette#594

Merged
EtanHey merged 11 commits into
mainfrom
feat/core4-palette
Jul 14, 2026
Merged

feat: add Core-4 MCP tool palette#594
EtanHey merged 11 commits into
mainfrom
feat/core4-palette

Conversation

@EtanHey

@EtanHey EtanHey commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • Make brain_search, brain_store, brain_recall, and brain_expand the default BrainLayer MCP palette in both BrainBar/Swift and Python.
  • Keep every existing handler available through BRAINLAYER_MCP_PROFILE=full (or operator) and through the idempotent expand_palette control tool.
  • Advertise tools.listChanged, reject deferred canonical tools before expansion, and retain canonical full-profile schemas/annotations unchanged.
  • Add exact UTF-8 byte and o200k_base token measurement tooling, budget tests, profile/expansion round-trip tests, and contract documentation.
  • No DB/schema changes.

Context-budget receipt

Inventory Tools Bytes o200k tokens
Deployed pre-change baseline from rethink audit 17 4,038 1,027
New repository default: Core 4 + expand_palette 5 1,478 368
Repository full BrainBar profile 17 11,462 2,606

The new default is 2,560 bytes / 659 tokens smaller than the deployed baseline: 63.4% byte reduction and 64.2% token reduction. The verbose repository full-profile figure is reported separately because it is not the same artifact as the deployed audit capture.

The current canonical repository Core-4 schemas were 3,722 bytes before compact projection, so the default projection preserves names and validation-relevant input-schema structure while omitting nested descriptions and annotations. Full/operator output remains canonical.

Verification

All test commands were run with ulimit -n 4096 for the decisive receipts.

  • Pre-push regression gate (Python 3.12): 3,528 passed, 9 skipped, 61 deselected, 1 xfailed; then 3/3 MCP registration, 40/40 isolated eval/hook routing, 1/1 Bun, and FTS5 determinism shell gate all passed.
  • Local Python suite: 3,595 passed, 9 skipped, 78 deselected, 1 xfailed, 329 warnings. The extra deselection is the unrelated backup suite-order flake noted below; it passes alone and passed in the raised-fd pre-push gate.
  • Swift: 750 executed, 0 failures, 2 skipped.
  • Focused profile/palette/measurement checks: 12 Python passed; focused router/socket regression checks: 93 Swift passed.
  • Ruff: clean for all changed Python files.
  • git diff --check: clean.

Unrelated baseline / tooling notes

  • TestStoreAutoEnrich is a pre-existing untouched-main baseline failure per standing direction. It was not investigated or changed. It did not reproduce in the raised-fd local or pre-push runs.
  • One local full-suite run hit test_brainbar_vacuum_request_accepts_valid_target_after_lost_response because that test monkeypatches the shared stdlib time.sleep while a background 0.25-second loop is alive. The test and implementation are unchanged from origin/main; it passed alone and in the raised-fd pre-push gate. No unrelated fix is included.
  • An initial push-gate invocation inherited the low fd limit and emitted Too many open files; the complete gate was rerun from ulimit -n 4096 and passed.
  • Local CodeRabbit CLI review was attempted twice; it first repeated a minor environment-test isolation observation that was addressed, then hit the free OSS rate limit. Remote review is requested below.

Risk / rollout

  • This changes default tool exposure, not handler availability or data contracts.
  • full and operator are immediate compatibility escape hatches; expand_palette upgrades the active palette without restart and is idempotent.
  • No daemon deployment/restart or merge is part of this PR endpoint.

Note

Medium Risk
Changes default tool visibility for all MCP clients; full/operator env and expand_palette mitigate breakage, but clients assuming the full list without profile or expansion will see fewer tools and failed calls until upgraded.

Overview
Default MCP sessions now advertise a compact Core-4 palette (brain_search, brain_store, brain_recall, brain_expand) plus expand_palette, instead of the full BrainBar/Python tool inventories at connect time. BRAINLAYER_MCP_PROFILE selects behavior: missing/blank/core (and unknown values fail closed to core), full / operator expose the complete canonical set with no control tool.

BrainBar adds per-socket PaletteSession state, routes tools/list and tools/call through session-aware exposure checks, and sets tools.listChanged: true. Server-handled tools (brain_subscribe, etc.) follow the same defer-until-expanded rules. Python adds ToolPalette with matching profile resolution, exposure guards, and idempotent expand_palette receipts.

expand_palette promotes only that session to full tools; repeat calls are no-ops. Deferred tools error until expansion. Canonical handler definitions are unchanged; core listings use compact schema projections to meet the boot budget. Adds measure_mcp_palette.py, contract/docs updates, and broad test coverage (profile, expansion, per-client isolation, byte budget).

Reviewed by Cursor Bugbot for commit 0a86e9b. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Core-4 MCP tool palette with per-session expansion across Swift and Python transports

  • Introduces a ToolPalette abstraction (palette.py) and MCPRouter.PaletteSession (MCPRouter.swift) that restrict the default tool set to four core tools plus an expand_palette control tool when BRAINLAYER_MCP_PROFILE is unset, core, or unrecognized.
  • Full and operator profiles expose all tools (17 in Swift, 13 in Python) unchanged; unknown/missing profile values fail closed to the core set.
  • Calling expand_palette once per session promotes the client to the full tool inventory; subsequent calls return already_expanded without changing state. BrainBar advertises tools.listChanged=true so clients can refresh via tools/list.
  • Core tool definitions are compacted (descriptions and nested schema descriptions stripped) in BrainBar to stay within a 1,500-byte boot budget.
  • Risk: tools that were previously always available (brain_subscribe, brain_unsubscribe, brain_ack in BrainBar; all non-core tools in Python) now return JSON-RPC -32601 until expand_palette is called in core profile.

Macroscope summarized 0a86e9b.

Summary by CodeRabbit

  • New Features

    • Added configurable MCP tool profiles with a compact core palette and full/operator modes.
    • Added an in-session “expand palette” action to reveal additional tools without reconnecting.
    • Added tool-list change notifications so clients can refresh available tools.
    • Added palette measurement tooling for JSON size and token counts.
  • Documentation

    • Documented profile behavior, expansion semantics, compatibility requirements, and size limits.
  • Tests

    • Added coverage for profile resolution, expansion behavior, deferred tool access, and payload measurements.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@cursor

cursor Bot commented Jul 13, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_eb3c6713-dac7-4500-baff-adddc9084620)

@EtanHey

EtanHey commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@EtanHey

EtanHey commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

@BugBot review

@EtanHey

EtanHey commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@EtanHey

EtanHey commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

@cursor review

@cursor

cursor Bot commented Jul 13, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_ffc80501-161c-4a97-9911-8d478e9c1800)

@cursor

cursor Bot commented Jul 13, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_bc4a011b-cd00-41dd-9a9c-67af7ac149b0)

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

MCP tool exposure now supports core and full profiles, session-level palette expansion, exposed-tool validation, and list-change signaling in BrainBar and Python. Contracts, design plans, measurement tooling, dependencies, and tests were updated for the new behavior.

Changes

MCP palette behavior

Layer / File(s) Summary
Profile contract and palette state
contracts/engine-ui-contract.md, docs/plans/..., src/brainlayer/mcp/palette.py, brain-bar/Sources/BrainBar/MCPRouter.swift
Defines profile resolution, core tool inventories, fail-closed handling, and idempotent session expansion.
Python MCP palette wiring
src/brainlayer/mcp/__init__.py
Separates full tool construction from palette-based listing and gates calls to currently exposed tools.
BrainBar router exposure and dispatch
brain-bar/Sources/BrainBar/MCPRouter.swift
Adds dynamic listing, expand_palette handling, tools.listChanged, exposed-call validation, and centralized result construction.
Palette measurement CLI
pyproject.toml, scripts/measure_mcp_palette.py
Adds deterministic tool extraction and reports tool count, UTF-8 bytes, and o200k_base tokens with optional byte-limit enforcement.
Palette and compatibility validation
brain-bar/Tests/BrainBarTests/*, tests/test_mcp_palette.py, tests/test_measure_mcp_palette.py, tests/test_*
Covers profile inventories, expansion receipts, environment isolation, measurement behavior, and full-definition schema assertions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant MCPServer
  participant ToolPalette
  Client->>MCPServer: tools/list
  MCPServer->>ToolPalette: expose full tool definitions
  ToolPalette-->>MCPServer: exposed core or full tools
  MCPServer-->>Client: tools list
  Client->>MCPServer: tools/call expand_palette
  MCPServer->>ToolPalette: expand palette
  ToolPalette-->>MCPServer: expansion receipt
  MCPServer-->>Client: structured receipt and updated list state
Loading

Possibly related PRs

Suggested reviewers: bhanuprasad14

Poem

I’m a rabbit with tools in a neat little row,
Core ones appear, then the full set can grow.
“Expand!” says the client; the palette replies,
With stable receipts and refreshed tool skies.
Tests hop behind, keeping each promise bright.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.46% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: introducing a Core-4 MCP tool palette with profile-based exposure and expansion support.
✨ 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/core4-palette

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.

Comment thread brain-bar/Sources/BrainBar/MCPRouter.swift Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1e4ace1f6f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread brain-bar/Sources/BrainBar/MCPRouter.swift Outdated
@cursor

cursor Bot commented Jul 13, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_c1991af7-8ec6-4e23-8762-4dcee7492d55)

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@brain-bar/Sources/BrainBar/MCPRouter.swift`:
- Around line 201-223: Update compactCoreToolDefinition for brain_recall and
brain_expand to preserve the anthropic/maxResultSizeChars annotation with value
250,000 while retaining the existing description and inputSchema compaction.
Confirm the resulting Core4 definitions still satisfy the enforced 1,500-byte
budget; if they do not, use the smallest targeted representation that preserves
this hint for these two tools without restoring unrelated annotations.
- Around line 399-413: Update toolCallResult to protect reserved response keys
when merging output.metadata. Define or reuse a reserved-key set covering
content, isError, jsonrpc, id, and result, then filter or assert metadata
entries before assigning them so metadata cannot overwrite the response body or
JSON-RPC fields.
🪄 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: 5c1b0537-62bb-4535-8b60-a381165b9e6e

📥 Commits

Reviewing files that changed from the base of the PR and between ff34c77 and 1e4ace1.

📒 Files selected for processing (22)
  • brain-bar/Sources/BrainBar/MCPRouter.swift
  • brain-bar/Tests/BrainBarTests/MCPRouterTests.swift
  • brain-bar/Tests/BrainBarTests/SocketIntegrationTests.swift
  • contracts/engine-ui-contract.md
  • docs/plans/2026-07-13-brainlayer-core4-palette-design.md
  • docs/plans/2026-07-13-brainlayer-core4-palette.md
  • pyproject.toml
  • scripts/measure_mcp_palette.py
  • src/brainlayer/mcp/__init__.py
  • src/brainlayer/mcp/palette.py
  • tests/test_3tool_aliases.py
  • tests/test_audit_search_quality.py
  • tests/test_entity_type_sync.py
  • tests/test_mcp_digest_modes.py
  • tests/test_mcp_input_schema_limits.py
  • tests/test_mcp_palette.py
  • tests/test_measure_mcp_palette.py
  • tests/test_phase3_digest.py
  • tests/test_search_filter_params.py
  • tests/test_think_recall_integration.py
  • tests/test_tool_annotations.py
  • tests/test_tool_description_quality.py
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: swift (macos-15)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.12)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.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

Files:

  • tests/test_think_recall_integration.py
  • tests/test_search_filter_params.py
  • tests/test_tool_annotations.py
  • tests/test_mcp_palette.py
  • tests/test_phase3_digest.py
  • tests/test_audit_search_quality.py
  • tests/test_entity_type_sync.py
  • src/brainlayer/mcp/palette.py
  • tests/test_mcp_digest_modes.py
  • tests/test_mcp_input_schema_limits.py
  • scripts/measure_mcp_palette.py
  • tests/test_tool_description_quality.py
  • tests/test_3tool_aliases.py
  • tests/test_measure_mcp_palette.py
  • src/brainlayer/mcp/__init__.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Do not run tests/test_vector_store.py or tests/test_engine.py during worker pre-push checks; run them only as deliberate production-database checks.

Files:

  • tests/test_think_recall_integration.py
  • tests/test_search_filter_params.py
  • tests/test_tool_annotations.py
  • tests/test_mcp_palette.py
  • tests/test_phase3_digest.py
  • tests/test_audit_search_quality.py
  • tests/test_entity_type_sync.py
  • tests/test_mcp_digest_modes.py
  • tests/test_mcp_input_schema_limits.py
  • tests/test_tool_description_quality.py
  • tests/test_3tool_aliases.py
  • tests/test_measure_mcp_palette.py
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.py: Use paths.py:get_db_path() for database path resolution in all scripts and CLI code.
Retry on SQLITE_BUSY; each worker must use its own database connection.

Files:

  • src/brainlayer/mcp/palette.py
  • src/brainlayer/mcp/__init__.py
src/brainlayer/mcp/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/brainlayer/mcp/**/*.py: Use the fixed-size read-only WAL VectorStore pool; respect BRAINLAYER_READ_POOL_SIZE, BRAINLAYER_READ_BUSY_TIMEOUT_MS, and the approximately 768 MB pool/cache startup limit.
Add new MCP tool implementations in the mcp/ directory and expose them through the brainlayer-mcp entrypoint.

Files:

  • src/brainlayer/mcp/palette.py
  • src/brainlayer/mcp/__init__.py
src/brainlayer/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/brainlayer/**/*.py: Preserve ai_code, stack_trace, and user_message verbatim; skip noise, summarize build logs, and retain only structure for directory listings.
Use AST-aware tree-sitter chunking, never split stack traces, and mask large tool output.
Use Groq as the primary enrichment backend, Gemini as fallback, and Ollama as the offline last resort; honor BRAINLAYER_ENRICH_BACKEND and BRAINLAYER_ENRICH_RATE.
Default searches must exclude lifecycle-managed chunks; include_archived=True may show history.
brain_supersede must apply a safety gate to personal data, and brain_archive must perform a timestamped soft delete.
brain_store must support an atomic supersedes store-and-replace operation.
Production backup logs must identify real runs with backup_log_provenance=real; tests must use BRAINLAYER_BACKUP_LOG_PATH and provenance pytest so they cannot modify the production heartbeat log.
Before bulk database operations, stop enrichment workers, checkpoint WAL before and after with PRAGMA wal_checkpoint(FULL), drop and recreate FTS triggers around large deletes, batch deletes in 5–10K chunks, and checkpoint every three batches.

Files:

  • src/brainlayer/mcp/palette.py
  • src/brainlayer/mcp/__init__.py
🧠 Learnings (2)
📚 Learning: 2026-03-18T00:12:08.774Z
Learnt from: EtanHey
Repo: EtanHey/brainlayer PR: 87
File: brain-bar/Sources/BrainBar/BrainBarServer.swift:118-129
Timestamp: 2026-03-18T00:12:08.774Z
Learning: In Swift files under brain-bar/Sources/BrainBar, enforce that when a critical dependency like the database is nil due to startup ordering (socket before DB), any tool handler that accesses the database must throw an explicit error (e.g., ToolError.noDatabase) instead of returning a default/empty value. Do not allow silent defaults (e.g., guard let db else { return ... }). Flag patterns that silently return defaults when db is nil, as this masks startup timing issues. This guidance applies broadly to similar Swift files in the BrainBar module, not just this one location.

Applied to files:

  • brain-bar/Sources/BrainBar/MCPRouter.swift
📚 Learning: 2026-03-29T18:45:40.988Z
Learnt from: EtanHey
Repo: EtanHey/brainlayer PR: 133
File: brain-bar/Sources/BrainBar/BrainDatabase.swift:0-0
Timestamp: 2026-03-29T18:45:40.988Z
Learning: In the BrainBar module’s Swift database layer (notably BrainDatabase.swift), ensure that the `search()` function’s `unreadOnly=true` path orders results by the delivery frontier cursor so the watermark `maxRowID` stays contiguous. Specifically, when `unreadOnly` is enabled, the query must include `ORDER BY c.rowid ASC` (e.g., via `let orderByClause = unreadOnly ? "c.rowid ASC" : "f.rank"`). Do not replace the unread-only ordering with relevance-based sorting (e.g., `f.rank`) unconditionally or for the unread-only path, as it can introduce gaps in the watermark and incorrectly mark unseen rows as delivered. Flag any future change to the `ORDER BY` clause in this function that makes relevance sorting apply to the unread-only case.

Applied to files:

  • brain-bar/Sources/BrainBar/MCPRouter.swift
🪛 ast-grep (0.44.1)
scripts/measure_mcp_palette.py

[info] 36-36: use jsonify instead of json.dumps for JSON output
Context: json.dumps(tools, ensure_ascii=False, separators=(",", ":"), sort_keys=True)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)


[info] 66-66: use jsonify instead of json.dumps for JSON output
Context: json.dumps(measurement, sort_keys=True)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

tests/test_measure_mcp_palette.py

[info] 16-16: use jsonify instead of json.dumps for JSON output
Context: json.dumps(tools, ensure_ascii=False, separators=(",", ":"), sort_keys=True)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)


[info] 35-35: use jsonify instead of json.dumps for JSON output
Context: json.dumps({"tools": [{"name": "brain_search"}]})
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

src/brainlayer/mcp/__init__.py

[info] 1337-1337: use jsonify instead of json.dumps for JSON output
Context: json.dumps(receipt, sort_keys=True)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

🪛 LanguageTool
docs/plans/2026-07-13-brainlayer-core4-palette-design.md

[style] ~90-~90: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... database, schema, or writer changes. - No deletion or renaming of compatibility t...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~91-~91: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...n or renaming of compatibility tools. - No attempt to make profile changes affect ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🪛 markdownlint-cli2 (0.23.0)
docs/plans/2026-07-13-brainlayer-core4-palette.md

[warning] 13-13: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)

🔇 Additional comments (29)
src/brainlayer/mcp/__init__.py (4)

4-4: LGTM!

Also applies to: 44-44, 401-402


1333-1340: 🎯 Functional Correctness

Verify that expansion emits tools/list_changed.

This path changes list_tools() results but only returns a receipt. If the MCP server does not synthesize the notification automatically, connected clients will retain the core-only tool cache after expansion. Confirm the SDK behavior and add a socket-level test; emit the notification explicitly if required.


1255-1262: 🎯 Functional Correctness

Global palette state is fine here. serve() runs one stdio MCP server process, and ToolPalette is the intended session state for list_tools()/expand_palette, so there’s no cross-client palette leak in this setup.

			> Likely an incorrect or invalid review comment.

1342-1343: 🎯 Functional Correctness

Legacy brainlayer_* aliases are intentional compatibility shims. The core-profile gate applies to canonical tools; the alias dispatch is expected to remain callable for backward compatibility.

			> Likely an incorrect or invalid review comment.
pyproject.toml (1)

85-85: LGTM!

scripts/measure_mcp_palette.py (1)

1-75: LGTM!

contracts/engine-ui-contract.md (1)

20-44: LGTM!

docs/plans/2026-07-13-brainlayer-core4-palette-design.md (1)

1-92: LGTM!

docs/plans/2026-07-13-brainlayer-core4-palette.md (1)

1-304: LGTM!

src/brainlayer/mcp/palette.py (1)

26-71: 🩺 Stability & Availability

Confirm ToolPalette is never mutated off the event loop.

_expanded is set on self with no lock (Line 66). This is safe only if expand()/is_exposed()/expose() always run on the single asyncio event loop thread with no interleaving await inside them (true here — none of these methods await). If the MCP server ever dispatches call_tool through a thread pool or multiple event-loop workers share one _tool_palette instance, this becomes an unguarded read-modify-write on _expanded.

As per coding guidelines: "Flag risky DB or concurrency changes explicitly and do not hand-wave lock behavior." Please confirm (or add a comment noting) that _tool_palette is a per-process singleton consumed only on the asyncio event loop thread.

Source: Coding guidelines

brain-bar/Sources/BrainBar/MCPRouter.swift (1)

13-38: LGTM!

Also applies to: 123-125, 329-329, 349-349, 368-384

brain-bar/Tests/BrainBarTests/MCPRouterTests.swift (4)

61-81: LGTM!

Also applies to: 111-138, 140-161


165-170: LGTM!

Also applies to: 191-204, 213-230


232-2597: LGTM!


206-211: 🎯 Functional Correctness

Drop the deferred-tool mismatch concern BrainBar returns a JSON-RPC -32601 for an unexposed tool, while Python returns a CallToolResult with isError=True and a “not exposed” message. The shared palette contract only covers which tools are exposed and how expand_palette behaves.

			> Likely an incorrect or invalid review comment.
tests/test_mcp_palette.py (1)

14-36: LGTM!

tests/test_measure_mcp_palette.py (1)

1-43: LGTM!

tests/test_think_recall_integration.py (1)

245-254: LGTM!

tests/test_tool_annotations.py (1)

69-72: LGTM!

tests/test_tool_description_quality.py (1)

1-15: LGTM!

brain-bar/Tests/BrainBarTests/SocketIntegrationTests.swift (1)

15-38: 🩺 Stability & Availability

No issue: BRAINLAYER_MCP_PROFILE is restored per test, and SwiftPM/XCTest parallelism runs tests in separate processes, so these mutations don’t create a cross-test race.

			> Likely an incorrect or invalid review comment.
tests/test_3tool_aliases.py (2)

17-19: LGTM!


22-29: 🎯 Functional Correctness

brain_expand stays a deprecation stub. CORE_TOOL_NAMES still lists it for palette exposure, but call_tool() returns the deprecated error path, so test_brain_expand_returns_deprecation_error matches the runtime behavior.

			> Likely an incorrect or invalid review comment.
tests/test_audit_search_quality.py (1)

125-127: LGTM!

tests/test_entity_type_sync.py (1)

31-34: LGTM!

tests/test_mcp_input_schema_limits.py (1)

8-14: LGTM!

Also applies to: 66-67

tests/test_phase3_digest.py (1)

289-350: LGTM!

tests/test_search_filter_params.py (1)

806-808: LGTM!

tests/test_mcp_digest_modes.py (1)

2-2: 🎯 Functional Correctness

No remaining asyncio usage — this file only uses @pytest.mark.asyncio, so removing the import is safe.

			> Likely an incorrect or invalid review comment.

Comment thread brain-bar/Sources/BrainBar/MCPRouter.swift
Comment thread brain-bar/Sources/BrainBar/MCPRouter.swift
@cursor

cursor Bot commented Jul 13, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_1709e326-2648-4fa4-a415-127ea354af22)

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@EtanHey

EtanHey commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

Independent standing-reviewer verdict (s:399) — SHIP ✅ (with one required fast-follow)

Reviewed from an isolated worktree at head 0a86e9b1. Core is correct + thoroughly tested + CI-green:

  1. Core-4 default + deferred tail — palette.py Core-4 + expand_palette; Python tests 12/12: core/None/""/whitespace/bogus → expose only Core-4 + expand (fail-closed); deferred tool before expand → isError "not exposed".
  2. Env seamBRAINLAYER_MCP_PROFILE core|full|operator, resolved-once, unknown→core+warn.
  3. expand_palette — round-trips (→13 tools, deferred now dispatchable) + idempotent.
  4. Deprecation-safe — full/operator preserve all 13; defer-not-remove.
  5. Per-client isolation + server-handler gate (your reviewer fixes) — each socket client gets its own PaletteSession (not global); server-handled brain_subscribe/unsubscribe/ack obey the same isToolExposed(session:) gate. Red→green Swift regressions present for both.
  6. No DB/schema changes — MCP surface + tests + docs only.
  7. Pre-existing TestStoreAutoEnrich — documented + untouched.

CI green (Python 3.11/3.12/3.13, Swift 750, lint, CodeRabbit, Macroscope).

Required fast-follow (the one gap): brief item 5's byte-budget REGRESSION GUARD is not wired — measure_mcp_palette.py + --max-bytes exist and are tested, but no test/CI asserts the CORE palette ≤1500 B (no fixture, no --max-bytes 1500 core invocation). The receipt's "Exact budget guard is ≤1,500 B" overstates it (manual capability, not enforced). Add test_core_palette_stays_within_budget or a CI step.
Caveat: the −63.4% savings is on the deployed BrainBar surface — not locally reproduced (no Swift build/fixture). The 5-of-17 exposure mechanism is verified by the Python tests; exact bytes are documented/audit-basis'd (PR body honestly distinguishes deployed vs verbose-full).

reviewDecision=REVIEW_REQUIRED + self-approval unavailable → proceeding with an admin merge (merge commit, no squash — R-010) per the #593 approval-block pattern; verification + disclosed gaps recorded in a .verified artifact.

@EtanHey EtanHey merged commit fb675ff into main Jul 14, 2026
9 checks passed
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