Skip to main content

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 { MCPClient } from 'mcp-use'

async function callToolExample() {
    // Configure the MCP server
    const config = {
        mcpServers: {
            everything: {
                command: 'npx',
                args: ['-y', '@modelcontextprotocol/server-everything']
            }
        }
    }

    // Create client from configuration
    const client = new MCPClient(config)

    try {
        // Initialize all configured sessions
        await client.createAllSessions()

        // Get the session for a specific server
        const session = client.getSession('everything')

        // List available tools
        const tools = await session.listTools()
        const toolNames = tools.map(t => t.name)
        console.log(`Available tools: ${toolNames}`)

        // Call a specific tool with arguments
        const result = await session.callTool(
            'add',
            { a: 1, b: 2 }
        )

        // Handle the result
        if (result.isError) {
            console.error(`Error: ${result.content}`)
        } else {
            console.log(`Tool result: ${result.content}`)
            console.log(`Text result: ${result.content[0].text}`)
        }

    } finally {
        // Clean up resources
        await client.closeAllSessions()
    }
}

callToolExample().catch(console.error)

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
const result = await session.callTool(
    'get_weather',
    { location: 'San Francisco' }
)

// Check for errors
if (result.isError) {
    console.error(`Tool error: ${result.content}`)
} else {
    // Access text content
    const textResult = result.content[0].text
    console.log(`Weather: ${textResult}`)

    // Access structured content if available
    if (result.structuredContent) {
        const structured = result.structuredContent
        console.log(`Temperature: ${structured.temperature}`)
    }
}

Timeout Configuration

For long-running tools, especially those that trigger sampling or LLM operations, you may need to configure custom timeout settings:
// Extended timeout for long-running operations
const result = await session.callTool(
    'process_large_dataset',
    { dataset: 'large-data.json' },
    {
        timeout: 300000, // 5 minutes
    }
)
Available timeout options:
  • timeout: Request timeout in milliseconds (default: 60000 / 60 seconds)
  • maxTotalTimeout: Maximum total time in milliseconds, even with progress resets
  • resetTimeoutOnProgress: If true, resets the timeout counter when a progress notification is received (default: false)
  • signal: An AbortSignal to programmatically cancel the request
Note: When resetTimeoutOnProgress is enabled and the server sends progress notifications (like ctx.sample() does automatically), the operation can run indefinitely as long as progress is reported regularly.

Multiple Server Example

You can work with multiple MCP servers and call tools from each:
import { MCPClient } from 'mcp-use'

async function multiServerExample() {
    const config = {
        mcpServers: {
            filesystem: {
                command: 'npx',
                args: ['-y', '@modelcontextprotocol/server-filesystem'],
                env: { FILE_PATH: '/tmp' }
            },
            time: {
                command: 'npx',
                args: ['-y', '@modelcontextprotocol/server-time']
            }
        }
    }

    const client = new MCPClient(config)

    try {
        await client.createAllSessions()

        // Call tool from filesystem server
        const fsSession = client.getSession('filesystem')
        const files = await fsSession.callTool(
            'list_files',
            { path: '/tmp' }
        )
        console.log(`Files: ${files.content[0].text}`)

        // Call tool from time server
        const timeSession = client.getSession('time')
        const currentTime = await timeSession.callTool(
            'get_current_time',
            {}
        )
        console.log(`Current time: ${currentTime.content[0].text}`)

    } finally {
        await client.closeAllSessions()
    }
}

multiServerExample().catch(console.error)

Discovering Available Tools

Before calling tools, you may want to discover what’s available:
async function discoverTools() {
    const client = new MCPClient(config)
    await client.createAllSessions()

    try {
        const session = client.getSession('my_server')

        // Get all tools
        const tools = await session.listTools()

        for (const tool of tools) {
            console.log(`Tool: ${tool.name}`)
            console.log(`  Description: ${tool.description}`)

            // Print input schema if available
            if (tool.inputSchema) {
                console.log(`  Parameters: ${JSON.stringify(tool.inputSchema)}`)
            }
            console.log()
        }

    } finally {
        await client.closeAllSessions()
    }
}

Error Handling

Always handle potential errors when making direct tool calls:
async function safeToolCall() {
    try {
        const result = await session.callTool(
            'some_tool',
            { param: 'value' }
        )

        if (result.isError) {
            // Handle tool-specific errors
            const errorMsg = result.content?.[0]?.text || 'Unknown error'
            console.error(`Tool returned error: ${errorMsg}`)
            return null
        }

        return result.content[0].text

    } catch (error) {
        // Handle connection or other errors
        console.error(`Failed to call tool: ${error}`)
        return null
    }
}

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