# Server entrypoint
async def main():
    async with stdio_server() as (read_stream, write_stream):
        await mcp.run(
            read_stream,
            write_stream,
            InitializationOptions(
                server_name="roots-example-server",
                server_version="0.1.0",
                capabilities=mcp.get_capabilities(
                    notification_options=NotificationOptions(),
                    experimental_capabilities={},
                ),
            ),
        )

if __name__ == "__main__":
    asyncio.run(main())
```

### Client implementation

```python filename="client-example.py"
from fastmcp import Client
from fastmcp.client.transports import FastMCPTransport
import asyncio

async def client_example():
    # Connect to a running MCP server and specify the roots we want to use
    async with Client(transport_url="stdio:", roots=["roots://list"]) as client:
        # Call a tool on the server
        result = await client.call_tool("process_file", {"filename": "data.csv"})
        print(result)

if __name__ == "__main__":
    asyncio.run(client_example())
```

In these examples:

- The server is initialized with a descriptive name.
- Roots are defined using the `@app.resource` decorator with `roots://list`.
- A simple tool demonstrates how tools can be used within the scope of roots.
- The client specifies which roots it wants to use when connecting to the server.
- The client calls a tool on the server, which will operate within the boundaries defined by roots.

## Implementation patterns

Let's look at some examples of how we can use MCP roots within a server.

### File system access

When granting access to file system directories, always verify that accessed paths are within approved roots:

```python
def read_file(file_path):
    # Convert to absolute path
    abs_path = os.path.abspath(file_path)

    # Check if path is within any approved root
    for root in app.roots:
        if root.uri.startswith("file://"):
            root_path = root.uri.replace("file://", "")
            if abs_path.startswith(root_path):
                # Safe to read
                with open(abs_path, 'r') as f:
                    return f.read()

    # Not within any approved root
    raise SecurityError(f"Access denied: {file_path} is outside approved roots")
```

### API endpoint access

Similarly, when making API calls, make sure they respect root boundaries:

```python
async def make_api_call(endpoint, method="GET", data=None):
    # Check if endpoint is within an approved root
    for root in app.roots:
        if root.uri.startswith("https://") and endpoint.startswith(root.uri):
            # Safe to proceed
            async with httpx.AsyncClient() as client:
                response = await client.request(method, endpoint, json=data)
                return response.json()

    # Not within any approved root
    raise SecurityError(f"Access denied: {endpoint} is outside approved API roots")
```

### Tool integration

MCP roots also work with MCP tools. If a tool is granted access to a root, it can only operate on files within that root:

```python
@app.tool("create_file")
async def create_file(path: str, content: str):
    # Convert to absolute path
    abs_path = os.path.abspath(path)

    # Check against allowed file system roots
    for root in app.current_roots:
        if root.uri.startswith("file://"):
            root_path = root.uri.replace("file://", "")
            if abs_path.startswith(root_path):
                # Write the file
                with open(abs_path, 'w') as f:
                    f.write(content)
                return {"success": True, "path": abs_path}

    return {
        "success": False,
        "error": "Permission denied: path is outside allowed roots"
    }
```

### Resource integration

Resources can also be filtered based on allowed roots:

```python
@app.on_resources_list()
async def list_resources():
    all_resources = await get_all_available_resources()

    # Filter to only include resources within allowed roots
    allowed_resources = []
    for resource in all_resources:
        for root in app.current_roots:
            if resource.uri.startswith(root.uri):
                allowed_resources.append(resource)
                break

    return allowed_resources
```

### Roots vs tools

MCP roots are distinct from [MCP tools](/mcp/tools), which are callable server-side actions. While MCP roots define where operations can occur, MCP tools define what actions can be performed.