# `Hermolaos.Transport`
[🔗](https://github.com/nyo16/hermolaos/blob/v0.5.0/lib/hermolaos/transport/behaviour.ex#L1)

Behaviour defining the transport interface for MCP communication.

A transport is responsible for the low-level communication between an MCP
client and server. MCP supports two standard transports:

- **stdio**: Communication via stdin/stdout with a subprocess
- **HTTP/SSE**: Communication via HTTP POST with optional Server-Sent Events

## Implementing a Custom Transport

To create a custom transport, implement this behaviour:

    defmodule MyApp.CustomTransport do
      @behaviour Hermolaos.Transport
      use GenServer

      @impl Hermolaos.Transport
      def start_link(opts) do
        GenServer.start_link(__MODULE__, opts)
      end

      @impl Hermolaos.Transport
      def send_message(transport, message) do
        GenServer.call(transport, {:send, message})
      end

      @impl Hermolaos.Transport
      def close(transport) do
        GenServer.stop(transport)
      end

      @impl Hermolaos.Transport
      def connected?(transport) do
        GenServer.call(transport, :connected?)
      end

      # GenServer callbacks...
    end

## Message Flow

Transports communicate with their owner (typically a Connection GenServer)
via messages:

- `{:transport_ready, pid}` - Transport is ready for messages
- `{:transport_message, pid, message}` - Received a message from server
- `{:transport_closed, pid, reason}` - Transport connection closed
- `{:transport_error, pid, error}` - Transport error occurred

## Options

All transports accept these common options:

- `:owner` - The PID to send messages to (required)
- `:name` - Optional name for the transport process

# `message`

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

# `send_result`

```elixir
@type send_result() :: :ok | {:error, term()}
```

# `start_result`

```elixir
@type start_result() :: {:ok, pid()} | {:error, term()}
```

# `t`

```elixir
@type t() :: pid() | GenServer.name()
```

# `cast_message`
*optional* 

```elixir
@callback cast_message(transport :: t(), message :: message()) :: :ok
```

Sends a message asynchronously (fire-and-forget).

This is an optional callback for transports that support non-blocking sends.
If not implemented, defaults to calling `send_message/2`.

# `close`

```elixir
@callback close(transport :: t()) :: :ok
```

Closes the transport connection.

This should gracefully shut down the transport, cleaning up any resources.
The transport should send `{:transport_closed, self(), :normal}` to the
owner before terminating.

## Returns

- `:ok` on success

# `connected?`

```elixir
@callback connected?(transport :: t()) :: boolean()
```

Checks if the transport is currently connected.

## Returns

- `true` if connected and ready for messages
- `false` otherwise

# `info`
*optional* 

```elixir
@callback info(transport :: t()) :: map()
```

Returns transport-specific information.

This is an optional callback for transports to expose metadata like
session IDs, connection state, or statistics.

# `send_message`

```elixir
@callback send_message(transport :: t(), message :: message()) :: send_result()
```

Sends a JSON-RPC message through the transport.

The message should be a map that will be JSON-encoded by the transport.

## Parameters

- `transport` - The transport process (PID or name)
- `message` - The JSON-RPC message map to send

## Returns

- `:ok` on success
- `{:error, reason}` on failure

## Notes

This function may block until the message is sent, or it may be
asynchronous depending on the transport implementation. For non-blocking
sends, consider using `cast_message/2` if available.

# `start_link`

```elixir
@callback start_link(opts :: keyword()) :: start_result()
```

Starts the transport process.

The transport should send `{:transport_ready, self()}` to the owner
when it's ready to send and receive messages.

## Options

- `:owner` - PID to receive transport messages (required)
- Other options are transport-specific

## Returns

- `{:ok, pid}` on success
- `{:error, reason}` on failure

# `send`

```elixir
@spec send(module(), t(), message()) :: send_result()
```

Sends a message, falling back to sync send if async is not available.

## Examples

    Hermolaos.Transport.send(transport_mod, transport_pid, message)

# `valid_transport?`

```elixir
@spec valid_transport?(module()) :: boolean()
```

Validates that a module implements the Transport behaviour.

## Examples

    iex> Hermolaos.Transport.valid_transport?(Hermolaos.Transport.Stdio)
    true

    iex> Hermolaos.Transport.valid_transport?(String)
    false

---

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