%%%------------------------------------------------------------------- %%% @doc Behaviour for handling server-initiated MCP messages. %%% %%% A host application implements this module to react to requests %%% the server sends *to* the client (per the declared client %%% capabilities) and to notifications the server emits. %%% %%% Capabilities and the matching callbacks: %%% %%% %%% Notifications cover the rest of the spec: `notifications/cancelled', %%% `notifications/progress', `notifications/resources/updated', %%% `notifications/resources/list_changed', `notifications/tools/list_changed', %%% `notifications/prompts/list_changed', `notifications/message'. %%% %%% A default implementation in `barrel_mcp_client_handler_default' %%% returns `method_not_found' for every request and ignores every %%% notification, so a host only writes callbacks for capabilities it %%% actually declares. %%% %%% Async replies: when answering a request requires a long-running %%% operation (e.g. an HTTP call to an LLM provider), return %%% `{async, Tag, State}' from `handle_request/3' and later send %%% `barrel_mcp_client:reply_async(ClientPid, Tag, Result)' from any %%% process. The client's state machine will not block while the %%% handler is computing. %%% %%% == Example == %%% %%% A handler that answers `sampling/createMessage' synchronously: %%% %%% ``` %%% -module(my_sampler). %%% -behaviour(barrel_mcp_client_handler). %%% -export([init/1, handle_request/3, handle_notification/3, %%% terminate/2]). %%% %%% init(Args) -> {ok, Args}. %%% %%% handle_request(<<"sampling/createMessage">>, Params, S) -> %%% Reply = call_my_llm(Params), %% your provider integration %%% Result = #{<<"content">> => #{<<"type">> => <<"text">>, %%% <<"text">> => Reply}, %%% <<"model">> => <<"my-model">>, %%% <<"role">> => <<"assistant">>}, %%% {reply, Result, S}; %%% handle_request(Method, _Params, S) -> %%% {error, -32601, <<"Method not found: ", Method/binary>>, S}. %%% %%% handle_notification(_Method, _Params, S) -> {ok, S}. %%% terminate(_Reason, _State) -> ok. %%% ''' %%% %%% Wire the handler in via the connect spec: %%% `#{handler => {my_sampler, []}}'. %%% %%% See `examples/sampling_host/' for a runnable end-to-end version. %%% @end %%%------------------------------------------------------------------- -module(barrel_mcp_client_handler). -export_type([state/0, async_tag/0]). -type state() :: term(). -type async_tag() :: term(). -callback init(Args :: term()) -> {ok, state()} | {error, term()}. -callback handle_request( Method :: binary(), Params :: map(), State :: state() ) -> {reply, Result :: term(), state()} | {error, Code :: integer(), Message :: binary(), state()} | {async, async_tag(), state()}. -callback handle_notification( Method :: binary(), Params :: map(), State :: state() ) -> {ok, state()}. -callback terminate(Reason :: term(), State :: state()) -> any(). -optional_callbacks([terminate/2]).