GenMCP.Suite (gen_mcp v2.0.0)

Copy Markdown View Source

Ready-made GenMCP server that serves tools, resources, and prompts from a set of configured providers.

GenMCP.Suite is the default :server for GenMCP.Transport.StreamableHTTP. Instead of implementing the GenMCP behaviour yourself, you configure a Suite with lists of provider modules and it answers every Model Context Protocol request by dispatching to them: server/discover, tools/list, tools/call, the resources/* and prompts/* requests, and subscription requests. The server/discover capability snapshot is self-describing, computed from the providers you configured, so you never hand-declare capabilities. A request for a method the Suite does not implement is answered with a JSON-RPC "method not found" error.

Like any GenMCP implementation, a Suite runs per request on the stateless 2026-07-28 transport: init/1 builds the state from your configuration on every request, and per-request client context arrives through the GenMCP.Mux.Channel.t/0, not through long-lived session state. Pagination cursors and the multi round-trip requestState are encrypted and carried by the client between requests, so the Suite holds no state of its own between them.

Minimal usage

Configure a Suite with a single add tool. The tool implements GenMCP.Suite.Tool and delegates the arithmetic to a plain Calculator module, so the tool stays a thin adapter with no logic of its own:

defmodule MyApp.Calculator do
  def add(a, b) do
    a + b
  end
end

defmodule MyApp.AddTool do
  use GenMCP.Suite.Tool,
    name: "add",
    description: "Adds two numbers and returns the sum.",
    input_schema: %{
      "type" => "object",
      "properties" => %{
        "a" => %{"type" => "number"},
        "b" => %{"type" => "number"}
      },
      "required" => ["a", "b"]
    }

  alias GenMCP.MCP.V2607, as: MCP

  @impl true
  def call(request, _channel, _arg) do
    %{"a" => a, "b" => b} = request.params.arguments
    {:result, MCP.call_tool_result(text: "#{MyApp.Calculator.add(a, b)}")}
  end
end

Because the Suite is the default server, its options are passed straight to the transport plug in your router. The tool list, server name, and server version are all the Suite needs:

# In your router
forward "/mcp", GenMCP.Transport.StreamableHTTP,
  server_name: "My App",
  server_version: "1.0.0",
  tools: [MyApp.AddTool]

Configuration

The Suite accepts these options:

  • :server_name - required string, the server's name reported in server/discover.
  • :server_version - required string, the server's version.
  • :server_title - optional human-readable title, defaults to nil.
  • :tools - the list of GenMCP.Suite.Tool implementations exposed by the server. Defaults to [].
  • :resources - the list of GenMCP.Suite.ResourceRepo implementations to serve resources from. Defaults to [].
  • :prompts - the list of GenMCP.Suite.PromptRepo implementations to generate prompts with. Defaults to [].
  • :extensions - the list of GenMCP.Suite.Extension implementations that contribute further tools, resource repositories, and prompt repositories, typically computed from the request's GenMCP.Mux.Channel.t/0. Defaults to [].
  • :subscription_handler - a single GenMCP.Suite.SubscriptionHandler implementation that answers subscription requests. Defaults to nil (no subscriptions).
  • :send_server_info - whether to include the server's io.modelcontextprotocol/serverInfo metadata (built from :server_name, :server_version, and :server_title) in the _meta of every result, as the spec recommends. Defaults to true.

Providers and their arguments

Every provider entry (in :tools, :resources, :prompts, :extensions, and :subscription_handler) is given in one of three forms: a bare module, a {module, arg} tuple, or a ready-built descriptor map. The provider behaviours (GenMCP.Suite.Tool, GenMCP.Suite.ResourceRepo, GenMCP.Suite.PromptRepo, GenMCP.Suite.SubscriptionHandler, and GenMCP.Suite.Extension) share two arguments that recur across their callbacks:

  • arg is the trailing argument of every provider callback. It is the value you attach next to the module as {module, arg} (a bare module is treated as {module, []}). It lets one generic, config-driven provider module be configured differently in different Suites, for example {MyApp.FileResource, root: "/srv/docs"}.
  • channel is the request-scoped GenMCP.Mux.Channel.t/0, threaded as the second-to-last argument of the callbacks that need request context. It carries read-only client meta and authorization assigns, so a provider can vary what it exposes per request (for instance hiding a tool from an unauthorized caller). It is passed only to the callbacks that act on a request, not to every callback.

See each provider behaviour's own documentation for the callbacks it defines and how it carries state across a streaming request.

Custom server

A Suite covers the common case. When you need full control over request handling, implement the GenMCP behaviour directly and hand your module to the transport as the :server option instead. See GenMCP for that contract.