Direct Tool Calls

MCP-Use allows you to call MCP server tools directly without needing an LLM or agent. This is useful when you want to use MCP servers as a simple interface to various tools and APIs, or when you need programmatic control over tool execution.

When to Use Direct Tool Calls

Direct tool calls are appropriate when:
  • You know exactly which tool to call and with what parameters
  • You don’t need an LLM to make decisions about tool selection
  • You want to integrate MCP tools into existing Python applications
  • You need deterministic, programmatic control over tool execution
Direct tool calls will not work for tools that require sampling/completion, as these need an LLM to generate responses.

Basic Example

Here’s how to call tools directly using the MCPClient:
import asyncio
from mcp_use import MCPClient

async def call_tool_example():
    # Configure the MCP server
    config = {
        "mcpServers": {
            "everything": {
                "command": "npx",
                "args": ["-y", "@modelcontextprotocol/server-everything"],
            }
        }
    }

    # Create client from configuration
    client = MCPClient.from_dict(config)

    try:
        # Initialize all configured sessions
        await client.create_all_sessions()

        # Get the session for a specific server
        session = client.get_session("everything")

        # List available tools
        tools = await session.list_tools()
        tool_names = [t.name for t in tools]
        print(f"Available tools: {tool_names}")

        # Call a specific tool with arguments
        result = await session.call_tool(
            name="add",
            arguments={"a": 1, "b": 2}
        )

        # Handle the result
        if getattr(result, "isError", False):
            print(f"Error: {result.content}")
        else:
            print(f"Tool result: {result.content}")
            print(f"Text result: {result.content[0].text}")

    finally:
        # Clean up resources
        await client.close_all_sessions()

if __name__ == "__main__":
    asyncio.run(call_tool_example())

Working with Tool Results

The call_tool method returns a CallToolResult object with the following attributes:
  • content: A list of ContentBlock objects containing the tool’s output
  • structuredContent: A dictionary with the structured result (for non-sampling tools)
  • isError: Boolean indicating if the tool call encountered an error

Accessing Results

# Call a tool
result = await session.call_tool(
    name="get_weather",
    arguments={"location": "San Francisco"}
)

# Check for errors
if result.isError:
    print(f"Tool error: {result.content}")
else:
    # Access text content
    text_result = result.content[0].text
    print(f"Weather: {text_result}")

    # Access structured content if available
    if hasattr(result, 'structuredContent'):
        structured = result.structuredContent
        print(f"Temperature: {structured.get('temperature')}")

Multiple Server Example

You can work with multiple MCP servers and call tools from each:
import asyncio
from mcp_use import MCPClient

async def multi_server_example():
    config = {
        "mcpServers": {
            "filesystem": {
                "command": "npx",
                "args": ["-y", "@modelcontextprotocol/server-filesystem"],
                "env": {"FILE_PATH": "/tmp"}
            },
            "time": {
                "command": "npx",
                "args": ["-y", "@modelcontextprotocol/server-time"]
            }
        }
    }

    client = MCPClient.from_dict(config)

    try:
        await client.create_all_sessions()

        # Call tool from filesystem server
        fs_session = client.get_session("filesystem")
        files = await fs_session.call_tool(
            name="list_files",
            arguments={"path": "/tmp"}
        )
        print(f"Files: {files.content[0].text}")

        # Call tool from time server
        time_session = client.get_session("time")
        current_time = await time_session.call_tool(
            name="get_current_time",
            arguments={}
        )
        print(f"Current time: {current_time.content[0].text}")

    finally:
        await client.close_all_sessions()

if __name__ == "__main__":
    asyncio.run(multi_server_example())

Discovering Available Tools

Before calling tools, you may want to discover what’s available:
async def discover_tools():
    client = MCPClient.from_dict(config)
    await client.create_all_sessions()

    try:
        session = client.get_session("my_server")

        # Get all tools
        tools = await session.list_tools()

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

            # Print input schema if available
            if hasattr(tool, 'inputSchema'):
                print(f"  Parameters: {tool.inputSchema}")
            print()

    finally:
        await client.close_all_sessions()

Error Handling

Always handle potential errors when making direct tool calls:
async def safe_tool_call():
    try:
        result = await session.call_tool(
            name="some_tool",
            arguments={"param": "value"}
        )

        if result.isError:
            # Handle tool-specific errors
            error_msg = result.content[0].text if result.content else "Unknown error"
            print(f"Tool returned error: {error_msg}")
            return None

        return result.content[0].text

    except Exception as e:
        # Handle connection or other errors
        print(f"Failed to call tool: {e}")
        return None

Limitations

When using direct tool calls, be aware of these limitations:
  1. No Sampling Support: Tools that require sampling/completion (like text generation) won’t work without an LLM
  2. Manual Tool Selection: You need to know which tool to call - there’s no automatic selection
  3. No Context Management: Unlike agents, direct calls don’t maintain conversation context
  4. Parameter Validation: You’re responsible for providing correct parameters

Complete Example

View the complete working example in the repository: examples/direct_tool_call.py

Next Steps