barrel_mcp (barrel_mcp v2.0.2)

View Source

Main API module for barrel_mcp.

This module provides the primary public interface for the barrel_mcp library, implementing the Model Context Protocol (MCP) specification.

Overview

barrel_mcp allows you to expose tools, resources, and prompts that AI assistants (like Claude) can interact with. The library supports both server mode (exposing your functionality) and client mode (consuming external MCP servers).

Quick Start

   %% Start the application
   application:ensure_all_started(barrel_mcp).
  
   %% Register a simple tool
   barrel_mcp:reg_tool(<<"greet">>, my_module, greet_handler, #{
       description => <<"Greet someone by name">>
   }).
  
   %% Start HTTP server
   {ok, _} = barrel_mcp:start_http(#{port => 9090}).

Handler Functions

All handlers (tools, resources, prompts) must be exported functions with arity 1, receiving a map of arguments:

   -module(my_module).
   -export([greet_handler/1]).
  
   greet_handler(Args) ->
       Name = maps:get(<<"name">>, Args, <<"World">>),
       <<"Hello, ", Name/binary, "!">>.

Summary

Functions

all() deprecated

List all tools (alias for list_tools/0).

Call a tool locally.

Send elicitation/create to the client behind a session to request structured user input. Requires the client to have declared elicitation capability in its initialize request and an active SSE stream. Blocks until the client responds or timeout_ms (default 30s) elapses.

Find a tool by name.

Get a prompt with arguments filled in.

List all currently connected clients as [{ServerId, Pid}].

List all registered prompts.

List all registered resource templates.

List all registered resources.

Return the ids of currently connected sessions whose client declared elicitation capability.

Return the ids of currently connected sessions whose client declared roots capability.

Return the ids of currently connected sessions whose client declared sampling capability.

List all registered tools.

Push a notifications/<kind>/list_changed envelope to every currently-connected SSE session. Hosts call this when they mutate the catalogue out-of-band (the registry already calls it for reg/4,5 and unreg/2).

Emit notifications/message (the MCP server log stream) to a session. The notification is dropped silently when Level is below the session's configured level (logging/setLevel). Logger is an optional component name; pass undefined to omit it. Data is the structured payload — typically a string or a map.

Emit notifications/progress to a session. Total may be omitted (defaults to undefined = absent in the wire payload).

Notify all subscribers of a resource that it has changed. The notification body is a JSON-RPC notification with no params; the client is expected to issue a resources/read to fetch the new state.

Read a resource locally.

Register a tool (alias for reg_tool/4).

Register a completion handler for a prompt argument or a resource-template argument. Handlers receive (PartialValue, Ctx) and return {ok, [Suggestion]} or {ok, [Suggestion], #{has_more => true}}.

Register a prompt with the MCP server.

Register a resource with the MCP server.

Register a resource template (RFC 6570 URI template).

Register a tool with the MCP server.

Send roots/list to the client behind a session to enumerate the host's available roots (typically filesystem or workspace roots the host has authorised the server to operate on). Requires the client to have declared roots capability in its initialize request and an active SSE stream. Blocks until the client responds or timeout_ms (default 30s) elapses.

run(Name, Args) deprecated

Call a tool (alias for call_tool/2).

Send sampling/createMessage to the client behind a session. Requires the client to have declared sampling capability in its initialize request and an active SSE stream. Blocks until the client responds or timeout_ms (default 30s) elapses.

Start a supervised MCP client connecting to a remote server.

Start the HTTP server for MCP.

Start the Streamable HTTP server for MCP (Protocol 2025-03-26).

Start the stdio server for MCP.

Start the stdio server as a supervised gen_server.

Stop a previously-started client.

Stop the HTTP server.

Stop the Streamable HTTP server.

unreg(Name) deprecated

Unregister a tool (alias for unreg_tool/1).

Unregister a prompt.

Unregister a resource.

Unregister a resource template.

Unregister a tool.

Look up the pid of a connected client by ServerId.

Functions

all()

This function is deprecated. Use list_tools/0 instead..
-spec all() -> [{binary(), map()}].

List all tools (alias for list_tools/0).

call_tool(Name, Args)

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

Call a tool locally.

Executes a registered tool handler with the given arguments. Useful for testing tools without going through the MCP protocol.

Example

  {ok, Result} = barrel_mcp:call_tool(<<"search">>, #{
      <<"query">> => <<"erlang">>
  }).

elicit_create(SessionId, Params, Opts)

-spec elicit_create(binary(), map(), map()) ->
                       {ok, Result :: map()} |
                       {error, timeout | not_supported | no_sse | not_found | term()}.

Send elicitation/create to the client behind a session to request structured user input. Requires the client to have declared elicitation capability in its initialize request and an active SSE stream. Blocks until the client responds or timeout_ms (default 30s) elapses.

find(Name)

-spec find(Name :: binary()) -> {ok, map()} | error.

Find a tool by name.

Looks up a tool by name and returns its metadata if found.

get_prompt(Name, Args)

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

Get a prompt with arguments filled in.

Executes the prompt handler with the provided arguments and returns the generated messages.

list_clients()

-spec list_clients() -> [{term(), pid()}].

List all currently connected clients as [{ServerId, Pid}].

list_prompts()

-spec list_prompts() -> [{binary(), map()}].

List all registered prompts.

list_resource_templates()

-spec list_resource_templates() -> [{binary(), map()}].

List all registered resource templates.

list_resources()

-spec list_resources() -> [{binary(), map()}].

List all registered resources.

list_sessions_with_elicitation()

-spec list_sessions_with_elicitation() -> [binary()].

Return the ids of currently connected sessions whose client declared elicitation capability.

list_sessions_with_roots()

-spec list_sessions_with_roots() -> [binary()].

Return the ids of currently connected sessions whose client declared roots capability.

list_sessions_with_sampling()

-spec list_sessions_with_sampling() -> [binary()].

Return the ids of currently connected sessions whose client declared sampling capability.

list_tools()

-spec list_tools() -> [{binary(), map()}].

List all registered tools.

Returns a list of tuples containing tool names and their metadata.

Example

  Tools = barrel_mcp:list_tools(),
  %% Returns: [{<<"search">>, #{description => ...}}, ...]

notify_list_changed(Kind)

-spec notify_list_changed(tool | resource | prompt) -> ok.

Push a notifications/<kind>/list_changed envelope to every currently-connected SSE session. Hosts call this when they mutate the catalogue out-of-band (the registry already calls it for reg/4,5 and unreg/2).

notify_log(SessionId, Level, Data)

-spec notify_log(binary(), atom() | binary(), term()) -> ok.

Emit notifications/message (the MCP server log stream) to a session. The notification is dropped silently when Level is below the session's configured level (logging/setLevel). Logger is an optional component name; pass undefined to omit it. Data is the structured payload — typically a string or a map.

notify_log(SessionId, Level, Logger, Data)

-spec notify_log(binary(), atom() | binary(), binary() | undefined, term()) -> ok.

notify_progress(SessionId, Token, Progress)

-spec notify_progress(binary(), term(), number()) -> ok.

Emit notifications/progress to a session. Total may be omitted (defaults to undefined = absent in the wire payload).

notify_progress(SessionId, Token, Progress, Total)

-spec notify_progress(binary(), term(), number(), number() | undefined) -> ok.

notify_resource_updated(Uri)

-spec notify_resource_updated(binary()) -> ok.

Notify all subscribers of a resource that it has changed. The notification body is a JSON-RPC notification with no params; the client is expected to issue a resources/read to fetch the new state.

notify_resource_updated(Uri, Extra)

-spec notify_resource_updated(binary(), map()) -> ok.

read_resource(Name)

-spec read_resource(Name :: binary()) -> {ok, term()} | {error, term()}.

Read a resource locally.

Executes the resource handler and returns its content.

reg(Name, Module, Function, Opts)

This function is deprecated. Use reg_tool/4 instead..
-spec reg(binary(), module(), atom(), map()) -> ok | {error, term()}.

Register a tool (alias for reg_tool/4).

reg_completion(Ref, Module, Function, Opts)

-spec reg_completion(Ref, Module, Function, Opts) -> ok | {error, term()}
                        when
                            Ref ::
                                {prompt, binary(), binary()} | {resource_template, binary(), binary()},
                            Module :: module(),
                            Function :: atom(),
                            Opts :: map().

Register a completion handler for a prompt argument or a resource-template argument. Handlers receive (PartialValue, Ctx) and return {ok, [Suggestion]} or {ok, [Suggestion], #{has_more => true}}.

reg_prompt(Name, Module, Function, Opts)

-spec reg_prompt(Name, Module, Function, Opts) -> ok | {error, term()}
                    when
                        Name :: binary(),
                        Module :: module(),
                        Function :: atom(),
                        Opts ::
                            #{description => binary(),
                              arguments =>
                                  [#{name := binary(), description => binary(), required => boolean()}]}.

Register a prompt with the MCP server.

Prompts are pre-defined conversation templates that AI assistants can use. They support arguments for dynamic content generation.

Options

  • description - Prompt description
  • arguments - List of argument definitions

Each argument definition is a map with:

  • name - Argument name (binary)
  • description - Argument description
  • required - Whether the argument is required (boolean)

Handler Return Value

The handler must return a map with:

  • description - Prompt description
  • messages - List of message maps with role and content

Example

  barrel_mcp:reg_prompt(<<"summarize">>, my_mod, summarize, #{
      description => <<"Summarize content">>,
      arguments => [
          #{name => <<"content">>, description => <<"Text to summarize">>, required => true},
          #{name => <<"style">>, description => <<"Summary style">>, required => false}
      ]
  }).

reg_resource(Name, Module, Function, Opts)

-spec reg_resource(Name, Module, Function, Opts) -> ok | {error, term()}
                      when
                          Name :: binary(),
                          Module :: module(),
                          Function :: atom(),
                          Opts ::
                              #{name => binary(),
                                uri => binary(),
                                description => binary(),
                                mime_type => binary()}.

Register a resource with the MCP server.

Resources expose data that AI assistants can read, such as configuration files, database records, or dynamic content.

Options

  • name - Human-readable resource name
  • uri - Unique resource URI (e.g., <<"file:///config">>)
  • description - Resource description
  • mime_type - MIME type (default: <<"text/plain">>)

Handler Return Values

  • binary() - Text content
  • map() - JSON content (auto-encoded)
  • #{blob => binary(), mimeType => binary()} - Binary content

Example

  barrel_mcp:reg_resource(<<"config">>, my_mod, get_config, #{
      name => <<"App Configuration">>,
      uri => <<"config://app/settings">>,
      description => <<"Current application settings">>,
      mime_type => <<"application/json">>
  }).

reg_resource_template(Name, Module, Function, Opts)

-spec reg_resource_template(Name, Module, Function, Opts) -> ok | {error, term()}
                               when
                                   Name :: binary(),
                                   Module :: module(),
                                   Function :: atom(),
                                   Opts ::
                                       #{name => binary(),
                                         uri_template => binary(),
                                         description => binary(),
                                         mime_type => binary()}.

Register a resource template (RFC 6570 URI template).

Resource templates surface as resources/templates/list on the wire and let clients discover URI patterns the server can serve via resources/read.

Options:

  • name — display name.
  • uri_template — RFC 6570 URI template (e.g. <<"file:///{path}">>).
  • description — human-readable description.
  • mime_type — content type (default <<"text/plain">>).

reg_tool(Name, Module, Function, Opts)

-spec reg_tool(Name, Module, Function, Opts) -> ok | {error, term()}
                  when
                      Name :: binary(),
                      Module :: module(),
                      Function :: atom(),
                      Opts ::
                          #{description => binary(),
                            input_schema => map(),
                            annotations => map(),
                            _ => _}.

Register a tool with the MCP server.

Tools are functions that AI assistants can call to perform actions or retrieve information. Each tool has a unique name and a handler function that processes requests.

Options

  • description - Human-readable description of the tool
  • input_schema - JSON Schema defining expected input format
  • annotations - Map of MCP behavioural hints surfaced under annotations on tools/list. Spec keys are readOnlyHint, destructiveHint, idempotentHint, openWorldHint (all booleans). Values pass through verbatim.

Handler Return Values

The handler function can return:

  • binary() - Returned as text content
  • map() - Automatically JSON encoded
  • [map()] - List of content blocks

Example

  barrel_mcp:reg_tool(<<"search">>, my_mod, search, #{
      description => <<"Search the database">>,
      input_schema => #{
          <<"type">> => <<"object">>,
          <<"properties">> => #{
              <<"query">> => #{<<"type">> => <<"string">>}
          },
          <<"required">> => [<<"query">>]
      }
  }).

roots_list(SessionId)

-spec roots_list(binary()) ->
                    {ok, [map()]} | {error, timeout | not_supported | no_sse | not_found | term()}.

Send roots/list to the client behind a session to enumerate the host's available roots (typically filesystem or workspace roots the host has authorised the server to operate on). Requires the client to have declared roots capability in its initialize request and an active SSE stream. Blocks until the client responds or timeout_ms (default 30s) elapses.

roots_list(SessionId, Opts)

-spec roots_list(binary(), map()) ->
                    {ok, [map()]} | {error, timeout | not_supported | no_sse | not_found | term()}.

run(Name, Args)

This function is deprecated. Use call_tool/2 instead..
-spec run(binary(), map()) -> {ok, term()} | {error, term()}.

Call a tool (alias for call_tool/2).

sampling_create_message(SessionId, Params, Opts)

-spec sampling_create_message(binary(), map(), map()) ->
                                 {ok, Result :: map(), Usage :: map()} |
                                 {error, timeout | not_supported | no_sse | not_found | term()}.

Send sampling/createMessage to the client behind a session. Requires the client to have declared sampling capability in its initialize request and an active SSE stream. Blocks until the client responds or timeout_ms (default 30s) elapses.

start_client(ServerId, Spec)

-spec start_client(term(), barrel_mcp_client:connect_spec()) -> {ok, pid()} | {error, term()}.

Start a supervised MCP client connecting to a remote server.

ServerId is any term the host uses to identify the connection (typically a binary). Spec is a barrel_mcp_client:connect_spec():

  barrel_mcp:start_client(<<"github">>, #{
      transport => {http, <<"https://mcp.github.com/">>},
      handler => {my_handler_mod, []},
      auth => {bearer, <<"ghp_xxx">>},
      capabilities => #{sampling => true}
  }).

start_http(Opts)

-spec start_http(Opts) -> {ok, pid()} | {error, term()}
                    when Opts :: #{port => pos_integer(), ip => inet:ip_address(), auth => map()}.

Start the HTTP server for MCP.

Starts a Cowboy HTTP server that handles MCP JSON-RPC requests. The server listens for POST requests at /mcp and /.

Options

  • port - Port number (default: 9090)
  • ip - IP address to bind (default: {0, 0, 0, 0})
  • auth - Authentication configuration (see barrel_mcp_auth)

Authentication Example

  barrel_mcp:start_http(#{
      port => 9090,
      auth => #{
          provider => barrel_mcp_auth_bearer,
          provider_opts => #{
              secret => <<"your-jwt-secret">>
          }
      }
  }).

See also: barrel_mcp_auth, barrel_mcp_http.

start_http_stream(Opts)

-spec start_http_stream(Opts) -> {ok, pid()} | {error, term()}
                           when
                               Opts ::
                                   #{port => pos_integer(),
                                     ip => inet:ip_address(),
                                     auth => map(),
                                     session_enabled => boolean(),
                                     ssl =>
                                         #{certfile := string(),
                                           keyfile := string(),
                                           cacertfile => string()}}.

Start the Streamable HTTP server for MCP (Protocol 2025-03-26).

Starts a Cowboy HTTP server implementing the MCP Streamable HTTP transport. This transport supports: - POST for client requests with JSON or SSE streaming responses - GET for server-to-client notification streams (SSE) - DELETE for session termination - Session management via Mcp-Session-Id header

This is the transport expected by Claude Code's --transport http` option. == Options == <dl> <dt>port</dt><dd>Port number (default: 9090)</dd> <dt>ip</dt><dd>IP address to bind (default: {0, 0, 0, 0})</dd> <dt>auth</dt><dd>Authentication configuration (see <a docgen-rel="seeerl" docgen-href="barrel_mcp_auth" href="barrel_mcp_auth.html"><code>barrel_mcp_auth</code></a>)</dd> <dt>session_enabled</dt><dd>Enable session management (default: true)</dd> <dt>ssl</dt><dd>SSL/TLS configuration for HTTPS: certfile, keyfile, cacertfile (optional)</dd> </dl> == Example == ``` %% Start with API key authentication barrel_mcp:start_http_stream(#{ port => 9090, auth => #{ provider => barrel_mcp_auth_apikey, provider_opts => #{keys => [<<"my-api-key">>]} } }). %% Start with HTTPS barrel_mcp:start_http_stream(#{ port => 9443, ssl => #{ certfile => "/path/to/cert.pem", keyfile => "/path/to/key.pem" } }).''

Claude Code Integration

After starting the server, add it to Claude Code:

  claude mcp add my-server --transport http http://localhost:9090/mcp \
    --header "X-API-Key: my-api-key"

See also: barrel_mcp_auth, barrel_mcp_http_stream.

start_stdio()

-spec start_stdio() -> ok.

Start the stdio server for MCP.

Starts an MCP server that communicates over stdin/stdout. This is the transport used for Claude Desktop integration.

Warning: This function blocks and runs the read-handle-respond loop until the input stream closes.

Claude Desktop Configuration

Configure your claude_desktop_config.json:

  {
     "mcpServers": {
      "my-server": {
        "command": "/path/to/my_app",
        "args": ["mcp"]
      }
    }
  }

See also: barrel_mcp_stdio, start_stdio_link/0.

start_stdio_link()

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

Start the stdio server as a supervised gen_server.

Starts an MCP stdio server that can be supervised. Unlike start_stdio/0, this function returns immediately after spawning the server process.

The server registers locally as barrel_mcp_stdio.

Example

  %% In your supervisor:
  init([]) ->
      SupFlags = #{strategy => one_for_one},
      Children = [
          #{id => mcp_stdio,
            start => {barrel_mcp, start_stdio_link, []},
            restart => permanent,
            type => worker}
      ],
      {ok, {SupFlags, Children}}.

See also: barrel_mcp_stdio, start_stdio/0.

stop_client(ServerId)

-spec stop_client(term()) -> ok | {error, not_found}.

Stop a previously-started client.

stop_http()

-spec stop_http() -> ok | {error, not_found}.

Stop the HTTP server.

Stops the MCP HTTP server if running.

stop_http_stream()

-spec stop_http_stream() -> ok | {error, not_found}.

Stop the Streamable HTTP server.

Stops the MCP Streamable HTTP server if running.

unreg(Name)

This function is deprecated. Use unreg_tool/1 instead..
-spec unreg(binary()) -> ok.

Unregister a tool (alias for unreg_tool/1).

unreg_completion(_)

-spec unreg_completion(term()) -> ok.

unreg_prompt(Name)

-spec unreg_prompt(Name :: binary()) -> ok.

Unregister a prompt.

unreg_resource(Name)

-spec unreg_resource(Name :: binary()) -> ok.

Unregister a resource.

unreg_resource_template(Name)

-spec unreg_resource_template(Name :: binary()) -> ok.

Unregister a resource template.

unreg_tool(Name)

-spec unreg_tool(Name :: binary()) -> ok.

Unregister a tool.

Removes a previously registered tool from the MCP server. After unregistration, the tool will no longer appear in tools/list responses.

whereis_client(ServerId)

-spec whereis_client(term()) -> pid() | undefined.

Look up the pid of a connected client by ServerId.