# `ExMCP.Types`
[🔗](https://github.com/azmaveth/ex_mcp/blob/v1.0.0/lib/ex_mcp/types.ex#L1)

Type definitions for the Model Context Protocol.

MCP **2026-07-28** is the latest stable protocol revision. This module holds
the shared type surface used across both protocol eras. Version-specific
modules document the wire differences for each revision.

## Version-specific modules

- `ExMCP.Types.V20241105` — initial stable version
- `ExMCP.Types.V20250326` — subscriptions / batch era
- `ExMCP.Types.V20250618` — structured tool output; batching removed
- `ExMCP.Types.V20251125` — tasks, icons, URL elicitation, sampling tool calls
- `ExMCP.Types.V20260728` — latest stable stateless era, MRTR, discovery, caching

Prefer the documented `:protocol_mode` client/server option for negotiation
rather than hard-coding version strings in application code.

Unqualified initialize results, roots, sampling, session, and legacy task
types remain here for 1.x source compatibility. For fields that changed on
the wire, use `ExMCP.Types.V20260728` as the modern reference instead of
assuming the shared legacy shape applies to MCP 2026-07-28.

> #### Protocol-mode rollout {: .info}
>
> rc.6 defaults new connections to `:prefer_modern`, with evidence-based
> fallback to legacy peers. The zero-arity legacy compatibility accessor
> continues to return 2025-11-25 during the soak and throughout 1.x.

# `annotations`

```elixir
@type annotations() :: %{
  optional(:audience) =&gt; [role()],
  optional(:priority) =&gt; float(),
  optional(:lastModified) =&gt; String.t()
}
```

# `atom_initialize_result`

```elixir
@type atom_initialize_result() :: %{
  :protocolVersion =&gt; String.t(),
  :capabilities =&gt; server_capabilities(),
  :serverInfo =&gt; server_info(),
  optional(:instructions) =&gt; String.t()
}
```

# `audio_content`

```elixir
@type audio_content() :: %{
  :type =&gt; :audio,
  :data =&gt; String.t(),
  :mimeType =&gt; String.t(),
  optional(:annotations) =&gt; annotations()
}
```

# `blob_resource_contents`

```elixir
@type blob_resource_contents() :: %{
  :uri =&gt; String.t(),
  :blob =&gt; String.t(),
  optional(:mimeType) =&gt; String.t()
}
```

# `cache_scope`

```elixir
@type cache_scope() :: :public | :private | String.t()
```

# `cacheable_result`

```elixir
@type cacheable_result() :: %{
  optional(:resultType) =&gt; String.t(),
  optional(:ttlMs) =&gt; non_neg_integer(),
  optional(:cacheScope) =&gt; cache_scope()
}
```

# `call_tool_request`

```elixir
@type call_tool_request() :: %{
  :name =&gt; String.t(),
  optional(:arguments) =&gt; %{required(String.t()) =&gt; any()}
}
```

# `call_tool_result`

```elixir
@type call_tool_result() :: tool_result()
```

# `cancelled_notification`

```elixir
@type cancelled_notification() :: %{
  :requestId =&gt; request_id(),
  optional(:reason) =&gt; String.t()
}
```

# `client_capabilities`

```elixir
@type client_capabilities() :: %{
  optional(:experimental) =&gt; %{required(String.t()) =&gt; map()},
  optional(:roots) =&gt; %{optional(:listChanged) =&gt; boolean()},
  optional(:sampling) =&gt; %{},
  optional(:elicitation) =&gt; %{},
  optional(:extensions) =&gt; %{required(String.t()) =&gt; map()}
}
```

# `client_info`

```elixir
@type client_info() :: implementation()
```

# `client_notification`

```elixir
@type client_notification() ::
  cancelled_notification()
  | progress_notification()
  | list_changed_notification()
```

# `client_request`

```elixir
@type client_request() ::
  ping_request()
  | initialize_request()
  | list_resources_request()
  | read_resource_request()
  | list_prompts_request()
  | get_prompt_request()
  | list_tools_request()
  | call_tool_request()
  | complete_request()
  | set_level_request()
  | subscribe_request()
  | unsubscribe_request()
```

# `client_result`

```elixir
@type client_result() ::
  empty_result()
  | create_message_result()
  | list_roots_result()
  | elicit_result()
```

# `complete_argument`

```elixir
@type complete_argument() :: completion_argument()
```

# `complete_ref`

```elixir
@type complete_ref() :: completion_reference()
```

# `complete_request`

```elixir
@type complete_request() :: %{
  ref: completion_reference(),
  argument: completion_argument()
}
```

# `complete_result`

```elixir
@type complete_result() :: completion_result()
```

# `completion_argument`

```elixir
@type completion_argument() :: %{name: String.t(), value: String.t()}
```

# `completion_reference`

```elixir
@type completion_reference() :: resource_reference() | prompt_reference()
```

# `completion_result`

```elixir
@type completion_result() :: %{
  completion: %{
    :values =&gt; [String.t()],
    optional(:total) =&gt; integer(),
    optional(:hasMore) =&gt; boolean()
  }
}
```

# `content`

```elixir
@type content() ::
  text_content() | image_content() | audio_content() | embedded_resource()
```

# `content_type`

```elixir
@type content_type() :: :text | :image | :audio | :resource
```

# `create_message_params`

```elixir
@type create_message_params() :: %{
  :messages =&gt; [sampling_message()],
  :maxTokens =&gt; integer(),
  optional(:modelPreferences) =&gt; model_preferences(),
  optional(:systemPrompt) =&gt; String.t(),
  optional(:includeContext) =&gt; include_context(),
  optional(:temperature) =&gt; float(),
  optional(:stopSequences) =&gt; [String.t()],
  optional(:metadata) =&gt; map(),
  optional(:tools) =&gt; [tool()],
  optional(:toolChoice) =&gt; tool_choice()
}
```

# `create_message_result`

```elixir
@type create_message_result() :: %{
  :role =&gt; role(),
  :content =&gt; text_content() | image_content() | audio_content(),
  :model =&gt; String.t(),
  optional(:stopReason) =&gt; String.t()
}
```

# `cursor`

```elixir
@type cursor() :: String.t()
```

# `elicit_request`

```elixir
@type elicit_request() :: %{
  message: String.t(),
  requestedSchema: %{
    :type =&gt; String.t(),
    :properties =&gt; %{required(String.t()) =&gt; primitive_schema()},
    optional(:required) =&gt; [String.t()]
  }
}
```

# `elicit_result`

```elixir
@type elicit_result() :: %{
  :action =&gt; :accept | :decline | :cancel,
  optional(:content) =&gt; %{required(String.t()) =&gt; any()}
}
```

# `embedded_resource`

```elixir
@type embedded_resource() :: %{
  :type =&gt; :resource,
  :resource =&gt; resource_contents(),
  optional(:annotations) =&gt; annotations()
}
```

# `empty_result`

```elixir
@type empty_result() :: %{}
```

# `error_code`

```elixir
@type error_code() :: integer()
```

# `error_data`

```elixir
@type error_data() :: any()
```

# `get_prompt_request`

```elixir
@type get_prompt_request() :: %{
  :name =&gt; String.t(),
  optional(:arguments) =&gt; %{required(String.t()) =&gt; String.t()}
}
```

# `get_prompt_result`

```elixir
@type get_prompt_result() :: %{
  optional(:description) =&gt; String.t(),
  messages: [prompt_message()]
}
```

# `icon`

```elixir
@type icon() :: %{
  :type =&gt; String.t(),
  :uri =&gt; String.t(),
  optional(:mediaType) =&gt; String.t()
}
```

# `image_content`

```elixir
@type image_content() :: %{
  :type =&gt; :image,
  :data =&gt; String.t(),
  :mimeType =&gt; String.t(),
  optional(:annotations) =&gt; annotations()
}
```

# `implementation`

```elixir
@type implementation() :: %{
  :name =&gt; String.t(),
  :version =&gt; String.t(),
  optional(:title) =&gt; String.t(),
  optional(:description) =&gt; String.t()
}
```

# `include_context`

```elixir
@type include_context() :: :none | :thisServer | :allServers
```

# `initialize_request`

```elixir
@type initialize_request() :: %{
  protocolVersion: String.t(),
  capabilities: client_capabilities(),
  clientInfo: client_info()
}
```

# `initialize_result`

```elixir
@type initialize_result() :: atom_initialize_result() | wire_initialize_result()
```

# `json_schema`

```elixir
@type json_schema() :: map()
```

# `jsonrpc_error`

```elixir
@type jsonrpc_error() :: %{
  :code =&gt; error_code(),
  :message =&gt; String.t(),
  optional(:data) =&gt; error_data()
}
```

# `jsonrpc_error_response`

```elixir
@type jsonrpc_error_response() :: %{
  jsonrpc: String.t(),
  id: request_id(),
  error: jsonrpc_error()
}
```

# `jsonrpc_message`

```elixir
@type jsonrpc_message() ::
  jsonrpc_request()
  | jsonrpc_notification()
  | jsonrpc_response()
  | jsonrpc_error_response()
```

# `jsonrpc_notification`

```elixir
@type jsonrpc_notification() :: %{
  :jsonrpc =&gt; String.t(),
  :method =&gt; String.t(),
  optional(:params) =&gt; map()
}
```

# `jsonrpc_request`

```elixir
@type jsonrpc_request() :: %{
  :jsonrpc =&gt; String.t(),
  :id =&gt; request_id(),
  :method =&gt; String.t(),
  optional(:params) =&gt; map()
}
```

# `jsonrpc_response`

```elixir
@type jsonrpc_response() :: %{jsonrpc: String.t(), id: request_id(), result: any()}
```

# `list_changed_notification`

```elixir
@type list_changed_notification() :: %{}
```

# `list_prompts_request`

```elixir
@type list_prompts_request() :: paginated_request()
```

# `list_prompts_result`

```elixir
@type list_prompts_result() :: %{
  :prompts =&gt; [prompt()],
  optional(:nextCursor) =&gt; cursor()
}
```

# `list_resource_templates_request`

```elixir
@type list_resource_templates_request() :: paginated_request()
```

# `list_resource_templates_result`

```elixir
@type list_resource_templates_result() :: %{
  :resourceTemplates =&gt; [resource_template()],
  optional(:nextCursor) =&gt; cursor()
}
```

# `list_resources_request`

```elixir
@type list_resources_request() :: paginated_request()
```

# `list_resources_result`

```elixir
@type list_resources_result() :: %{
  :resources =&gt; [resource()],
  optional(:nextCursor) =&gt; cursor()
}
```

# `list_roots_request`

```elixir
@type list_roots_request() :: %{}
```

# `list_roots_result`

```elixir
@type list_roots_result() :: %{roots: [root()]}
```

# `list_tools_request`

```elixir
@type list_tools_request() :: paginated_request()
```

# `list_tools_result`

```elixir
@type list_tools_result() :: %{:tools =&gt; [tool()], optional(:nextCursor) =&gt; cursor()}
```

# `log_level`

```elixir
@type log_level() ::
  :debug | :info | :notice | :warning | :error | :critical | :alert | :emergency
```

# `log_level_string`

```elixir
@type log_level_string() :: String.t()
```

# `log_notification`

```elixir
@type log_notification() :: %{
  :level =&gt; log_level_string(),
  optional(:logger) =&gt; String.t(),
  data: any()
}
```

# `model_hint`

```elixir
@type model_hint() :: %{optional(:name) =&gt; String.t()}
```

# `model_preferences`

```elixir
@type model_preferences() :: %{
  optional(:hints) =&gt; [model_hint()],
  optional(:costPriority) =&gt; float(),
  optional(:speedPriority) =&gt; float(),
  optional(:intelligencePriority) =&gt; float()
}
```

# `paginated_request`

```elixir
@type paginated_request() :: %{optional(:cursor) =&gt; cursor()}
```

# `paginated_result`

```elixir
@type paginated_result() :: %{optional(:nextCursor) =&gt; cursor()}
```

# `ping_request`

```elixir
@type ping_request() :: %{}
```

# `primitive_schema`

```elixir
@type primitive_schema() :: %{
  :type =&gt; String.t(),
  optional(:title) =&gt; String.t(),
  optional(:description) =&gt; String.t(),
  optional(:enum) =&gt; [String.t()],
  optional(:enumNames) =&gt; [String.t()],
  optional(:default) =&gt; any(),
  optional(:minLength) =&gt; integer(),
  optional(:maxLength) =&gt; integer(),
  optional(:minimum) =&gt; number(),
  optional(:maximum) =&gt; number(),
  optional(:format) =&gt; String.t()
}
```

# `progress_notification`

```elixir
@type progress_notification() :: %{
  :progressToken =&gt; progress_token(),
  :progress =&gt; number(),
  optional(:total) =&gt; number(),
  optional(:message) =&gt; String.t()
}
```

# `progress_token`

```elixir
@type progress_token() :: String.t() | integer()
```

# `prompt`

```elixir
@type prompt() :: %{
  :name =&gt; String.t(),
  optional(:title) =&gt; String.t(),
  optional(:description) =&gt; String.t(),
  optional(:arguments) =&gt; [prompt_argument() | map()],
  optional(:icons) =&gt; [icon()],
  optional(:_meta) =&gt; map()
}
```

# `prompt_argument`

```elixir
@type prompt_argument() :: %{
  :name =&gt; String.t(),
  optional(:description) =&gt; String.t(),
  optional(:required) =&gt; boolean()
}
```

# `prompt_message`

```elixir
@type prompt_message() :: %{role: role(), content: content()}
```

# `prompt_reference`

```elixir
@type prompt_reference() :: %{type: :&quot;ref/prompt&quot;, name: String.t()}
```

# `protocol_mode`

```elixir
@type protocol_mode() :: :legacy_only | :modern_only | :prefer_legacy | :prefer_modern
```

# `read_resource_request`

```elixir
@type read_resource_request() :: %{uri: String.t()}
```

# `read_resource_result`

```elixir
@type read_resource_result() :: %{contents: [resource_contents()]}
```

# `request_id`

```elixir
@type request_id() :: String.t() | integer()
```

# `resource`

```elixir
@type resource() :: %{
  :uri =&gt; String.t(),
  :name =&gt; String.t(),
  optional(:title) =&gt; String.t(),
  optional(:description) =&gt; String.t(),
  optional(:mimeType) =&gt; String.t(),
  optional(:annotations) =&gt; annotations(),
  optional(:size) =&gt; integer(),
  optional(:icons) =&gt; [icon()],
  optional(:_meta) =&gt; map()
}
```

# `resource_contents`

```elixir
@type resource_contents() :: text_resource_contents() | blob_resource_contents()
```

# `resource_reference`

```elixir
@type resource_reference() :: %{type: :&quot;ref/resource&quot;, uri: String.t()}
```

# `resource_template`

```elixir
@type resource_template() :: %{
  :uriTemplate =&gt; String.t(),
  :name =&gt; String.t(),
  optional(:title) =&gt; String.t(),
  optional(:description) =&gt; String.t(),
  optional(:mimeType) =&gt; String.t(),
  optional(:annotations) =&gt; annotations(),
  optional(:icons) =&gt; [icon()],
  optional(:_meta) =&gt; map()
}
```

# `resource_updated_notification`

```elixir
@type resource_updated_notification() :: %{uri: String.t()}
```

# `role`

```elixir
@type role() :: :user | :assistant
```

# `role_string`

```elixir
@type role_string() :: String.t()
```

# `root`

```elixir
@type root() :: %{:uri =&gt; String.t(), optional(:name) =&gt; String.t()}
```

# `sampling_message`

```elixir
@type sampling_message() :: %{
  role: role(),
  content: text_content() | image_content() | audio_content()
}
```

# `server_capabilities`

```elixir
@type server_capabilities() :: %{
  optional(:experimental) =&gt; %{required(String.t()) =&gt; map()},
  optional(:logging) =&gt; %{},
  optional(:completions) =&gt; %{},
  optional(:prompts) =&gt; %{optional(:listChanged) =&gt; boolean()},
  optional(:resources) =&gt; %{
    optional(:subscribe) =&gt; boolean(),
    optional(:listChanged) =&gt; boolean()
  },
  optional(:tools) =&gt; %{optional(:listChanged) =&gt; boolean()},
  optional(:extensions) =&gt; %{required(String.t()) =&gt; map()}
}
```

# `server_info`

```elixir
@type server_info() :: implementation()
```

# `server_notification`

```elixir
@type server_notification() ::
  cancelled_notification()
  | progress_notification()
  | log_notification()
  | resource_updated_notification()
  | list_changed_notification()
```

# `server_request`

```elixir
@type server_request() ::
  ping_request()
  | create_message_params()
  | list_roots_request()
  | elicit_request()
```

# `server_result`

```elixir
@type server_result() ::
  empty_result()
  | initialize_result()
  | complete_result()
  | get_prompt_result()
  | list_prompts_result()
  | list_resource_templates_result()
  | list_resources_result()
  | read_resource_result()
  | call_tool_result()
  | list_tools_result()
```

# `set_level_request`

```elixir
@type set_level_request() :: %{level: log_level_string()}
```

# `subscribe_request`

```elixir
@type subscribe_request() :: %{uri: String.t()}
```

# `subscribe_result`

```elixir
@type subscribe_result() :: %{}
```

# `task`

```elixir
@type task() :: %{
  :id =&gt; String.t(),
  :state =&gt; task_state(),
  optional(:toolName) =&gt; String.t(),
  optional(:arguments) =&gt; map(),
  optional(:createdAt) =&gt; String.t(),
  optional(:ttl) =&gt; integer(),
  optional(:result) =&gt; tool_result(),
  optional(:metadata) =&gt; map()
}
```

# `task_state`

```elixir
@type task_state() :: :working | :input_required | :completed | :failed | :cancelled
```

# `text_content`

```elixir
@type text_content() :: %{
  :type =&gt; :text,
  :text =&gt; String.t(),
  optional(:annotations) =&gt; annotations()
}
```

# `text_resource_contents`

```elixir
@type text_resource_contents() :: %{
  :uri =&gt; String.t(),
  :text =&gt; String.t(),
  optional(:mimeType) =&gt; String.t()
}
```

# `tool`

```elixir
@type tool() :: %{
  :name =&gt; String.t(),
  optional(:description) =&gt; String.t(),
  optional(:title) =&gt; String.t(),
  :inputSchema =&gt; json_schema(),
  optional(:outputSchema) =&gt; json_schema(),
  optional(:annotations) =&gt; tool_annotations(),
  optional(:icons) =&gt; [icon()],
  optional(:execution) =&gt; map(),
  optional(:_meta) =&gt; map()
}
```

# `tool_annotations`

```elixir
@type tool_annotations() :: %{
  optional(:title) =&gt; String.t(),
  optional(:readOnlyHint) =&gt; boolean(),
  optional(:destructiveHint) =&gt; boolean(),
  optional(:idempotentHint) =&gt; boolean(),
  optional(:openWorldHint) =&gt; boolean()
}
```

# `tool_choice`

```elixir
@type tool_choice() :: %{type: String.t()}
```

# `tool_result`

```elixir
@type tool_result() :: %{
  :content =&gt; [content()],
  optional(:isError) =&gt; boolean(),
  optional(:structuredContent) =&gt; any()
}
```

# `tool_result_content`

```elixir
@type tool_result_content() :: %{
  :type =&gt; String.t(),
  :tool_use_id =&gt; String.t(),
  :content =&gt; [content()],
  optional(:isError) =&gt; boolean()
}
```

# `tool_use_content`

```elixir
@type tool_use_content() :: %{
  type: String.t(),
  id: String.t(),
  name: String.t(),
  input: map()
}
```

# `transport`

```elixir
@type transport() :: :stdio | :http | module()
```

# `unsubscribe_request`

```elixir
@type unsubscribe_request() :: %{uri: String.t()}
```

# `unsubscribe_result`

```elixir
@type unsubscribe_result() :: %{}
```

# `wire_initialize_result`

```elixir
@type wire_initialize_result() :: %{required(String.t()) =&gt; any()}
```

# `content_type_to_string`

```elixir
@spec content_type_to_string(content_type()) :: String.t()
```

Converts a content type atom to a string.

# `include_context_to_string`

```elixir
@spec include_context_to_string(include_context()) :: String.t()
```

Converts an include context atom to a string.

# `internal_error`

# `invalid_params`

# `invalid_request`

# `jsonrpc_version`

# `latest_protocol_version`

```elixir
@spec latest_protocol_version() :: String.t()
```

Returns the newest legacy protocol revision used by compatibility helpers.

# `log_level_to_string`

```elixir
@spec log_level_to_string(log_level()) :: String.t()
```

Converts a log level atom to a string.

# `method_not_found`

# `parse_error`

# `role_to_string`

```elixir
@spec role_to_string(role()) :: String.t()
```

Converts a role atom to a string.

# `string_to_content_type`

```elixir
@spec string_to_content_type(String.t()) :: content_type()
```

Converts a string content type to an atom.

# `string_to_include_context`

```elixir
@spec string_to_include_context(String.t()) :: include_context()
```

Converts a string include context to an atom.

# `string_to_log_level`

```elixir
@spec string_to_log_level(String.t()) :: log_level()
```

Converts a string log level to an atom.

# `string_to_role`

```elixir
@spec string_to_role(String.t()) :: role()
```

Converts a string role to an atom.

---

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