MCP (Model Context Protocol) over Layr8 DIDComm.
A growing set of Layr8 services (Loom is the first) expose an MCP surface as
DIDComm request/reply: a request of type #{base}/<method> carrying a
JSON-RPC 2.0 body, answered by a #{base}/<method>-result message whose body
is the JSON-RPC response. The reply echoes the request's DIDComm thid, so
Layr8.Client.request/3 correlates it automatically — this module just
removes the boilerplate (protocol subscription, the #{base}/… type, the
JSON-RPC envelope, and unwrapping result / returning error).
Cross-language contract: ~/Developments/contracts/mcp-over-didcomm.md.
@layr8/sdk's src/mcp.ts is the same abstraction on the Node side.
Usage
Layr8.Client.mcp/2 must be called BEFORE connect/1, like handle/3,
because it registers the protocol subscription the node needs in order to
deliver replies:
{:ok, client} = Layr8.Client.start_link(%{...})
{:ok, binding} = Layr8.Client.mcp(client) # default base
:ok = Layr8.Client.connect(client)
loom = Layr8.Mcp.peer(binding, loom_did)
{:ok, _} = Layr8.Mcp.initialize(loom)
{:ok, tools} = Layr8.Mcp.list_tools(loom)
{:ok, result} = Layr8.Mcp.call_tool(loom, "create_workflow", %{"name" => name})Errors
Every call returns {:ok, result} or {:error, reason} — this module never
raises, unlike Layr8.Client.request/3, because a tool call failing is an
ordinary outcome a caller routes on rather than an exceptional one. A JSON-RPC
error object from the peer comes back as
{:error, {:mcp_error, code, message, data}}; a DIDComm problem report as
{:error, {:problem_report, code, comment}}; a lapsed deadline as
{:error, :timeout}. It goes through Layr8.Client.request_result/3, which
is the same request path with the raises left off.
A denial (e.m.authz.*) arrives as a problem report, and the usual cause is
a Verifiable Grant that never reached the wire rather than one that is
misconfigured — see Layr8.Wallet and the client's :on_grant_miss.
Summary
Types
A base-bound MCP binding, returned by Layr8.Client.mcp/2.
Functions
Calls an MCP method on the peer with optional params, returning the
JSON-RPC result.
Convenience for MCP tools/call.
The default MCP protocol base (mcp/1.0).
Convenience for MCP initialize.
Convenience for MCP tools/list; returns the tools list.
A caller bound to did on this binding's protocol base.
The DIDComm type for an MCP method: tools/call → #{base}/tools-call.
Types
Functions
Calls an MCP method on the peer with optional params, returning the
JSON-RPC result.
Options
:timeout— milliseconds to wait for the reply (default 30s), forwarded toLayr8.Client.request/3.
@spec call_tool(Layr8.Mcp.Peer.t(), String.t(), map(), keyword()) :: {:ok, term()} | {:error, term()}
Convenience for MCP tools/call.
@spec default_base() :: String.t()
The default MCP protocol base (mcp/1.0).
@spec initialize(Layr8.Mcp.Peer.t(), map() | nil, keyword()) :: {:ok, term()} | {:error, term()}
Convenience for MCP initialize.
@spec list_tools( Layr8.Mcp.Peer.t(), keyword() ) :: {:ok, [map()]} | {:error, term()}
Convenience for MCP tools/list; returns the tools list.
@spec peer(Layr8.Mcp.Binding.t(), String.t()) :: Layr8.Mcp.Peer.t()
A caller bound to did on this binding's protocol base.
The DIDComm type for an MCP method: tools/call → #{base}/tools-call.