defmodule Urchin do
@moduledoc """
A Model Context Protocol (MCP) server library implementing the `2025-11-25`
specification over the Streamable HTTP transport.
See `Urchin.Server` for authoring a server (behaviour or DSL) and
`Urchin.Transport.StreamableHTTP` for mounting it as a `Plug`. The convenience
runner below boots a server with a standalone Bandit endpoint.
"""
@doc """
Starts a server module behind a standalone Bandit HTTP endpoint.
Requires the optional `:bandit` dependency. Options are forwarded to
`Urchin.Endpoint.start_link/1`; common ones are `:port`, `:path`, `:ip` and
transport options such as `:allowed_origins`.
## Example
{:ok, _pid} = Urchin.start_link(MyServer, port: 4000, path: "/mcp")
"""
@spec start_link(module(), keyword()) :: {:ok, pid()} | {:error, term()}
def start_link(server, opts \\ []) when is_atom(server) do
Urchin.Endpoint.start_link(Keyword.put(opts, :server, server))
end
@doc "The latest protocol revision implemented by this library."
@spec protocol_version() :: String.t()
def protocol_version, do: Urchin.Protocol.latest_version()
@doc """
Sends a notification to every active session on its general (GET) stream.
Useful for fan-out notifications such as `notifications/tools/list_changed` or
`notifications/resources/list_changed`. Sessions without a connected GET stream
buffer the notification for resumption.
Returns the number of sessions notified.
"""
@spec broadcast(String.t(), map() | nil) :: non_neg_integer()
def broadcast(method, params \\ nil) when is_binary(method) do
Urchin.Session.Registry
|> Registry.select([{{:_, :"$1", :_}, [], [:"$1"]}])
|> Enum.reduce(0, fn pid, count ->
Urchin.Session.notify(pid, method, params)
count + 1
end)
end
end