Files
Luigi Pederzani b45d9663ec feat(python): introduce roots support in MCPClient and Server (#751)
* fix(python): expose client capabilities to MCP servers

Add list_roots_callback support to Python connectors to properly
advertise the roots capability during MCP initialization handshake.

Previously, the Python library was not exposing any client capabilities
because the list_roots_callback was not being passed to ClientSession.
This fix ensures the roots capability is always advertised, bringing
the Python library in line with the TypeScript implementation.

Changes:
- Add roots and list_roots_callback params to BaseConnector
- Add get_roots() and set_roots() methods for managing roots
- Pass list_roots_callback to ClientSession in all connectors
- Export Root type from connectors module
- Update unit tests to expect list_roots_callback parameter

Closes MCP-1063

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(python): add roots and list_roots_callback params to connectors

- Add roots and list_roots_callback parameters to StdioConnector,
  HttpConnector, and SandboxConnector constructors
- Add comprehensive unit tests for client capabilities exposure
- Tests verify roots capability, get_roots(), set_roots(), and
  that all callbacks are correctly passed to ClientSession

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: fix import sorting and update API documentation

- Fix import sorting in test_client_capabilities.py (ruff)
- Regenerate API documentation for updated connector classes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(python): add list_roots() method to server Context

Allow MCP servers to request the list of roots from connected clients
via ctx.list_roots(). This follows the same pattern as ctx.sample()
and ctx.elicit() for server-to-client requests.

* feat(python): add roots params to MCPClient and config

- Add roots and list_roots_callback parameters to MCPClient.__init__,
  from_dict(), and from_config_file() methods
- Update create_connector_from_config() to pass roots params to all
  connector types (Stdio, HTTP, Sandbox)
- Allows clients to configure roots at the MCPClient level and have
  them propagate to all sessions

* test(python): add get_client_roots tool to primitive server

Add a tool that demonstrates the roots capability by requesting and
returning the list of roots from the connected client. Used for
integration testing of the roots feature.

* test(python): add integration tests for roots capability

Add comprehensive integration tests for the roots feature:
- test_roots_capability: Verify server can request/receive roots
- test_roots_empty_by_default: Verify empty roots when not provided
- test_roots_with_custom_callback: Verify custom callback precedence
- test_set_roots_updates_server_view: Verify dynamic root updates

* test(python): update unit tests for roots parameters

Update mock assertions in test_client.py to expect the new roots
and list_roots_callback parameters in create_connector_from_config
calls.

* ci(python): add roots to primitive tests and README badge

- Add roots to CI workflow primitive test matrix
- Add roots to PR comment report primitives list
- Add Roots badge to README Primitives row

* feat(docs): enhance MCP client and server documentation for roots feature

- Added `roots` and `list_roots_callback` parameters to MCP client initialization documentation.
- Updated `create_connector_from_config` documentation to include new parameters.
- Introduced `list_roots` method documentation in server context, detailing its usage and return values.
- Improved overall clarity and completeness of API reference for better developer guidance.

* feat(python): add get_client_roots tool to context example

- Introduced a new tool, `get_client_roots`, to the context example, demonstrating how to retrieve and display the list of roots exposed by the client.
- Updated documentation to include the new tool and its functionality, enhancing the example's coverage of context features.

* feat(docs): add roots documentation for client and server

- Introduced new documentation for the `roots` feature, detailing how clients can expose file system roots to MCP servers and how servers can access these roots.
- Added examples for configuring roots in the MCP client and using the `list_roots()` method in the server context.
- Enhanced overall clarity and usability of the documentation to support developers in implementing the roots functionality.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: pietrozullo <pietro.zullo@gmail.com>
2026-01-12 11:16:09 +01:00

143 lines
5.2 KiB
Python

"""
Configuration loader for MCP session.
This module provides functionality to load MCP configuration from JSON files.
"""
import json
from typing import Any
from mcp.client.session import ElicitationFnT, ListRootsFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
from mcp.types import Root
from mcp_use.client.connectors.base import BaseConnector
from mcp_use.client.connectors.http import HttpConnector
from mcp_use.client.connectors.sandbox import SandboxConnector, SandboxOptions
from mcp_use.client.connectors.stdio import StdioConnector
from mcp_use.client.connectors.utils import is_stdio_server
from mcp_use.client.connectors.websocket import WebSocketConnector
from mcp_use.client.middleware import Middleware
from mcp_use.telemetry.telemetry import Telemetry
_telemetry = Telemetry()
def load_config_file(filepath: str) -> dict[str, Any]:
"""Load a configuration file.
Args:
filepath: Path to the configuration file
Returns:
The parsed configuration
"""
with open(filepath) as f:
return json.load(f)
def create_connector_from_config(
server_config: dict[str, Any],
sandbox: bool = False,
sandbox_options: SandboxOptions | None = None,
sampling_callback: SamplingFnT | None = None,
elicitation_callback: ElicitationFnT | None = None,
message_handler: MessageHandlerFnT | None = None,
logging_callback: LoggingFnT | None = None,
middleware: list[Middleware] | None = None,
verify: bool | None = True,
roots: list[Root] | None = None,
list_roots_callback: ListRootsFnT | None = None,
) -> BaseConnector:
"""Create a connector based on server configuration.
This function can be called with just the server_config parameter:
create_connector_from_config(server_config)
Args:
server_config: The server configuration section
sandbox: Whether to use sandboxed execution mode for running MCP servers.
sandbox_options: Optional sandbox configuration options.
sampling_callback: Optional sampling callback function.
Returns:
A configured connector instance
"""
# Stdio connector (command-based)
if is_stdio_server(server_config) and not sandbox:
_telemetry.track_connector_init(
connector_type="stdio",
server_command=server_config["command"],
server_args=server_config["args"],
public_identifier=f"stdio:{server_config['command']} {' '.join(server_config['args'])}",
)
return StdioConnector(
command=server_config["command"],
args=server_config["args"],
env=server_config.get("env", None),
sampling_callback=sampling_callback,
elicitation_callback=elicitation_callback,
message_handler=message_handler,
logging_callback=logging_callback,
middleware=middleware,
roots=roots,
list_roots_callback=list_roots_callback,
)
# Sandboxed connector
elif is_stdio_server(server_config) and sandbox:
_telemetry.track_connector_init(
connector_type="sandbox",
server_command=server_config["command"],
server_args=server_config["args"],
public_identifier=f"sandbox:{server_config['command']} {' '.join(server_config['args'])}",
)
return SandboxConnector(
command=server_config["command"],
args=server_config["args"],
env=server_config.get("env", None),
e2b_options=sandbox_options,
sampling_callback=sampling_callback,
elicitation_callback=elicitation_callback,
message_handler=message_handler,
logging_callback=logging_callback,
middleware=middleware,
roots=roots,
list_roots_callback=list_roots_callback,
)
# HTTP connector
elif "url" in server_config:
_telemetry.track_connector_init(
connector_type="http",
server_url=server_config["url"],
public_identifier=f"http:{server_config['url']}",
)
return HttpConnector(
base_url=server_config["url"],
headers=server_config.get("headers", None),
auth=server_config.get("auth", {}),
timeout=server_config.get("timeout", 5),
sse_read_timeout=server_config.get("sse_read_timeout", 60 * 5),
sampling_callback=sampling_callback,
elicitation_callback=elicitation_callback,
message_handler=message_handler,
logging_callback=logging_callback,
middleware=middleware,
verify=verify,
roots=roots,
list_roots_callback=list_roots_callback,
)
# WebSocket connector
elif "ws_url" in server_config:
_telemetry.track_connector_init(
connector_type="websocket",
server_url=server_config["ws_url"],
public_identifier=f"websocket:{server_config['ws_url']}",
)
return WebSocketConnector(
url=server_config["ws_url"],
headers=server_config.get("headers", None),
auth=server_config.get("auth", {}),
)
raise ValueError("Cannot determine connector type from config")