ExMCP.Content.Protocol (ex_mcp v0.7.4)

View Source

Content protocol for ExMCP - type-safe content handling system.

This module defines the core protocol and types for handling different content types in MCP messages, providing a unified interface for text, images, audio, and embedded resources.

Content Types

  • Text: Plain text, markdown, code, etc.
  • Image: Base64-encoded images with metadata
  • Audio: Base64-encoded audio with metadata
  • Resource: Embedded resource references
  • Annotation: Metadata annotations for content

Design Principles

  1. Type Safety: All content has compile-time type checking
  2. Extensibility: Easy to add new content types
  3. Validation: Built-in validation for content structure
  4. Performance: Efficient serialization/deserialization
  5. Developer Experience: Intuitive constructors and builders

Summary

Types

Annotation content for metadata

Audio content with base64 data and metadata

Base content protocol for all content types

Image content with base64 data and metadata

Resource reference content

Content serialization options

Text content with optional formatting

Content validation result

Functions

Creates new annotation content.

Creates new audio content from base64 data.

Gets the content type for given content.

Checks if content is of a specific type.

Deserializes content from MCP protocol format.

Creates new image content from base64 data.

Creates new resource reference content.

Serializes content to MCP protocol format.

Creates new text content.

Validates content structure and data integrity.

Types

annotation()

@type annotation() :: %{
  type: :annotation,
  annotation: %{
    type: String.t(),
    confidence: float() | nil,
    text: String.t() | nil
  },
  metadata: map()
}

Annotation content for metadata

audio()

@type audio() :: %{
  type: :audio,
  data: String.t(),
  mime_type: String.t(),
  duration: float() | nil,
  transcript: String.t() | nil,
  metadata: map()
}

Audio content with base64 data and metadata

content()

@type content() :: text() | image() | audio() | resource() | annotation()

Base content protocol for all content types

image()

@type image() :: %{
  type: :image,
  data: String.t(),
  mime_type: String.t(),
  width: pos_integer() | nil,
  height: pos_integer() | nil,
  alt_text: String.t() | nil,
  metadata: map()
}

Image content with base64 data and metadata

resource()

@type resource() :: %{
  type: :resource,
  resource: %{
    uri: String.t(),
    text: String.t() | nil,
    mime_type: String.t() | nil
  },
  metadata: map()
}

Resource reference content

serialize_opts()

@type serialize_opts() :: [
  format: :mcp | :json | :compact,
  validate: boolean(),
  include_metadata: boolean()
]

Content serialization options

text()

@type text() :: %{
  type: :text,
  text: String.t(),
  format: :plain | :markdown | :code | :html,
  language: String.t() | nil,
  metadata: map()
}

Text content with optional formatting

validation_result()

@type validation_result() :: :ok | {:error, String.t()}

Content validation result

Functions

annotation(annotation_type, opts \\ [])

@spec annotation(
  String.t(),
  keyword()
) :: annotation()

Creates new annotation content.

Examples

iex> ExMCP.Content.Protocol.annotation("sentiment", confidence: 0.95, text: "positive")
%{type: :annotation, annotation: %{type: "sentiment", confidence: 0.95, text: "positive"}, metadata: %{}}

audio(base64_data, mime_type, opts \\ [])

@spec audio(String.t(), String.t(), keyword()) :: audio()

Creates new audio content from base64 data.

Examples

iex> data = Base.encode64("fake audio data")
iex> ExMCP.Content.Protocol.audio(data, "audio/wav")
%{type: :audio, data: data, mime_type: "audio/wav", duration: nil, transcript: nil, metadata: %{}}

content_type(map)

@spec content_type(content()) :: atom()

Gets the content type for given content.

Examples

iex> content = ExMCP.Content.Protocol.text("Hello")
iex> ExMCP.Content.Protocol.content_type(content)
:text

content_type?(content, type)

@spec content_type?(content(), atom()) :: boolean()

Checks if content is of a specific type.

Examples

iex> content = ExMCP.Content.Protocol.text("Hello")
iex> ExMCP.Content.Protocol.content_type?(content, :text)
true
iex> ExMCP.Content.Protocol.content_type?(content, :image)
false

deserialize(data)

@spec deserialize(map()) :: {:ok, content()} | {:error, String.t()}

Deserializes content from MCP protocol format.

Examples

iex> data = %{"type" => "text", "text" => "Hello"}
iex> ExMCP.Content.Protocol.deserialize(data)
{:ok, %{type: :text, text: "Hello", format: :plain, language: nil, metadata: %{}}}

image(base64_data, mime_type, opts \\ [])

@spec image(String.t(), String.t(), keyword()) :: image()

Creates new image content from base64 data.

Examples

iex> data = Base.encode64("fake image data")
iex> ExMCP.Content.Protocol.image(data, "image/png")
%{type: :image, data: data, mime_type: "image/png", width: nil, height: nil, alt_text: nil, metadata: %{}}

resource(uri, opts \\ [])

@spec resource(
  String.t(),
  keyword()
) :: resource()

Creates new resource reference content.

Examples

iex> ExMCP.Content.Protocol.resource("file://data.txt")
%{type: :resource, resource: %{uri: "file://data.txt", text: nil, mime_type: nil}, metadata: %{}}

iex> ExMCP.Content.Protocol.resource("file://doc.pdf", text: "Important document", mime_type: "application/pdf")
%{type: :resource, resource: %{uri: "file://doc.pdf", text: "Important document", mime_type: "application/pdf"}, metadata: %{}}

serialize(content, opts \\ [])

@spec serialize(content(), serialize_opts()) :: map()

Serializes content to MCP protocol format.

Examples

iex> content = ExMCP.Content.Protocol.text("Hello")
iex> ExMCP.Content.Protocol.serialize(content)
%{"type" => "text", "text" => "Hello"}

iex> content2 = ExMCP.Content.Protocol.text("Hello")
iex> ExMCP.Content.Protocol.serialize(content2, format: :compact)
%{"type" => "text", "text" => "Hello"}

text(content, opts \\ [])

@spec text(
  String.t(),
  keyword()
) :: text()

Creates new text content.

Examples

iex> ExMCP.Content.Protocol.text("Hello, world!")
%{type: :text, text: "Hello, world!", format: :plain, language: nil, metadata: %{}}

iex> ExMCP.Content.Protocol.text("# Header", format: :markdown)
%{type: :text, text: "# Header", format: :markdown, language: nil, metadata: %{}}

iex> ExMCP.Content.Protocol.text("console.log('hi')", format: :code, language: "javascript")
%{type: :text, text: "console.log('hi')", format: :code, language: "javascript", metadata: %{}}

validate(content)

@spec validate(content()) :: validation_result()

Validates content structure and data integrity.

Examples

iex> content = ExMCP.Content.Protocol.text("Hello")
iex> ExMCP.Content.Protocol.validate(content)
:ok

iex> invalid = %{type: :text, text: nil}
iex> ExMCP.Content.Protocol.validate(invalid)
{:error, "Text content must have non-nil text field"}