Skip to content

Comments

feat: Let stdio MCP honor per-project cloud routing#590

Merged
phernandez merged 1 commit intomainfrom
feat/hybrid-mcp-routing-stdio
Feb 20, 2026
Merged

feat: Let stdio MCP honor per-project cloud routing#590
phernandez merged 1 commit intomainfrom
feat/hybrid-mcp-routing-stdio

Conversation

@phernandez
Copy link
Member

Summary

  • Stdio transport (bm mcp) no longer forces local routing, allowing per-project cloud routing to work (local projects stay local, cloud projects route to cloud)
  • HTTP/SSE transports continue to force local routing since they serve as local API endpoints
  • Externally-set env vars (e.g. BASIC_MEMORY_FORCE_CLOUD=true) are honored for stdio, preserving escape hatches

Test plan

  • 4 new transport-specific routing tests (stdio no-force, stdio honors external env, streamable-http forces local, sse forces local)
  • Existing routing unit tests pass (tests/cli/test_routing.py, tests/mcp/test_async_client_modes.py)
  • Full just fast-check passes (2171 tests, lint, typecheck)

🤖 Generated with Claude Code

HTTP/SSE transports continue to force local routing since they serve as
local API endpoints. Stdio no longer injects BASIC_MEMORY_FORCE_LOCAL or
BASIC_MEMORY_EXPLICIT_ROUTING, allowing the natural resolution order
(factory > explicit routing > per-project mode > default local) to apply.

Externally-set env vars (e.g. BASIC_MEMORY_FORCE_CLOUD) are honored for
stdio, preserving escape hatches for cloud deployments.

- Conditional env-var logic by transport in mcp.py
- Replace 1 test with 4 transport-specific routing tests
- Update SPEC-PER-PROJECT-ROUTING.md and README.md
- Incidental ruff formatting fixes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
@github-actions
Copy link
Contributor

github-actions bot commented Feb 20, 2026

Claude finished @phernandez's task in 2m 20s —— View job


PR Review: feat/hybrid-mcp-routing-stdio

  • Read changed files and gather context
  • Review code quality & standards
  • Review testing & documentation
  • Review architecture & security
  • Post final review summary

Summary

This PR makes a targeted, correct change: bm mcp --transport stdio no longer injects BASIC_MEMORY_FORCE_LOCAL or BASIC_MEMORY_EXPLICIT_ROUTING, allowing the natural per-project routing resolution to apply. HTTP/SSE transports still force local routing. The implementation is clean and well-reasoned.


Code Quality & Standards

  • Follows Basic Memory's coding conventions in CLAUDE.md
  • Python 3.12+ type annotations and async patterns
  • FastAPI and Typer conventions followed
  • 100-character line length limit maintained
  • No commented-out code blocks
  • Literate programming style — decision-point comment in mcp.py is excellent:
# Trigger: MCP server command invocation.
# Why: HTTP/SSE transports serve as local API endpoints and must never
#      route through cloud. Stdio is a client-facing protocol that
#      should honor per-project routing (local or cloud).
# Outcome: HTTP/SSE get explicit local override; stdio passes through
#          whatever env vars are already set (honoring external overrides)
#          and defaults to per-project routing resolution.

Minor inconsistency: The routing check on line 56 uses if transport in ("streamable-http", "sse"): (preferred tuple containment), but the run dispatch on line 91 uses elif transport == "streamable-http" or transport == "sse":. These should be consistent. Fix this →


Testing & Documentation

  • Unit tests for new functions/methods
  • 4 new transport-specific routing tests covering all cases
  • Edge case: test_mcp_stdio_honors_external_env_override verifies externally-set env vars pass through — good
  • Test isolation via monkeypatch.delenv is correct
  • Removed unused EVENT_PROMO_OPTED_OUT import from promo.py and test file — correct cleanup
  • SPEC-PER-PROJECT-ROUTING.md updated with MCP transport routing section
  • README.md updated

Documentation inconsistency in spec: docs/SPEC-PER-PROJECT-ROUTING.md line 111 still reads:

3. Show an explicit `MCP (stdio)` target column that always resolves to `local`.

This is now incorrect — stdio resolves based on per-project mode, not always to local. This line should be updated (e.g., "that resolves per project mode"). Fix this →


Basic Memory Architecture

  • Preserves local-first architecture principles
  • Per-project routing resolution order is respected (factory > explicit override > per-project mode > default local)
  • HTTP/SSE local-only guarantee is maintained
  • BASIC_MEMORY_EXPLICIT_ROUTING=true is correctly set for HTTP/SSE (prevents per-project mode from overriding forced-local)
  • No database changes, no migrations needed

The "do nothing for stdio" approach is the right design: not injecting env vars is cleaner than injecting and then removing them.


Security & Performance

  • No hardcoded secrets or credentials
  • No new input validation concerns introduced
  • No sensitive data in logs
  • Fire-and-forget analytics thread pattern is unchanged and safe

Compatibility

  • No file path comparisons introduced
  • No new emoji/unicode in console or log output

Verdict

The core change is correct, minimal, and well-tested. Two non-blocking items to address:

  1. (Minor) Style consistency: elif transport in (...) vs or on line 91 of mcp.py
  2. (Minor) Spec doc: line 111 of SPEC-PER-PROJECT-ROUTING.md still says stdio "always resolves to local" — should be updated to reflect per-project routing

Neither blocks merging, but the spec inaccuracy could cause confusion for future contributors. Recommend fixing before merge.

@phernandez phernandez merged commit 30499a9 into main Feb 20, 2026
26 checks passed
@phernandez phernandez deleted the feat/hybrid-mcp-routing-stdio branch February 20, 2026 07:43
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