barrel_mcp (barrel_mcp v3.0.1)
View SourceMain 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
List all tools (alias for list_tools/0).
Call a tool locally.
Whether the client declared a capability, before asking it for something that needs one.
Mark a URL-mode elicitation complete, notifying the client that started it. Call it from whatever handles your redirect; completion is authorised against the owning principal.
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.
Build the params for a form-mode elicitation/create. Pass the result as an {input_required, Requests, State} entry, or as the Params of elicit_create/3.
Build the params for a URL-mode elicitation/create, the mode for anything the client must not see. Ctx is the tool handler's context. See barrel_mcp_elicitation:url/4 for what is checked and what stays yours to honour.
Find a tool by name.
Get a prompt with arguments filled in.
Read the client's answer to one of the input requests this tool asked for on a previous attempt.
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.
Emit notifications/message for the request this tool is serving. Logger is optional; pass undefined to omit it.
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.
The term this tool passed as the state of its previous attempt, verified and deserialised.
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.
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.
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.
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
List all tools (alias for list_tools/0).
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">>
}).
Whether the client declared a capability, before asking it for something that needs one.
A server must not send an input request for a capability the client did not declare, so a tool that would returns an error to the client instead. Checking first lets the tool degrade on its own terms.
Mark a URL-mode elicitation complete, notifying the client that started it. Call it from whatever handles your redirect; completion is authorised against the owning principal.
2025-11-25 only.
-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.
Legacy era only; see the section note above.
Build the params for a form-mode elicitation/create. Pass the result as an {input_required, Requests, State} entry, or as the Params of elicit_create/3.
Never for secrets; use elicit_url/3.
Build the params for a URL-mode elicitation/create, the mode for anything the client must not see. Ctx is the tool handler's context. See barrel_mcp_elicitation:url/4 for what is checked and what stays yours to honour.
Find a tool by name.
Looks up a tool by name and returns its metadata if found.
Get a prompt with arguments filled in.
Executes the prompt handler with the provided arguments and returns the generated messages.
Read the client's answer to one of the input requests this tool asked for on a previous attempt.
The shapes match the blocking calls they replace, so porting a handler is a matter of moving the call rather than rewriting what it returns:
elicitation/create:{ok, ElicitResult}, the raw result with itsactionandcontent, aselicit_create/3.sampling/createMessage:{ok, Result, Usage}, assampling_create_message/3.roots/list:{ok, Roots}, already unwrapped, asroots_list/2.
none means the client has not answered this key: either the first attempt, or it chose not to. Ask again or give up; do not assume.
List all currently connected clients as [{ServerId, Pid}].
List all registered prompts.
List all registered resource templates.
List all registered resources.
-spec list_sessions_with_elicitation() -> [binary()].
Return the ids of currently connected sessions whose client declared elicitation capability.
-spec list_sessions_with_roots() -> [binary()].
Return the ids of currently connected sessions whose client declared roots capability.
-spec list_sessions_with_sampling() -> [binary()].
Return the ids of currently connected sessions whose client declared sampling capability.
List all registered tools.
Returns a list of tuples containing tool names and their metadata.
Example
Tools = barrel_mcp:list_tools(),
%% Returns: [{<<"search">>, #{description => ...}}, ...]
Emit notifications/message for the request this tool is serving. Logger is optional; pass undefined to omit it.
Modern: on this request's own response stream, and only if it named io.modelcontextprotocol/logLevel in its _meta. Legacy: on the session's SSE channel, filtered by logging/setLevel. Silent when there is nowhere to deliver.
-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).
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.
Legacy era only. 2026-07-28 deprecated logging and removed logging/setLevel; a modern client opts in per request instead, by naming a level in _meta. This keeps serving legacy sessions.
Emit notifications/progress to a session. Total may be omitted (defaults to undefined = absent in the wire payload).
-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.
Read a resource locally.
Executes the resource handler and returns its content.
Register a tool (alias for reg_tool/4).
-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}}.
-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 descriptionarguments- List of argument definitions
Each argument definition is a map with:
name- Argument name (binary)description- Argument descriptionrequired- Whether the argument is required (boolean)
Handler Return Value
The handler must return a map with:
description- Prompt descriptionmessages- List of message maps withroleandcontent
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}
]
}).
-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 nameuri- Unique resource URI (e.g.,<<"file:///config">>)description- Resource descriptionmime_type- MIME type (default:<<"text/plain">>)
Handler Return Values
binary()- Text contentmap()- 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">>
}).
-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">>).
-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 toolinput_schema- JSON Schema defining expected input formatannotations- Map of MCP behavioural hints surfaced underannotationsontools/list. Spec keys arereadOnlyHint,destructiveHint,idempotentHint,openWorldHint(all booleans). Values pass through verbatim.
Handler Return Values
The handler function can return:
binary()- Returned as text contentmap()- 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">>]
}
}).
The term this tool passed as the state of its previous attempt, verified and deserialised.
none on a first attempt. A state that failed verification never reaches the handler: the request is rejected before it runs.
-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.
Legacy era only, and Roots itself was removed by 2026-07-28; see the section note above.
Call a tool (alias for call_tool/2).
-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.
Legacy era only; see the section note above.
-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}
}).
-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 (seebarrel_mcp_auth)
Authentication Example
barrel_mcp:start_http(#{
port => 9090,
auth => #{
provider => barrel_mcp_auth_bearer,
provider_opts => #{
secret => <<"your-jwt-secret">>,
audience => <<"https://mcp.example.com">>
}
}
}).See also: barrel_mcp_auth, barrel_mcp_http.
-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.
Starts an HTTP/1.1 and HTTP/2 listener (the h1 and h2 libraries) implementing the MCP Streamable HTTP transport, serving every revision from 2025-03-26 to 2026-07-28. 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.
-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 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.
-spec stop_client(term()) -> ok | {error, not_found}.
Stop a previously-started client.
-spec stop_http() -> ok | {error, not_found}.
Stop the HTTP server.
Stops the MCP HTTP server if running.
-spec stop_http_stream() -> ok | {error, not_found}.
Stop the Streamable HTTP server.
Stops the MCP Streamable HTTP server if running.
-spec unreg(binary()) -> ok.
Unregister a tool (alias for unreg_tool/1).
-spec unreg_completion(term()) -> ok.
-spec unreg_prompt(Name :: binary()) -> ok.
Unregister a prompt.
-spec unreg_resource(Name :: binary()) -> ok.
Unregister a resource.
-spec unreg_resource_template(Name :: binary()) -> ok.
Unregister a resource template.
-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.
Look up the pid of a connected client by ServerId.