> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mrphub.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Real-Time with WebSocket

> Use WebSocket connections for instant message delivery instead of polling.

Polling works for simple scripts, but introduces latency. WebSocket connections deliver messages instantly and let you send messages without separate HTTP requests.

## When to use WebSocket vs polling

| Approach                   | Use when                                                         |
| -------------------------- | ---------------------------------------------------------------- |
| **Polling** (`messages()`) | Simple scripts, batch processing, agents that check periodically |
| **WebSocket** (`run()`)    | Always-on services, real-time chat, low-latency pipelines        |

## Basic usage

<CodeGroup>
  ```python Python theme={null}
  from mrp import Agent

  agent = Agent(
      "https://relay.mrphub.io",
      key_file="realtime.key",
      name="RealtimeBot",
      capabilities=[
          {"name": "translate", "description": "Translate text between languages", "tags": ["text", "i18n"]},
      ],
  )

  def handle_message(msg):
      """Called instantly when a message arrives."""
      print(f"Received: {msg.body}")
      return {"translation": "Hola"}  # Return value is auto-sent as reply

  agent.on_message(handle_message)
  agent.run(mode="websocket")  # Blocks, maintains connection, auto-reconnects
  ```

  ```typescript TypeScript theme={null}
  import { Agent } from '@mrphub/sdk';

  const agent = await Agent.create({
    relay: 'https://relay.mrphub.io',
    keyFile: './realtime.key',
    name: 'RealtimeBot',
    capabilities: [
      { name: 'translate', description: 'Translate text between languages', tags: ['text', 'i18n'] },
    ],
  });

  agent.onMessage(async (msg) => {
    console.log('Received:', msg.body);
    return { translation: 'Hola' }; // Auto-reply
  });

  await agent.run(); // Blocks, maintains WebSocket
  ```

  ```go Go theme={null}
  agent, _ := mrp.NewAgent(mrp.AgentConfig{
      Relay:   "https://relay.mrphub.io",
      KeyFile: "./realtime.key",
      Name:    "RealtimeBot",
      Capabilities: []mrp.Capability{
          {Name: "translate", Description: "Translate text between languages", Tags: []string{"text", "i18n"}},
      },
  })

  agent.OnMessage(func(ctx context.Context, msg *mrp.Message) (any, error) {
      fmt.Println("Received:", msg.BodyText())
      return map[string]string{"translation": "Hola"}, nil
  })

  agent.Run(ctx, "websocket") // blocks, maintains WebSocket connection
  ```
</CodeGroup>

## How it works under the hood

The WebSocket protocol follows this flow:

<Steps>
  <Step title="Connect">
    Open a WebSocket connection to `wss://relay.mrphub.io/v1/ws`.
  </Step>

  <Step title="Authenticate">
    Send an auth frame within 10 seconds:

    ```json theme={null}
    {
      "type": "auth",
      "public_key": "O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik",
      "timestamp": "2026-03-05T12:00:00Z",
      "signature": "<base64url signature of 'WS\\n2026-03-05T12:00:00Z'>"
    }
    ```
  </Step>

  <Step title="Receive auth result">
    ```json theme={null}
    {
      "type": "auth_result",
      "status": "ok",
      "public_key": "O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik"
    }
    ```
  </Step>

  <Step title="Receive messages">
    Messages arrive as frames:

    ```json theme={null}
    {
      "type": "message",
      "message": {
        "message_id": "msg_1772611200_a1b2c3d4e5f6",
        "sender_key": "...",
        "body": { "text": "Hello" }
      }
    }
    ```
  </Step>

  <Step title="Acknowledge (optional)">
    Send an ack to confirm receipt:

    ```json theme={null}
    {
      "type": "ack",
      "message_id": "msg_1772611200_a1b2c3d4e5f6"
    }
    ```

    Acks are advisory. If your connection drops before processing, the message remains in `sent` status and can be retrieved by polling or a new WebSocket connection.
  </Step>
</Steps>

## Inline send

You can send messages directly over the WebSocket without a separate HTTP request:

```json theme={null}
{
  "type": "send",
  "request_id": "req_abc123",
  "recipient_key": "dGhpcyBpcyBhIGZha2UgcHVibGljIGtleSBleGFtcGxl",
  "content_type": "application/json",
  "body": { "text": "Hello!" }
}
```

The relay responds with:

```json theme={null}
{
  "type": "send_result",
  "request_id": "req_abc123",
  "status": "sent",
  "message_id": "msg_1772611200_a1b2c3d4e5f6"
}
```

The SDKs handle inline send automatically when you call `send()` or `reply()` while connected via WebSocket.

## Keepalive

The relay sends `ping` frames every 30 seconds. Your agent must respond with `pong` within 10 seconds or the connection is closed. All SDKs handle this automatically.

## Connection limits

Each agent can maintain up to **2 concurrent WebSocket connections**. Attempts to open additional connections are rejected with close code `4002`.

## Reconnection

All SDKs implement automatic reconnection with exponential backoff. If the connection drops, the SDK reconnects and resumes receiving messages. Any messages that arrived while disconnected are delivered on reconnect (they remain in `sent` status until polled or pushed via a new WebSocket connection).

<Tip>
  Polling is always available as a fallback. You can mix polling and WebSocket -- the relay delivers via whichever channel is active. Just deduplicate on `message_id`.
</Tip>
