Skip to content

[Client][Server] Add Roots support#395

Open
wachterjohannes wants to merge 3 commits into
modelcontextprotocol:mainfrom
wachterjohannes:eve/roots
Open

[Client][Server] Add Roots support#395
wachterjohannes wants to merge 3 commits into
modelcontextprotocol:mainfrom
wachterjohannes:eve/roots

Conversation

@wachterjohannes

@wachterjohannes wachterjohannes commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Adds support for the MCP Roots capability, letting a client expose file:// "workspace folders" that the server is allowed to operate on.

Client side

  • ListRootsRequestHandler + RootsCallbackInterface — answer server roots/list requests from a user-provided callback (mirrors the existing SamplingRequestHandler / SamplingCallbackInterface pattern).
  • Client::sendRootsListChanged() — send notifications/roots/list_changed. Guarded: it throws unless the client advertised the roots.listChanged capability, so a misconfigured client can't emit an undeclared notification.
  • Mcp\Exception\RootsException — thrown from a roots callback, its message is forwarded to the server (same contract as SamplingException); any other throwable is logged and returned as a generic internal error.

Server side

  • ClientGateway::listRoots() — request the client's roots (returns ListRootsResult, throws ClientException on an error response).
  • ClientGateway::supportsRoots() — capability-gating helper alongside supportsElicitation().

Schema

  • ListRootsResult::fromArray() — parse the client's response; Root continues to enforce the file:// URI scheme required by the spec.

Docs / examples / tests

  • README + docs/client.md sections and a runnable examples/client/stdio_roots.php.
  • Unit tests covering the handler, result parsing (incl. non-file:// rejection), capability serialization/round-trip, sendRootsListChanged() gating, and the ClientGateway roots methods.
  • CHANGELOG entry under 0.7.0.

Conformance notes

  • Root URIs are restricted to file:// per the current spec.
  • roots capability presence gates roots/list; roots.listChanged gates the list_changed notification.

wachterjohannes and others added 3 commits July 14, 2026 19:24
…y::listRoots)

Wire up the MCP "roots" capability on both sides:

Client (php-sdk as client):
- RootsCallbackInterface + ListRootsRequestHandler answer server roots/list
  requests, mirroring the Sampling handler.
- Client::sendRootsListChanged() emits notifications/roots/list_changed.

Server (php-sdk as server):
- ClientGateway::listRoots() requests the client's roots, and
  supportsRoots() reports whether the client advertised the capability.
- Add ListRootsResult::fromArray() (used by listRoots()).

Includes unit tests, a runnable examples/client/stdio_roots.php example,
and docs/README/CHANGELOG updates. phpunit, php-cs-fixer and phpstan green.

Co-Authored-By: Claude <noreply@anthropic.com>
0.6.0 is already released; the Roots additions belong under a new unreleased
0.7.0 section rather than the shipped 0.6.0.

Co-Authored-By: Claude <noreply@anthropic.com>
…rding, docs & tests

Applies the five findings from the PR #1 code review:

1. Spec conformance: examples/docs/README now advertise
   `new ClientCapabilities(roots: true, rootsListChanged: true)` before
   sending `notifications/roots/list_changed`.
2. `ClientGateway::supportsRoots()`/`supportsElicitation()` normalize the
   stored capabilities to an array, avoiding a TypeError when a client
   advertised an empty capabilities object (\stdClass).
3. `Client::sendRootsListChanged()` now throws when the client did not
   advertise the `roots.listChanged` capability.
4. Add tests: capability serialization/round-trip, sendRootsListChanged
   gating (sends when declared, throws otherwise), RootsException message
   forwarding, and rejection of non-file:// root URIs.
5. `ListRootsRequestHandler` forwards `RootsException` messages to the
   server (new Mcp\Exception\RootsException), mirroring SamplingRequestHandler.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012NrvzihxUvQzDSoWzjsm58
@chr-hertel chr-hertel added Client Issues & PRs related to the Client component Server Issues & PRs related to the Server component improves spec compliance Improves consistency with other SDKs such as TyepScript labels Jul 14, 2026
Comment on lines +49 to +52
$roots = array_map(
static fn (array $root): Root => Root::fromArray($root),
array_values($data['roots']),
);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

flaky in case roots doesn't carry an array as element => TypeError

@chr-hertel chr-hertel added the needs more work Not ready to be merged yet, needs additional follow-up from the author(s). label Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Client Issues & PRs related to the Client component improves spec compliance Improves consistency with other SDKs such as TyepScript needs more work Not ready to be merged yet, needs additional follow-up from the author(s). Server Issues & PRs related to the Server component

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants