barrel_mcp_client (barrel_mcp v2.0.2)

View Source

MCP client for connecting to external MCP servers.

A gen_statem that owns one connection to one MCP server. Two transports are supported: stdio (subprocess) and Streamable HTTP (POST + SSE GET).

States:

  • connecting — transport is opening.
  • initializinginitialize request in flight.
  • ready — handshake complete; calls accepted.
  • closing — owner asked to close.

Inbound JSON-RPC envelopes from the transport are routed by decode_envelope/1:

  • response/error with id — match against the pending-request table, post the result to the waiting caller.
  • request with id — dispatch to the configured barrel_mcp_client_handler module; reply (sync or async) goes back over the same transport.
  • notification (no id) — dispatch to handler; resource update notifications are also routed to subscribers.

Server-side host application code never sees the transport layer; it talks to this module via the API below. Whether to bind an LLM provider (Anthropic, OpenAI, Hermes-style local model) into this loop is the host's job — barrel_mcp itself stays a pure MCP library.

Summary

Functions

Invoke a tool by name. Args is forwarded verbatim as the JSON-RPC arguments field. Returns the server's result map, which has a <<"content">> list of content blocks.

Invoke a tool with options.

Cancel a previously-issued request by id. Sends notifications/cancelled to the server and unblocks the caller with {error, cancelled}.

Close the connection.

Send completion/complete to ask the server to suggest values for a prompt or resource template argument. Ref is the JSON-RPC ref map (e.g. #{<<"type">> => <<"ref/prompt">>, <<"name">> => N}) and Argument is #{<<"name">> => Key, <<"value">> => Partial}.

Render a prompt with the given arguments.

List prompts advertised by the server. Single page.

List prompts with pagination control. Same Opts shape as list_tools/2.

Walk every prompts/list page.

List resource templates advertised by the server. Single page.

List resource templates with pagination control. Same Opts shape as list_tools/2.

Walk every resources/templates/list page.

List resources advertised by the server. Single page.

List resources with pagination control. Same Opts shape as list_tools/2.

Walk every resources/list page and return the union.

List tools advertised by the server. Returns a single page. Use list_tools/2 with #{want_cursor => true} or list_tools_all/1 to walk pagination.

List tools with pagination control.

Walk all tools/list pages and return the full list.

Inform the connected server that the host's roots list has changed. The server may follow up with roots/list to fetch the new set. Hosts that mutate their roots after initialize (e.g. user opened a new workspace) call this so the server picks up the change without polling.

Send a ping request and wait for the response.

Return the negotiated protocol version (e.g. <<"2025-11-25">> or <<"2025-03-26">> if the server downgraded).

Read a resource by URI.

Deliver a deferred reply for a server-initiated request that the handler answered with {async, Tag, _}. Result is either a plain term (sent as the JSON-RPC result) or {error, Code, Message}.

Return the server capabilities map negotiated during initialize. Useful to gate work on optional features.

Return the serverInfo map the server reported during initialize (with keys like <<"name">> and <<"version">>).

Send logging/setLevel. Level is one of debug, info, notice, warning, error, critical, alert, emergency as a binary.

Start an unsupervised client.

Start a supervised client. Linked to the calling process.

Subscribe the calling process to updates for Uri. The calling process receives {mcp_resource_updated, Uri, Params} on every inbound notifications/resources/updated for that URI until it calls unsubscribe/2 or the client closes.

Cancel a long-running task by id. Returns {ok, _} on acceptance; the task transitions to cancelled status, which the server then broadcasts via notifications/tasks/status.

Fetch a single task by id.

List long-running tasks owned by the connected session. Single page; use tasks_list/2 with #{want_cursor => true} or tasks_list_all/1 to walk pagination.

Walk every tasks/list page.

Fetch the final result of a completed task. Returns the task's stored result map; for failed tasks returns {error, {Code, Message}}; for tasks still working returns {error, {_, <<"Task not yet complete">>}}.

Stop receiving updates for Uri on the calling process.

Types

connect_spec/0

-type connect_spec() ::
          #{transport :=
                {http, binary() | string()} | {stdio, #{command := string(), args => [string()]}},
            client_info => #{name => binary(), version => binary()},
            capabilities => map(),
            handler => {module(), term()},
            auth =>
                none |
                {bearer, binary()} |
                {oauth, map()} |
                {oauth_client_credentials, map()} |
                {oauth_enterprise, map()},
            protocol_version => binary(),
            request_timeout => pos_integer(),
            init_timeout => pos_integer(),
            ping_interval => pos_integer() | infinity,
            ping_failure_threshold => pos_integer()}.

Functions

call_tool(Pid, Name, Args)

-spec call_tool(pid(), binary(), map()) -> {ok, map()} | {error, term()}.

Invoke a tool by name. Args is forwarded verbatim as the JSON-RPC arguments field. Returns the server's result map, which has a <<"content">> list of content blocks.

call_tool(Pid, Name, Args, Opts)

-spec call_tool(pid(), binary(), map(), map()) -> {ok, map()} | {error, term()}.

Invoke a tool with options.

Opts may contain:

  • {progress_token, Token} — register the calling process to receive {mcp_progress, Token, Params} messages until the request settles.
  • {timeout, Ms} — override the per-request timeout (request_timeout from the connect spec, default 30000).

callback_mode()

cancel(Pid, RequestId)

-spec cancel(pid(), integer()) -> ok.

Cancel a previously-issued request by id. Sends notifications/cancelled to the server and unblocks the caller with {error, cancelled}.

close(Pid)

-spec close(pid()) -> ok.

Close the connection.

closing(E, Req, Data)

code_change(OldVsn, State, Data, Extra)

complete(Pid, Ref, Argument)

-spec complete(pid(), map(), map()) -> {ok, map()} | {error, term()}.

Send completion/complete to ask the server to suggest values for a prompt or resource template argument. Ref is the JSON-RPC ref map (e.g. #{<<"type">> => <<"ref/prompt">>, <<"name">> => N}) and Argument is #{<<"name">> => Key, <<"value">> => Partial}.

connecting(EventType, Req, Data)

get_prompt(Pid, Name, Args)

-spec get_prompt(pid(), binary(), map()) -> {ok, map()} | {error, term()}.

Render a prompt with the given arguments.

init(Spec)

initializing(EventType, Req, Data)

list_prompts(Pid)

-spec list_prompts(pid()) -> {ok, [map()]} | {error, term()}.

List prompts advertised by the server. Single page.

list_prompts(Pid, Opts)

-spec list_prompts(pid(), map()) ->
                      {ok, [map()], binary() | undefined} | {ok, [map()]} | {error, term()}.

List prompts with pagination control. Same Opts shape as list_tools/2.

list_prompts_all(Pid)

-spec list_prompts_all(pid()) -> {ok, [map()]} | {error, term()}.

Walk every prompts/list page.

list_resource_templates(Pid)

-spec list_resource_templates(pid()) -> {ok, [map()]} | {error, term()}.

List resource templates advertised by the server. Single page.

list_resource_templates(Pid, Opts)

-spec list_resource_templates(pid(), map()) ->
                                 {ok, [map()], binary() | undefined} | {ok, [map()]} | {error, term()}.

List resource templates with pagination control. Same Opts shape as list_tools/2.

list_resource_templates_all(Pid)

-spec list_resource_templates_all(pid()) -> {ok, [map()]} | {error, term()}.

Walk every resources/templates/list page.

list_resources(Pid)

-spec list_resources(pid()) -> {ok, [map()]} | {error, term()}.

List resources advertised by the server. Single page.

list_resources(Pid, Opts)

-spec list_resources(pid(), map()) ->
                        {ok, [map()], binary() | undefined} | {ok, [map()]} | {error, term()}.

List resources with pagination control. Same Opts shape as list_tools/2.

list_resources_all(Pid)

-spec list_resources_all(pid()) -> {ok, [map()]} | {error, term()}.

Walk every resources/list page and return the union.

list_tools(Pid)

-spec list_tools(pid()) -> {ok, [map()]} | {error, term()}.

List tools advertised by the server. Returns a single page. Use list_tools/2 with #{want_cursor => true} or list_tools_all/1 to walk pagination.

list_tools(Pid, Opts)

-spec list_tools(pid(), map()) ->
                    {ok, [map()], NextCursor :: binary() | undefined} | {ok, [map()]} | {error, term()}.

List tools with pagination control.

Opts may contain:

  • {cursor, Cursor} — start from a previously-returned nextCursor.
  • {want_cursor, true} — return {ok, Items, NextCursor} even on the last page (with undefined for NextCursor).
  • {timeout, Ms} — override the per-request timeout.

list_tools_all(Pid)

-spec list_tools_all(pid()) -> {ok, [map()]} | {error, term()}.

Walk all tools/list pages and return the full list.

notify_roots_list_changed(Pid)

-spec notify_roots_list_changed(pid()) -> ok.

Inform the connected server that the host's roots list has changed. The server may follow up with roots/list to fetch the new set. Hosts that mutate their roots after initialize (e.g. user opened a new workspace) call this so the server picks up the change without polling.

ping(Pid)

-spec ping(pid()) -> {ok, map()} | {error, term()}.

Send a ping request and wait for the response.

protocol_version(Pid)

-spec protocol_version(pid()) -> {ok, binary() | undefined}.

Return the negotiated protocol version (e.g. <<"2025-11-25">> or <<"2025-03-26">> if the server downgraded).

read_resource(Pid, Uri)

-spec read_resource(pid(), binary()) -> {ok, map()} | {error, term()}.

Read a resource by URI.

ready(EventType, EventContent, Data)

reply_async(Pid, Tag, Result)

-spec reply_async(pid(), term(), term() | {error, integer(), binary()}) -> ok.

Deliver a deferred reply for a server-initiated request that the handler answered with {async, Tag, _}. Result is either a plain term (sent as the JSON-RPC result) or {error, Code, Message}.

server_capabilities(Pid)

-spec server_capabilities(pid()) -> {ok, map() | undefined}.

Return the server capabilities map negotiated during initialize. Useful to gate work on optional features.

server_info(Pid)

-spec server_info(pid()) -> {ok, map() | undefined}.

Return the serverInfo map the server reported during initialize (with keys like <<"name">> and <<"version">>).

set_log_level(Pid, Level)

-spec set_log_level(pid(), binary()) -> {ok, map()} | {error, term()}.

Send logging/setLevel. Level is one of debug, info, notice, warning, error, critical, alert, emergency as a binary.

start(Spec)

-spec start(connect_spec()) -> {ok, pid()} | {error, term()}.

Start an unsupervised client.

start_link(Spec)

-spec start_link(connect_spec()) -> {ok, pid()} | {error, term()}.

Start a supervised client. Linked to the calling process.

subscribe(Pid, Uri)

-spec subscribe(pid(), binary()) -> {ok, map()} | {error, term()}.

Subscribe the calling process to updates for Uri. The calling process receives {mcp_resource_updated, Uri, Params} on every inbound notifications/resources/updated for that URI until it calls unsubscribe/2 or the client closes.

tasks_cancel(Pid, TaskId)

-spec tasks_cancel(pid(), binary()) -> {ok, map()} | {error, term()}.

Cancel a long-running task by id. Returns {ok, _} on acceptance; the task transitions to cancelled status, which the server then broadcasts via notifications/tasks/status.

tasks_get(Pid, TaskId)

-spec tasks_get(pid(), binary()) -> {ok, map()} | {error, term()}.

Fetch a single task by id.

tasks_list(Pid)

-spec tasks_list(pid()) -> {ok, [map()]} | {error, term()}.

List long-running tasks owned by the connected session. Single page; use tasks_list/2 with #{want_cursor => true} or tasks_list_all/1 to walk pagination.

tasks_list(Pid, Opts)

-spec tasks_list(pid(), map()) -> {ok, [map()], binary() | undefined} | {ok, [map()]} | {error, term()}.

tasks_list_all(Pid)

-spec tasks_list_all(pid()) -> {ok, [map()]} | {error, term()}.

Walk every tasks/list page.

tasks_result(Pid, TaskId)

-spec tasks_result(pid(), binary()) -> {ok, map()} | {error, term()}.

Fetch the final result of a completed task. Returns the task's stored result map; for failed tasks returns {error, {Code, Message}}; for tasks still working returns {error, {_, <<"Task not yet complete">>}}.

terminate(Reason, State, Data)

unsubscribe(Pid, Uri)

-spec unsubscribe(pid(), binary()) -> {ok, map()} | {error, term()}.

Stop receiving updates for Uri on the calling process.