> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dedaluslabs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Tools

> List and call tools on MCP servers

Tools are server-side functions that a client can execute with arguments. With `MCPClient`, the basic flow is:

## List tools

Discover available tools on the server:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from dedalus_mcp.client import open_connection

async with open_connection("http://127.0.0.1:8000/mcp") as client:
    tools = await client.list_tools()

    for tool in tools.tools:
        print(f"{tool.name}: {tool.description}")
```

## Tool schema

Each tool includes:

| Field         | Type   | Description               |
| ------------- | ------ | ------------------------- |
| `name`        | `str`  | Tool identifier           |
| `description` | `str`  | What the tool does        |
| `inputSchema` | `dict` | JSON Schema for arguments |

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
for tool in tools.tools:
    print(f"Name: {tool.name}")
    print(f"Description: {tool.description}")
    print(f"Parameters schema: {tool.inputSchema}")
```

## Call tools

Execute a tool with arguments:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
result = await client.call_tool("add", {"a": 5, "b": 3})

# Tool results are content blocks; most tools return TextContent.
print(result.content[0].text)
```

## Error handling

If the server returns a JSON-RPC error (common when a tool raises), `call_tool(...)` raises `McpError`:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from mcp.shared.exceptions import McpError

try:
    result = await client.call_tool("divide", {"a": 10, "b": 0})
    print(result.content[0].text)
except McpError as e:
    print(f"Tool call failed: {e}")
```

## Example: Calculator

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import asyncio
from dedalus_mcp.client import MCPClient
from mcp.shared.exceptions import McpError

async def main():
    client = await MCPClient.connect("http://127.0.0.1:8000/mcp")
    try:
        tools = await client.list_tools()
        print("Available tools:")
        for tool in tools.tools:
            print(f"  - {tool.name}")

        add_result = await client.call_tool("add", {"a": 5, "b": 3})
        print(f"5 + 3 = {add_result.content[0].text}")

        mul_result = await client.call_tool("multiply", {"a": 4, "b": 7})
        print(f"4 * 7 = {mul_result.content[0].text}")

    except McpError as e:
        print(f"Tool call failed: {e}")
    finally:
        await client.close()

asyncio.run(main())
```

## Context manager

`open_connection(...)` is an async context manager. It means you don't have to remember to call await `client.close()`. When the async with block exits, it automatically closes the underlying connection for you.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from dedalus_mcp.client import open_connection

async with open_connection("http://127.0.0.1:8000/mcp") as client:
    tools = await client.list_tools()
    result = await client.call_tool("greet", {"name": "World"})
    print(result.content[0].text)
```
