ExMCP.Client (ex_mcp v0.9.0)
View SourceUnified MCP client combining the best features of all implementations.
This module provides a clean, consistent API for interacting with MCP servers while maintaining backward compatibility with existing code.
Features
- Simple connection with URL strings or transport specs
- Automatic transport fallback via TransportManager
- Consistent return values with optional normalization
- Convenience methods for common operations
- Clean separation of concerns
Examples
# Connect with URL
{:ok, client} = ExMCP.Client.connect("http://localhost:8080/mcp")
# Connect with transport spec
{:ok, client} = ExMCP.Client.start_link(
transport: :stdio,
command: "mcp-server"
)
# List and call tools
{:ok, %{"tools" => tools}} = ExMCP.Client.list_tools(client)
{:ok, result} = ExMCP.Client.call_tool(client, "weather", %{location: "NYC"})
Summary
Functions
Sends a batch of requests to the server.
Convenience alias for call_tool/4.
Calls a tool with the given arguments.
Returns a specification to start this module under a supervisor.
Requests completion suggestions from the server.
Connects to an MCP server using a URL or connection spec.
Disconnects the client gracefully, cleaning up all resources.
Finds a matching tool from a list of tools.
Finds a tool by name or pattern.
Gets the list of pending request IDs.
Gets a prompt with the given arguments.
Gets the client status.
Lists available prompts.
Lists available resource templates.
Lists available resources.
Lists available roots.
Lists available tools from the server.
Sends a log message to the server as a notification.
Sends a log message with additional data to the server as a notification.
Gets the negotiated protocol version with the server.
Sends a notification to the server.
Pings the server.
Reads a resource by URI.
Convenience alias for batch_request/3.
Sends a cancellation notification for a pending request.
Gets server capabilities.
Gets server information.
Sets the log level for the server.
Starts a client process with the given options.
Stops the client.
Subscribes to notifications for a resource.
Convenience alias for list_tools/2.
Unsubscribes from notifications for a resource.
Types
Functions
Sends a batch of requests to the server.
This function allows sending multiple requests in a single batch, which can be more efficient than sending them individually. The server processes the requests and returns a batch of responses.
Parameters
client- Client process referencerequests- A list of{method, params}tuples for each request.timeout- Timeout for the entire batch operation (default: 30_000).
Returns
{:ok, results}- On success, whereresultsis a list of{:ok, result}or{:error, error}tuples, in the same order as the original requests.{:error, reason}- If the batch request fails (e.g., timeout).
Example
requests = [
{"tools/list", %{}},
{"prompts/list", %{}}
]
{:ok, [tools_result, prompts_result]} = ExMCP.Client.batch_request(client, requests)
Convenience alias for call_tool/4.
Calls a tool with the given arguments.
Options
:timeout- Request timeout (default: 30000):format- Return format (:map or :struct, default: :map)
Returns a specification to start this module under a supervisor.
See Supervisor.
Requests completion suggestions from the server.
Sends a completion/complete request to get completion suggestions based on
a reference (prompt or resource) and partial input.
Parameters
client- Client process referenceref- Reference map describing what to complete:- For prompts:
%{"type" => "ref/prompt", "name" => "prompt_name"} - For resources:
%{"type" => "ref/resource", "uri" => "resource_uri"}
- For prompts:
argument- Argument map with completion context:%{"name" => "argument_name", "value" => "partial_value"}
Options
:timeout- Request timeout (default: 5000):format- Return format (:map or :struct, default: :map)
Returns
{:ok, result}- Success with completion suggestions:%{ completion: %{ values: ["suggestion1", "suggestion2", ...], total: 10, hasMore: false } }{:error, error}- Request failed with error details
Examples
# Complete prompt argument
{:ok, result} = ExMCP.Client.complete(
client,
%{"type" => "ref/prompt", "name" => "code_generator"},
%{"name" => "language", "value" => "java"}
)
# Complete resource URI
{:ok, result} = ExMCP.Client.complete(
client,
%{"type" => "ref/resource", "uri" => "file:///"},
%{"name" => "path", "value" => "/src"}
)
@spec connect( connection_spec(), keyword() ) :: {:ok, t()} | {:error, any()}
Connects to an MCP server using a URL or connection spec.
Examples
# URL string
{:ok, client} = ExMCP.Client.connect("http://localhost:8080/mcp")
# Transport spec
{:ok, client} = ExMCP.Client.connect({:stdio, command: "mcp-server"})
# Multiple transports with fallback
{:ok, client} = ExMCP.Client.connect([
"http://localhost:8080/mcp",
"stdio://mcp-server"
])
@spec disconnect(t()) :: :ok
Disconnects the client gracefully, cleaning up all resources.
This function performs a clean shutdown by:
- Closing the transport connection
- Cancelling health checks
- Stopping the receiver task
- Replying to any pending requests with an error
Examples
{:ok, client} = ExMCP.Client.connect("http://localhost:8080/mcp")
:ok = ExMCP.Client.disconnect(client)
@spec find_matching_tool([map()], String.t() | nil, keyword()) :: {:ok, map()} | {:error, :not_found}
Finds a matching tool from a list of tools.
Parameters
tools- List of tool mapsname- Tool name to find (exact match) or pattern (fuzzy match)opts- Options including :fuzzy for fuzzy matching
Examples
tools = [%{"name" => "calculator"}, %{"name" => "weather"}]
{:ok, tool} = ExMCP.Client.find_matching_tool(tools, "calculator", [])
{:ok, tool} = ExMCP.Client.find_matching_tool(tools, "calc", fuzzy: true)
@spec find_tool(t(), String.t() | nil, keyword()) :: {:ok, map()} | {:error, :not_found} | {:error, any()}
Finds a tool by name or pattern.
Options
:fuzzy- Enable fuzzy matching (default: false):timeout- Request timeout (default: 5000)
@spec get_pending_requests(t()) :: [ExMCP.Types.request_id()]
Gets the list of pending request IDs.
Returns a list of request IDs for requests that are currently in progress.
This can be used with send_cancelled/3 to cancel specific requests.
Examples
{:ok, client} = ExMCP.Client.connect("http://localhost:8080/mcp")
# Start a long-running request
task = Task.async(fn ->
ExMCP.Client.call_tool(client, "slow_tool", %{})
end)
# Get pending requests
pending = ExMCP.Client.get_pending_requests(client)
# => ["req_123", "req_456"]
# Cancel a specific request
ExMCP.Client.send_cancelled(client, "req_123", "User cancelled")
Gets a prompt with the given arguments.
Gets the client status.
@spec list_prompts(t(), keyword() | timeout()) :: {:ok, %{required(String.t()) => [map()]}} | {:error, any()}
Lists available prompts.
@spec list_resource_templates(t(), keyword() | timeout()) :: {:ok, %{required(String.t()) => [map()]}} | {:error, any()}
Lists available resource templates.
Sends a resources/templates/list request to the server to retrieve the list of
available resource templates.
@spec list_resources(t(), keyword() | timeout()) :: {:ok, %{required(String.t()) => [map()]}} | {:error, any()}
Lists available resources.
@spec list_roots(t(), keyword() | timeout()) :: {:ok, %{required(String.t()) => [map()]}} | {:error, any()}
Lists available roots.
Sends a roots/list request to the server to retrieve the list of
available root URIs.
@spec list_tools(t(), keyword() | timeout()) :: {:ok, %{required(String.t()) => [map()]}} | {:error, any()}
Lists available tools from the server.
Options
:timeout- Request timeout (default: 5000):format- Return format (:map or :struct, default: :map)
Sends a log message to the server as a notification.
This function sends log messages from the client to the server for centralized logging and monitoring. The message is sent as a notification (fire-and-forget) following the MCP specification.
Parameters
client- Client process referencelevel- Log level string (e.g., "debug", "info", "warning", "error")message- Log message text
Returns
:ok- Message sent successfully{:error, reason}- Failed to send message
Example
{:ok, client} = ExMCP.Client.start_link(transport: :http, url: "...")
:ok = ExMCP.Client.log_message(client, "info", "Operation completed")
Sends a log message with additional data to the server as a notification.
This function sends detailed log messages from the client to the server for centralized logging and monitoring. The message is sent as a notification (fire-and-forget) following the MCP specification.
Parameters
client- Client process referencelevel- Log level string (e.g., "debug", "info", "warning", "error")message- Log message textdata- Optional additional data (map or any JSON-serializable value)
Supported Log Levels
Standard RFC 5424 levels: "debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"
Returns
:ok- Message sent successfully{:error, reason}- Failed to send message
Examples
{:ok, client} = ExMCP.Client.start_link(transport: :http, url: "...")
# Simple log message
:ok = ExMCP.Client.log_message(client, "info", "User logged in")
# Log message with additional context
:ok = ExMCP.Client.log_message(client, "error", "Database connection failed", %{
host: "db.example.com",
port: 5432,
error_code: "CONNECTION_TIMEOUT"
})
Gets the negotiated protocol version with the server.
Sends a notification to the server.
Notifications are fire-and-forget messages that don't expect a response.
Parameters
client- Client process referencemethod- The method name to notifyparams- Parameters for the notification (map)
Returns
:ok- Notification sent
Examples
:ok = ExMCP.Client.notify(client, "resource_updated", %{"uri" => "file://test.txt"})
Pings the server.
Reads a resource by URI.
Convenience alias for batch_request/3.
Sends a batch of JSON-RPC requests. Available in protocol version 2025-03-26 only.
@spec send_cancelled(t(), ExMCP.Types.request_id(), String.t() | nil) :: :ok | {:error, :cannot_cancel_initialize}
Sends a cancellation notification for a pending request.
This function sends a notifications/cancelled message to inform the server
that a previously-sent request should be cancelled. The server MAY stop
processing the request if it hasn't completed yet.
Parameters
client- Client process referencerequest_id- The ID of the request to cancelreason- Optional human-readable reason for cancellation
Returns
:ok- Cancellation notification sent{:error, :cannot_cancel_initialize}- Cannot cancel initialize request
Examples
:ok = ExMCP.Client.send_cancelled(client, "req_123", "User cancelled")
:ok = ExMCP.Client.send_cancelled(client, 12345, nil)
Gets server capabilities.
Gets server information.
@spec set_log_level(GenServer.server(), String.t()) :: {:ok, map()} | {:error, any()}
Sets the log level for the server.
Sends a logging/setLevel request to configure the server's log verbosity.
This is part of the MCP specification for controlling server logging behavior.
Parameters
client- Client process referencelevel- Log level string: "debug", "info", "warning", or "error"
Returns
{:ok, result}- Success with any server response data{:error, error}- Request failed with error details
Example
{:ok, client} = ExMCP.Client.start_link(transport: :http, url: "...")
{:ok, _} = ExMCP.Client.set_log_level(client, "debug")
@spec start_link(keyword()) :: GenServer.on_start()
Starts a client process with the given options.
Options
:transport- Transport type (:stdio, :http, :sse, etc.):transports- List of transports for fallback:name- Optional GenServer name:health_check_interval- Interval for health checks (default: 30_000):reliability- Reliability features configuration (optional):retry_policy- Default retry policy for all client operations (optional)
Reliability Options
The :reliability option accepts a keyword list with the following options:
:circuit_breaker- Circuit breaker configuration orfalseto disable:failure_threshold- Number of failures before opening (default: 5):success_threshold- Number of successes to close half-open circuit (default: 3):reset_timeout- Time before transitioning from open to half-open (default: 30_000):timeout- Operation timeout in milliseconds (default: 5_000)
:health_check- Health check configuration orfalseto disable:check_interval- Interval between health checks (default: 60_000):failure_threshold- Health check failures before marking unhealthy (default: 3):recovery_threshold- Health check successes before marking healthy (default: 2)
Reliability Examples
# Client with circuit breaker protection
{:ok, client} = ExMCP.Client.start_link(
transport: :stdio,
command: "my-server",
reliability: [
circuit_breaker: [
failure_threshold: 3,
reset_timeout: 10_000
]
]
)
# Client with both circuit breaker and health monitoring
{:ok, client} = ExMCP.Client.start_link(
transport: :http,
url: "http://localhost:8080/mcp",
reliability: [
circuit_breaker: [failure_threshold: 5],
health_check: [check_interval: 30_000]
]
)Retry Policy Options
The :retry_policy option accepts a keyword list with the following options:
:max_attempts- Maximum number of retry attempts (default: 3):initial_delay- Initial delay between retries in milliseconds (default: 100):max_delay- Maximum delay between retries in milliseconds (default: 5000):backoff_factor- Exponential backoff multiplier (default: 2):jitter- Add random jitter to prevent thundering herd (default: true)
Retry Policy Examples
# Client with default retry policy for all operations
{:ok, client} = ExMCP.Client.start_link(
transport: :stdio,
command: "my-server",
retry_policy: [
max_attempts: 5,
initial_delay: 200
]
)
# Individual operation with custom retry policy
{:ok, tools} = ExMCP.Client.list_tools(client,
retry_policy: [max_attempts: 2, backoff_factor: 1.5])
# Operation with no retries (override client default)
{:ok, result} = ExMCP.Client.call_tool(client, "tool", %{},
retry_policy: false)
Stops the client.
Subscribes to notifications for a resource.
Sends a resources/subscribe request to receive notifications when the
specified resource changes. The server will send notifications/resources/updated
messages when the subscribed resource is modified.
Parameters
client- Client process referenceuri- Resource URI to subscribe to (e.g., "file:///path/to/file")
Options
:timeout- Request timeout (default: 5000):format- Return format (:map or :struct, default: :map)
Returns
{:ok, result}- Subscription successful{:error, error}- Subscription failed with error details
Examples
{:ok, _result} = ExMCP.Client.subscribe_resource(client, "file:///config.json")
Convenience alias for list_tools/2.
Unsubscribes from notifications for a resource.
Sends a resources/unsubscribe request to stop receiving notifications
for the specified resource.
Parameters
client- Client process referenceuri- Resource URI to unsubscribe from
Options
:timeout- Request timeout (default: 5000):format- Return format (:map or :struct, default: :map)
Returns
{:ok, result}- Unsubscription successful{:error, error}- Unsubscription failed with error details
Examples
{:ok, _result} = ExMCP.Client.unsubscribe_resource(client, "file:///config.json")