# `GenMCP.MCP.V2607`
[🔗](https://github.com/lud/gen_mcp/blob/main/lib/gen_mcp/mcp/v2607.ex#L1)

Builders and struct vocabulary for the MCP `2026-07-28` protocol version.

The structs under `GenMCP.MCP.V2607.*` mirror the MCP schema for this protocol
version: requests, results, content blocks, capabilities, and so on. This
module exposes builder functions that turn convenient Elixir terms (keyword
lists, tuples, and maps) into those structs, so handler code returns readable
values instead of hand-writing nested struct literals.

These builders are what handler callbacks reach for when they return a result.
A tool's `c:GenMCP.Suite.Tool.call/3`, a prompt repository's
`c:GenMCP.Suite.PromptRepo.get/4`, and a resource repository's
`c:GenMCP.Suite.ResourceRepo.read/3` all build their return value with one of
the functions here. The module is conventionally aliased as `MCP`:

    alias GenMCP.MCP.V2607, as: MCP

The most common builder is `call_tool_result/1`, which assembles the content,
optional structured content, and error flag of a tool's response:

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> result = MCP.call_tool_result(text: "42")
    iex> match?(%MCP.CallToolResult{content: [%MCP.TextContent{text: "42"}]}, result)
    true

### Builder groups

The builders fall into a few families:

* **Tool calls**: `call_tool_result/1` aggregates content, structured content,
  and the error flag. `content_block/1` builds one content block on its own.
* **Listings**: `list_tools_result/2`, `list_resources_result/3`,
  `list_resource_templates_result/2`, and `list_prompts_result/3` wrap a
  collection (and, where paginated, a cursor) in the matching result struct.
* **Reads**: `read_resource_result/1` and `resource_contents/1` build the
  contents returned for a `resources/read`. `get_prompt_result/1` builds a
  prompt's messages.
* **Discovery**: `discover_result/1`, `capabilities/1`, and `server_info/1`
  build the `server/discover` response and its parts.

### Cache hints

The list and read builders accept optional flat `:cache_scope` and `:ttl_ms`
options that populate the `cacheScope` / `ttlMs` fields the schema requires.
When omitted, they default to the no-cache hint returned by
`default_cache_control/0` (private, immediately stale). Passing only one of the
two fills the other with that same no-cache default.

# `call_tool_result`

Builds the `GenMCP.MCP.V2607.CallToolResult` a tool returns from a `tools/call`.

Pass a list (a keyword list reads well, since keys may repeat) of entries. Each
entry contributes to the result's `content`, its `structuredContent`, or its
`isError` flag:

* **Content shorthands** (`{:text, _}`, `{:image, _}`, `{:audio, _}`,
  `{:resource, _}`, `{:link, _}`) are turned into content blocks by
  `content_block/1` and appended to `content`.
* **Literal content structs** (a `GenMCP.MCP.V2607.TextContent` and the other
  content structs) are appended to `content` unchanged. You may mix shorthands
  and structs in the same list.
* **`{:data, map}`** sets `structuredContent` to the map and also mirrors it
  into `content` as a JSON-encoded text block.
* **`{:_data, map}`** sets `structuredContent` without the text mirror. A bare
  map entry behaves like `{:data, map}`.
* **`{:error, true}`** sets `isError` to `true`. The flag is sticky: once any
  entry sets it, a later `{:error, false}` does not clear it. `{:error,
  false}` and `{:error, nil}` on their own leave the flag unset.
* **`{:error, message}`** with a binary appends the message as a text block and
  sets `isError` to `true`.

Only one structured content may be set; a second `:data`, `:_data`, or bare map
raises `ArgumentError`. The result's `resultType` is always `"complete"`.

### Examples

The common case is a single text result:

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> result = MCP.call_tool_result(text: "42")
    iex> match?(%MCP.CallToolResult{content: [%MCP.TextContent{text: "42"}], isError: nil}, result)
    true

Return structured data alongside a human-readable summary. With `:data` the map
is both set as `structuredContent` and mirrored as a JSON text block; use
`:_data` to set the structured content without the extra text block:

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> result = MCP.call_tool_result(text: "3 rows", data: %{rows: 3})
    iex> result.structuredContent
    %{rows: 3}
    iex> length(result.content)
    2

Flag a failure with `error:`; a binary message is added as text content:

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> result = MCP.call_tool_result(error: "boom")
    iex> match?(%MCP.CallToolResult{content: [%MCP.TextContent{text: "boom"}], isError: true}, result)
    true

# `capabilities`

```elixir
@spec capabilities(keyword() | GenMCP.MCP.V2607.ServerCapabilities.t() | map()) ::
  GenMCP.MCP.V2607.ServerCapabilities.t()
```

Normalizes capability flags into a `GenMCP.MCP.V2607.ServerCapabilities` struct.

Each entry of the keyword list or map sets one capability field:

* a value of `true` becomes an empty map `%{}` (the capability is enabled with
  no extra options),
* a map value is kept as given (use this to pass sub-options such as
  `listChanged: true`),
* any other value (`false`, `nil`) leaves the field `nil`, meaning the
  capability is not advertised.

A `GenMCP.MCP.V2607.ServerCapabilities` struct passed in is returned unchanged.

### Examples

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> caps = MCP.capabilities(tools: true, resources: %{subscribe: true}, prompts: false)
    iex> {caps.tools, caps.resources, caps.prompts}
    {%{}, %{subscribe: true}, nil}

# `content_block`

Builds a single content block struct from a shorthand term.

Tools and prompts return content blocks. This builder turns a compact tuple
into the matching struct so callers do not write struct literals by hand. It is
used on its own when you need one block, and internally by `call_tool_result/1`
and `get_prompt_result/1` for each entry they receive.

The accepted shorthands are:

* `{:text, text}` - a `GenMCP.MCP.V2607.TextContent`.
* `{:image, {mime_type, data}}` - a `GenMCP.MCP.V2607.ImageContent`, with
  base64-encoded `data`.
* `{:audio, {mime_type, data}}` - a `GenMCP.MCP.V2607.AudioContent`, with
  base64-encoded `data`.
* `{:resource, %{uri: uri, text: text}}` or `{:resource, %{uri: uri, blob:
  blob}}` - a `GenMCP.MCP.V2607.EmbeddedResource`.
* `{:link, %{name: name, uri: uri}}` - a `GenMCP.MCP.V2607.ResourceLink`. Extra
  keys in the map are carried onto the struct.

Any other term raises `ArgumentError`.

### Examples

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> %MCP.TextContent{text: text} = MCP.content_block({:text, "hello"})
    iex> text
    "hello"

An image block carries its MIME type and base64 payload:

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> block = MCP.content_block({:image, {"image/png", "aGVsbG8="}})
    iex> {block.mimeType, block.data}
    {"image/png", "aGVsbG8="}

# `default_cache_control`

Returns the default cache control used when no cache hint is given.

The list and read builders fall back to this `{scope, ttl_ms}` tuple for their
`:cache_scope` and `:ttl_ms` options. It is the no-cache hint: a private scope
with an immediately stale TTL, which is schema-valid and tells clients not to
reuse the result.

# `discover_result`

Builds the `GenMCP.MCP.V2607.DiscoverResult` returned for `server/discover`.

In the stateless 2026 core, `server/discover` is where the server advertises
who it is and what it can do. This builder fills that response: it sets the
supported protocol versions and
marks the result as a no-cache snapshot.

### Options

* `:name` - the server name, required (passed to `server_info/1`).
* `:version` - the server version, required (passed to `server_info/1`).
* `:title` - an optional human-friendly server title.
* `:capabilities` - capability flags or maps, passed to `capabilities/1`.
  Defaults to no advertised capabilities.

### Examples

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> result = MCP.discover_result(name: "MyServer", version: "1.0.0", capabilities: [tools: true])
    iex> info = result._meta."io.modelcontextprotocol/serverInfo"
    iex> {info.name, result.capabilities.tools, result.supportedVersions}
    {"MyServer", %{}, ["2026-07-28"]}

# `get_prompt_result`

```elixir
@spec get_prompt_result(keyword() | [term()]) :: GenMCP.MCP.V2607.GetPromptResult.t()
```

Builds the `GenMCP.MCP.V2607.GetPromptResult` returned for `prompts/get`.

Pass a list of message entries (a keyword list reads well, since keys repeat),
plus an optional `:description`. Each message entry becomes a
`GenMCP.MCP.V2607.PromptMessage`:

* `{:text, binary}` - a `"user"` message carrying text content.
* `{:assistant, binary}` - an `"assistant"` message carrying text content.
* other content shorthands (`{:image, _}`, `{:audio, _}`, `{:resource, _}`) -
  a `"user"` message carrying that content, built by `content_block/1`. A
  `{:link, _}` shorthand is rejected, since a resource link is not a valid
  prompt message content.
* a ready `%{role: role, content: content}` map or `GenMCP.MCP.V2607.PromptMessage`
  struct is kept as is. Use this to pair the `"assistant"` role with non-text
  content.

The optional `:description` entry sets the result's `description`.

### Examples

The keyword shorthands cover a simple user/assistant exchange:

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> result = MCP.get_prompt_result(text: "hello", assistant: "hi there", description: "greeting")
    iex> result.description
    "greeting"
    iex> Enum.map(result.messages, & &1.role)
    ["user", "assistant"]

To give the assistant role non-text content, pass a full message struct, since
the `{:assistant, _}` shorthand only takes text:

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> result =
    ...>   MCP.get_prompt_result([
    ...>     {:text, "describe this sound"},
    ...>     %MCP.PromptMessage{
    ...>       role: "assistant",
    ...>       content: MCP.content_block({:audio, {"audio/mp3", "aGk="}})
    ...>     }
    ...>   ])
    iex> Enum.map(result.messages, & &1.role)
    ["user", "assistant"]

# `list_prompts_result`

```elixir
@spec list_prompts_result([term()], term() | nil, keyword()) ::
  GenMCP.MCP.V2607.ListPromptsResult.t()
```

Wraps prompts and a cursor into a `GenMCP.MCP.V2607.ListPromptsResult`.

This is the answer to a `prompts/list` request. The `prompts` are placed in the
result as given, and `next_cursor` becomes `nextCursor` (pass `nil` for the
last page).

### Options

* `:cache_scope` - the `cacheScope` cache hint. Defaults to the no-cache hint
  (see `default_cache_control/0`).
* `:ttl_ms` - the `ttlMs` cache hint. Same default.

### Examples

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> result = MCP.list_prompts_result([], nil)
    iex> {result.prompts, result.nextCursor}
    {[], nil}

# `list_resource_templates_result`

```elixir
@spec list_resource_templates_result(
  [term()],
  keyword()
) :: GenMCP.MCP.V2607.ListResourceTemplatesResult.t()
```

Wraps templates into a `GenMCP.MCP.V2607.ListResourceTemplatesResult`.

This is the answer to a `resources/templates/list` request. The `templates` are
placed in the result as given.

### Options

* `:cache_scope` - the `cacheScope` cache hint. Defaults to the no-cache hint
  (see `default_cache_control/0`).
* `:ttl_ms` - the `ttlMs` cache hint. Same default.

### Examples

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> result = MCP.list_resource_templates_result([])
    iex> result.resourceTemplates
    []

# `list_resources_result`

```elixir
@spec list_resources_result([term()], term() | nil, keyword()) ::
  GenMCP.MCP.V2607.ListResourcesResult.t()
```

Wraps resources and a cursor into a `GenMCP.MCP.V2607.ListResourcesResult`.

This is the answer to a `resources/list` request. The `resources` are placed in
the result as given, and `next_cursor` becomes `nextCursor` (pass `nil` for the
last page).

### Options

* `:cache_scope` - the `cacheScope` cache hint. Defaults to the no-cache hint
  (see `default_cache_control/0`).
* `:ttl_ms` - the `ttlMs` cache hint. Same default.

### Examples

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> result = MCP.list_resources_result([], "next-page")
    iex> {result.resources, result.nextCursor}
    {[], "next-page"}

# `list_tools_result`

```elixir
@spec list_tools_result(
  [GenMCP.Suite.Tool.tool() | GenMCP.MCP.V2607.Tool.t()],
  keyword()
) :: GenMCP.MCP.V2607.ListToolsResult.t()
```

Wraps a list of tools into a `GenMCP.MCP.V2607.ListToolsResult`.

This is what a Suite answers a `tools/list` request with. Each element is
either a ready `GenMCP.MCP.V2607.Tool` struct, which is kept as is, or a tool
definition (`t:GenMCP.Suite.Tool.tool/0`: a module, a `{module, arg}` pair, or
a descriptor map), which is converted with `GenMCP.Suite.Tool.describe/1`.

### Options

* `:cache_scope` - the `cacheScope` cache hint. Defaults to the no-cache hint
  (see `default_cache_control/0`).
* `:ttl_ms` - the `ttlMs` cache hint. Same default.

### Examples

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> tool = %MCP.Tool{name: "add", inputSchema: %{"type" => "object"}}
    iex> result = MCP.list_tools_result([tool])
    iex> [%MCP.Tool{name: name}] = result.tools
    iex> name
    "add"

# `read_resource_result`

```elixir
@spec read_resource_result(keyword()) :: GenMCP.MCP.V2607.ReadResourceResult.t()
```

Builds the `GenMCP.MCP.V2607.ReadResourceResult` returned for `resources/read`.

There are two ways to give the contents:

* **Single content (flat form)**: pass `:uri` together with `:text` or `:blob`
  (and optional `:mime_type`). One content entry is built for you with
  `resource_contents/1`.
* **Multiple contents (`:contents` form)**: pass `:contents` with a list of
  content structs (built by `resource_contents/1`) or plain maps. When
  `:contents` is given, the flat `:uri` / `:text` / `:blob` / `:mime_type`
  options are ignored. The list may be empty.

### Options

* `:uri` - the resource URI for the flat form. Required there; raises
  `KeyError` if missing.
* `:text` or `:blob` - the resource body for the flat form. One is required;
  raises `ArgumentError` if neither is given.
* `:mime_type` - optional MIME type for the flat form.
* `:contents` - a list of content entries, used instead of the flat options.
* `:_meta` - metadata set on the result object itself. This is distinct from a
  per-content `_meta`, which you attach through `resource_contents/1`.
* `:cache_scope` - the `cacheScope` cache hint. Defaults to the no-cache hint
  (see `default_cache_control/0`).
* `:ttl_ms` - the `ttlMs` cache hint. Same default.

### Examples

The flat form covers the usual single-file read:

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> result = MCP.read_resource_result(uri: "file:///readme.txt", text: "# Welcome")
    iex> [%MCP.TextResourceContents{uri: uri, text: text}] = result.contents
    iex> {uri, text}
    {"file:///readme.txt", "# Welcome"}

Use the `:contents` form to return several entries, building each with
`resource_contents/1`:

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> result =
    ...>   MCP.read_resource_result(
    ...>     contents: [
    ...>       MCP.resource_contents(uri: "file:///a.txt", text: "first"),
    ...>       MCP.resource_contents(uri: "file:///b.png", blob: "aGk=", mime_type: "image/png")
    ...>     ]
    ...>   )
    iex> length(result.contents)
    2

# `resource_contents`

```elixir
@spec resource_contents(keyword()) ::
  GenMCP.MCP.V2607.TextResourceContents.t()
  | GenMCP.MCP.V2607.BlobResourceContents.t()
```

Builds one resource content entry for `read_resource_result/1`.

Returns a `GenMCP.MCP.V2607.TextResourceContents` when `:text` is given, or a
`GenMCP.MCP.V2607.BlobResourceContents` when `:blob` is given. Use this to
assemble the `:contents` list passed to `read_resource_result/1` when a read
returns more than one entry.

### Options

* `:uri` - the content URI, required. Raises `KeyError` if missing.
* `:text` - the text body. Produces a `TextResourceContents`.
* `:blob` - the base64-encoded binary body. Produces a `BlobResourceContents`.
  Provide exactly one of `:text` or `:blob`, otherwise it raises
  `ArgumentError`.
* `:mime_type` - optional MIME type.
* `:_meta` - optional metadata attached to this content entry.

### Examples

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> contents = MCP.resource_contents(uri: "file:///a.txt", text: "hi", mime_type: "text/plain")
    iex> {contents.uri, contents.text, contents.mimeType}
    {"file:///a.txt", "hi", "text/plain"}

# `server_info`

```elixir
@spec server_info(keyword()) :: GenMCP.MCP.V2607.Implementation.t()
```

Builds the `GenMCP.MCP.V2607.Implementation` struct describing the server.

This is the `io.modelcontextprotocol/serverInfo` metadata carried in the
`_meta` of the `server/discover` response.

### Options

* `:name` - the server name, required. Raises `KeyError` if missing.
* `:version` - the server version, required. Raises `KeyError` if missing.
* `:title` - an optional human-friendly title.

### Examples

    iex> alias GenMCP.MCP.V2607, as: MCP
    iex> info = MCP.server_info(name: "MyServer", version: "1.0.0")
    iex> {info.name, info.version, info.title}
    {"MyServer", "1.0.0", nil}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
