Skip to main content
The useCallTool hook provides a type-safe way to call MCP tools from within your widgets. When paired with automatic type generation, it gives you full TypeScript IntelliSense for tool inputs and outputs.
Automatic Type Inference: When using mcp-use dev, types are automatically generated from your tool schemas. The useCallTool hook uses these types to provide IntelliSense for tool names, inputs, and outputs.

Import

import { useCallTool } from "mcp-use/react";

Basic Usage

import { useCallTool } from "mcp-use/react";

const MyWidget = () => {
  // Tool name is type-checked, input/output are fully typed
  const { callTool, data, isPending, isSuccess, isError, error } = 
    useCallTool("search-flights");

  return (
    <div>
      <button onClick={() => callTool({ destination: "NYC" })}>
        Search Flights
      </button>
      {isPending && <p>Searching...</p>}
      {isSuccess && (
        <ul>
          {data.structuredContent.flights.map(flight => (
            <li key={flight.id}>{flight.price}</li>
          ))}
        </ul>
      )}
      {isError && <p>Error: {error.message}</p>}
    </div>
  );
};
Type Safety: The hook automatically infers types from your tool definitions. You get autocomplete for:
  • Available tool names
  • Tool input parameters
  • Tool output structure
  • Error types

State Management

The hook uses a discriminated union state machine similar to TanStack Query:
type State =
  | { status: "idle"; data: undefined; error: undefined }
  | { status: "pending"; data: undefined; error: undefined }
  | { status: "success"; data: CallToolResult; error: undefined }
  | { status: "error"; data: undefined; error: Error }
This ensures type-safe access to data and error based on the current status.

Return Values

PropertyTypeDescription
status"idle" | "pending" | "success" | "error"Current state of the tool call
isIdlebooleanTrue when no call has been made
isPendingbooleanTrue while the tool is executing
isSuccessbooleanTrue when the tool call succeeded
isErrorbooleanTrue when the tool call failed
dataCallToolResult | undefinedTool result (only available when isSuccess)
errorError | undefinedError object (only available when isError)
callToolFunctionFire-and-forget method with callbacks
callToolAsyncFunctionPromise-based method for async/await

Calling Methods

Fire-and-Forget with Callbacks

Use callTool for non-blocking calls with optional callbacks:
const { callTool } = useCallTool("create-booking");

callTool(
  { flightId: "123", passenger: "John Doe" },
  {
    onSuccess: (data) => {
      console.log("Booking confirmed:", data.structuredContent);
      showNotification("Booking successful!");
    },
    onError: (error) => {
      console.error("Booking failed:", error);
      showError(error.message);
    },
    onSettled: (data, error, input) => {
      console.log("Request complete", { data, error, input });
      hideLoadingSpinner();
    }
  }
);
Callback Types:
CallbackSignatureDescription
onSuccess(data: CallToolResult, input: any) => voidCalled when tool succeeds
onError(error: Error, input: any) => voidCalled when tool fails
onSettled(data?, error?, input: any) => voidCalled after success or error

Async/Await Pattern

Use callToolAsync for sequential operations:
const { callToolAsync } = useCallTool("search-flights");

const handleSearch = async () => {
  try {
    const result = await callToolAsync({ 
      destination: "Tokyo", 
      date: "2024-12-01" 
    });
    
    console.log("Found flights:", result.structuredContent.flights);
    setFlights(result.structuredContent.flights);
  } catch (error) {
    console.error("Search failed:", error);
    showError(error.message);
  }
};
Use callToolAsync when you need to:
  • Chain multiple tool calls sequentially
  • Handle results in a try/catch block
  • Perform actions that depend on the result

Response Structure

Tool calls return a CallToolResult object:
interface CallToolResult {
  content: Array<TextContent | ImageContent | EmbeddedResource>;
  isError?: boolean;
  
  // Structured content (if tool returned it)
  structuredContent?: any;
  
  // MCP Apps specific
  metadata?: Record<string, unknown>;
}
Accessing structured content:
const { data } = useCallTool("get-weather");

if (data) {
  // Structured content is strongly typed based on your tool definition
  const weather = data.structuredContent;
  console.log(weather.temperature, weather.conditions);
}

Type Generation Integration

The hook automatically uses types from .mcp-use/tool-registry.d.ts: Server tool definition:
// index.ts
server.tool({
  name: "search-flights",
  description: "Search for flights",
  schema: z.object({
    destination: z.string(),
    date: z.string().optional(),
  }),
  execute: async ({ destination, date }) => {
    // ... implementation
    return object({
      flights: array(object({
        id: string(),
        price: number(),
      })),
    });
  }
});
Generated types:
// .mcp-use/tool-registry.d.ts (auto-generated)
declare module "mcp-use/react" {
  interface ToolRegistry {
    "search-flights": {
      input: { destination: string; date?: string };
      output: { flights: Array<{ id: string; price: number }> };
    };
  }
}
Widget with full type safety:
const { callTool, data } = useCallTool("search-flights");
//                                      ^ Autocomplete for tool names

// Autocomplete for input parameters
callTool({ destination: "NYC", date: "2024-12-01" });

// Strongly typed output
if (data) {
  data.structuredContent.flights.forEach(flight => {
    //                   ^ Autocomplete for output structure
    console.log(flight.id, flight.price);
  });
}

Advanced: Manual Type Generation

For scenarios without mcp-use dev, use generateHelpers():
import { generateHelpers } from "mcp-use/react";

type MyToolMap = {
  "search-flights": {
    input: { destination: string; date?: string };
    output: { flights: Array<{ id: string; price: number }> };
  };
  "book-flight": {
    input: { flightId: string };
    output: { confirmation: string };
  };
};

const { useCallTool } = generateHelpers<MyToolMap>();

// Now useCallTool is typed with your custom tool map
const { callTool, data } = useCallTool("search-flights");

Environment Support

The hook works in both protocols:
Uses JSON-RPC over postMessage to communicate with the MCP Apps client.
The protocol is detected automatically - your code stays identical.

Error Handling

Handle errors using the state flags or callbacks:
const { callTool, isError, error } = useCallTool("create-booking");

// State-based error handling
if (isError) {
  return <div className="error">{error.message}</div>;
}

// Callback-based error handling
callTool(input, {
  onError: (error) => {
    if (error.message.includes("unavailable")) {
      showNotification("Flight is no longer available");
    } else {
      showError("An error occurred. Please try again.");
    }
  }
});

Complete Example

import { useCallTool } from "mcp-use/react";
import { useState } from "react";

const FlightSearchWidget = () => {
  const [destination, setDestination] = useState("");
  const { callTool, data, isPending, isError, error } = 
    useCallTool("search-flights");

  const handleSearch = () => {
    callTool(
      { destination },
      {
        onSuccess: (result) => {
          console.log(`Found ${result.structuredContent.flights.length} flights`);
        },
        onError: (err) => {
          console.error("Search failed:", err);
        }
      }
    );
  };

  return (
    <div className="widget">
      <input
        type="text"
        value={destination}
        onChange={(e) => setDestination(e.target.value)}
        placeholder="Enter destination"
        disabled={isPending}
      />
      <button onClick={handleSearch} disabled={isPending || !destination}>
        {isPending ? "Searching..." : "Search Flights"}
      </button>

      {isError && (
        <div className="error">Error: {error.message}</div>
      )}

      {data && (
        <ul className="results">
          {data.structuredContent.flights.map((flight) => (
            <li key={flight.id}>
              {flight.departure}{flight.arrival} - ${flight.price}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
};

See Also