Skip to content

feat(SESF-86): take the current project from the client's MCP roots - #98

Merged
lbruton merged 3 commits into
mainfrom
fix/SESF-86-mcp-roots
Oct 30, 2026
Merged

lbruton merged 3 commits into
mainfrom
fix/SESF-86-mcp-roots

Conversation

@lbruton

@lbruton lbruton commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

SESF-86. The read tools take the current project from the client's MCP roots when the call names none.

Why

Claude Desktop Code-tab sessions connect before they enter the project, so their X-Project-Root header is empty and SESF-85 refuses their unscoped calls. A spike against a throwaway stateful server showed:

Client Roots support roots/list answer
Claude Code CLI 2.1.312 yes, listChanged its working directory
Desktop Code tab 2.1.309 yes, listChanged the project folder, at connect and at call time
Codex 0.163.0 no empty

Both Claude Code clients re-initialized by themselves after a server restart.

Changes

  • http_server.py: StreamableHTTPSessionManager(stateless=False, json_response=False). The roots request travels on the tool call's response stream, which needs stateful, streamed sessions.
  • client_roots.py (new): asks roots/list once per MCP session (5 s cap), keeps the first file:// root, and forgets every answer on notifications/roots/list_changed. Clients that declare no roots are not asked.
  • tools.py: scope order is project_root, then client roots, then the header, then refusal. The Scope: line says from client roots.
  • hooks/memory-session-start.py: carries Mcp-Session-Id, reads SSE replies and deletes its session after each call. It still works against a stateless server.
  • Docs: CLAUDE.md, AGENTS.md, README, DocVault Overview.

Deploy order

  1. Copy the hook to ~/.claude/hooks/ and ~/.codex/hooks/. The old hook breaks against the stateful server; the new one works against both.
  2. Restart the server with launchctl kickstart -k.

Tests

  • tests/test_client_roots.py: URI parsing, caching, timeout, errors, list_changed, and source order over a real in-memory MCP session.
  • tests/test_memory_session_start.py: a stateful mode in the fake server, plus an end-to-end run of the hook against the SDK's stateful manager under uvicorn.
  • ruff check . clean; 846 passed, 120 skipped.

Desktop chat (mcp-remote bridge) and OpenCode are untested: OpenCode's API key has expired.

Summary by CodeRabbit

  • New Features
    • Project-scoped searches can now use the MCP client’s first available local project folder when no project is specified. An explicit project selection takes precedence, with the project header used as a fallback.
    • MCP tool calls now support stateful streamed sessions, including session reuse and cleanup. If a session expires or the server restarts, the client must initialize a new session.
  • Documentation
    • Updated setup guidance to explain client project roots, session handling, and project-scope behavior.

Claude Desktop Code-tab sessions connect before they enter the project,
so their X-Project-Root header is empty and the read tools refused calls
that named no project. A spike showed that Claude Code (CLI 2.1.312 and
the Desktop Code tab 2.1.309) answers roots/list with the project folder,
both at connect and at call time, and re-initializes after a server
restart. Codex 0.163.0 declares no roots.

- http_server: run stateful, streamed MCP sessions so a tool call can
  ask the client for its roots.
- client_roots: ask roots/list once per MCP session on the tool call's
  stream (5 s cap), keep the first file:// root, forget on
  notifications/roots/list_changed.
- tools: scope order is project_root, then client roots, then the
  header, then refusal; the Scope line says "from client roots".
- hooks/memory-session-start.py: carry Mcp-Session-Id, read SSE
  replies, delete the session after each call. It still works against
  a stateless server, so install the hook before restarting.
- Docs: CLAUDE.md, AGENTS.md, README and Overview.
@lbruton

lbruton commented Oct 30, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor
✅ 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.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-30T03:55:51.618040Z 59b5f4f New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration

Walkthrough

Changes

Client roots and stateful MCP sessions

Layer / File(s) Summary
Client-root scope resolution
client_roots.py, tools.py, tests/test_client_roots.py, AGENTS.md, CLAUDE.md
The server validates and caches the first local MCP root per session. Search and statistics tools use it before X-Project-Root when project_root is absent.
Stateful streamed MCP server
http_server.py, tests/test_client_roots.py
The server registers root-change handling and uses stateful streamed sessions instead of stateless JSON responses.
Session-aware memory hook
hooks/memory-session-start.py, tests/test_memory_session_start.py
The hook retains session IDs, sends session headers, parses JSON or SSE responses, and deletes sessions after tool calls.
Scope and session guidance
README.md, AGENTS.md, CLAUDE.md
The documentation describes MCP-root scope selection, header fallback, and streamed session behavior.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant MemoryHook
  participant HTTPServer
  participant MCPClient
  MemoryHook->>HTTPServer: initialize stateful MCP session
  HTTPServer-->>MemoryHook: return session ID and streamed response
  MemoryHook->>HTTPServer: send tool call with session ID
  HTTPServer->>MCPClient: request client roots when scope is needed
  MCPClient-->>HTTPServer: return first local file root
  HTTPServer-->>MemoryHook: return SSE tool result
  MemoryHook->>HTTPServer: close session
Loading

Possibly related PRs

Merge Risk: Minimal · previous review c0886

The supplied evidence does not establish a blocker to merging. Client compatibility remains worth checking during rollout.

Pre-merge checks: Passed 4
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: deriving the current project from the client's MCP roots.
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.
Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

Roots arrive through streams of light
Sessions stay through day and night
Scope checks roots, then headers fall
SSE carries answers to all
Closed sessions leave no trace behind

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

@codacy-production

codacy-production Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 1 minor

Results:
1 new issue

Category Results
CodeStyle 1 minor

View in Codacy

🟢 Metrics 110 complexity · 1 duplication

Metric Results
Complexity 110
Duplication 1

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@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: c0886e85c0

ℹ️ 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 tools.py Resolved
A stateful session task keeps the context of its initialize request,
so the ProjectMiddleware ContextVar held the first request's header.
The scoped tools and cleanup_sessions now read the header from
server.request_context.request and fall back to the ContextVar only
when there is no HTTP request (Codex review).

@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: 59b5f4f625

ℹ️ 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 http_server.py Resolved
Sessions whose client exits without a DELETE are freed after 30
minutes. The value matches the mcp 1.30 default, but the documented
expiry no longer depends on it (Codex review).
@lbruton
lbruton merged commit 1f2e5fb into main Oct 30, 2026
10 checks passed
@lbruton
lbruton deleted the fix/SESF-86-mcp-roots branch October 30, 2026 04:45
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