# How to Add a New Platform Channel for an Unsupported Website to Agent Reach

> Learn how to add a new platform channel for unsupported websites to Agent Reach. This guide covers implementing URL detection, health checks, and registry registration.

- Repository: [Pnant/Agent-Reach](https://github.com/Panniantong/Agent-Reach)
- Tags: how-to-guide
- Published: 2026-07-09

---

**Adding a new platform channel to Agent Reach requires creating a Python subclass in `agent_reach/channels/`, implementing URL detection and health-check logic, and registering the class in the channel registry.**

Agent Reach treats every supported platform as a **channel**—a thin wrapper that routes calls to upstream CLI tools or APIs. If you need to add support for a website that is not yet covered, you will extend the base architecture without modifying core upstream logic. This guide walks through the exact implementation pattern used in the `Panniantong/Agent-Reach` repository.

## Understanding the Channel Architecture

Agent Reach channels follow a **backend-agnostic pattern**. Each channel declares a list of potential backends (such as platform-specific CLIs or generic OpenCLI), then probes them at runtime to find the first functional option. The base class in [`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py) provides the abstract `Channel` class and the `ordered_backends()` helper method, while the global registry in [`agent_reach/channels/__init__.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py) exposes all channels via the `ALL_CHANNELS` list.

Key architectural constraints to remember:

- **Tier classification**: Set `tier = 0` for zero-config channels, `tier = 1` for channels requiring a free API key, and `tier = 2` for channels needing complex setup.
- **Backend delegation**: Keep operational methods (`read()`, `search()`, `transcribe()`) thin—they should only route to the selected backend rather than implementing platform logic directly.
- **Health-check priority**: The `check()` method iterates through `self.ordered_backends()` and selects the first backend reporting `"ok"` or `"warn"` status.

## Step-by-Step Implementation Guide

### Step 1: Create a Channel Subclass

Create a new file in `agent_reach/channels/` (e.g., [`mysite.py`](https://github.com/Panniantong/Agent-Reach/blob/main/mysite.py)). Subclass `Channel` from [`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py) and declare the mandatory class attributes:

```python
from agent_reach.channels.base import Channel

class MySiteChannel(Channel):
    name = "mysite"               # Short identifier used in CLI and config files

    description = "MySite – read and search content"
    backends = ["mytool-cli", "OpenCLI"]   # Ordered candidate backends

    tier = 1                      # Requires free API key

```

### Step 2: Implement URL Detection with can_handle

Implement the `can_handle(self, url: str) -> bool` method to recognize URLs belonging to your platform. Use `urllib.parse.urlparse` to inspect the network location:

```python
from urllib.parse import urlparse

def can_handle(self, url: str) -> bool:
    """Return True for URLs that belong to MySite."""
    netloc = urlparse(url).netloc.lower()
    return "mysite.com" in netloc or "mys.site" in netloc

```

### Step 3: Configure Backend Health Checks

Override `check(self, config=None)` to probe each backend candidate. Follow the two-stage pattern used by existing multi-backend channels like **Twitter** or **Reddit** in [`agent_reach/channels/twitter.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/twitter.py):

1. Iterate over `self.ordered_backends(config)`.
2. Probe each candidate using `agent_reach.probe.probe_command` or `shutil.which` combined with `subprocess.run`.
3. Collect `(backend, status, message)` tuples.
4. Set `self.active_backend` to the first backend with `"ok"` or `"warn"` status.

```python
import shutil
from agent_reach.probe import probe_command

def check(self, config=None):
    """Probe backends and pick the first usable one."""
    self.active_backend = None
    findings = []

    for backend in self.ordered_backends(config):
        if backend == "mytool-cli":
            if not shutil.which("mytool-cli"):
                continue
            probe = probe_command("mytool-cli", ["--version"], package="mytool-cli")
            if probe.ok:
                findings.append((backend, "ok", "mytool-cli ready"))
            else:
                findings.append((backend, "warn", "installed but may need config"))
            continue

        if backend == "OpenCLI":
            from agent_reach.backends import opencli_status
            st = opencli_status()
            if not st.installed:
                continue
            status = "error" if st.broken else ("ok" if st.ready else "warn")
            findings.append((backend, status, st.hint))
            continue

    # Select best candidate

    for wanted in ("ok", "warn"):
        for backend, status, msg in findings:
            if status == wanted:
                self.active_backend = backend
                return status, msg

    return "off", "mytool-cli not installed. Install with `pip install mytool-cli`"

```

### Step 4: Register the Channel

Import the new class in [`agent_reach/channels/__init__.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py) and append an instance to the `ALL_CHANNELS` list. This makes the channel discoverable via `get_all_channels()` and `get_channel(name)`:

```python
from .mysite import MySiteChannel          # Add this import

ALL_CHANNELS: List[Channel] = [
    # ... existing channels ...

    MySiteChannel(),                       # Add the instance

]

```

### Step 5: Add Tests and Documentation

Write unit tests in [`tests/test_channels.py`](https://github.com/Panniantong/Agent-Reach/blob/main/tests/test_channels.py) verifying `can_handle` URL matching and `check` status reporting. Use the same monkey-patching style as the existing **TwitterChannel** test suite:

```python
def test_mysite_can_handle():
    ch = MySiteChannel()
    assert ch.can_handle("https://www.mysite.com/article/123")
    assert not ch.can_handle("https://github.com/user/repo")

def test_mysite_check_off_when_missing(monkeypatch):
    monkeypatch.setattr(shutil, "which", lambda _: None)
    ch = MySiteChannel()
    status, msg = ch.check()
    assert status == "off"
    assert "mytool-cli not installed" in msg

```

Update [`docs/install.md`](https://github.com/Panniantong/Agent-Reach/blob/main/docs/install.md) to include the new channel name in the *Supported channel names* table.

## Complete Working Example

Here is the full implementation for a fictional MySite channel following the Agent Reach patterns:

**[`agent_reach/channels/mysite.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/mysite.py)**

```python

# -*- coding: utf-8 -*-

"""MySite – example of adding a new platform channel."""

from urllib.parse import urlparse
import shutil
from agent_reach.probe import probe_command
from .base import Channel


class MySiteChannel(Channel):
    """Channel for the fictional MySite platform."""

    name = "mysite"
    description = "MySite – read and search content"
    backends = ["mytool-cli", "OpenCLI"]
    tier = 1

    def can_handle(self, url: str) -> bool:
        """Return True for URLs that belong to MySite."""
        netloc = urlparse(url).netloc.lower()
        return "mysite.com" in netloc or "mys.site" in netloc

    def check(self, config=None):
        """Probe backends and pick the first usable one."""
        self.active_backend = None
        findings = []

        for backend in self.ordered_backends(config):
            if backend == "mytool-cli":
                if not shutil.which("mytool-cli"):
                    continue
                probe = probe_command("mytool-cli", ["--version"], package="mytool-cli")
                if probe.status == "missing":
                    continue
                if probe.ok:
                    findings.append((backend, "ok", "mytool-cli ready"))
                else:
                    findings.append((backend, "warn", "mytool-cli installed but may need config"))
                continue

            if backend == "OpenCLI":
                from agent_reach.backends import opencli_status
                st = opencli_status()
                if not st.installed:
                    continue
                if st.broken:
                    findings.append((backend, "error", st.hint))
                elif st.ready:
                    findings.append((backend, "ok", st.hint))
                else:
                    findings.append((backend, "warn", st.hint))
                continue

        for wanted in ("ok", "warn"):
            for backend, status, msg in findings:
                if status == wanted:
                    self.active_backend = backend
                    return status, msg

        return "off", "mytool-cli not installed. Install with `pip install mytool-cli`"

```

## Key Files and Reference Architecture

When adding a new platform channel, reference these source files in the `Panniantong/Agent-Reach` repository:

- **[`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py)** – Provides the abstract `Channel` class and the `ordered_backends()` helper.
- **[`agent_reach/channels/__init__.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py)** – Contains the `ALL_CHANNELS` list used for CLI discovery and the `doctor` command.
- **[`agent_reach/channels/twitter.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/twitter.py)** – Demonstrates the canonical multi-backend probing pattern with fallback logic.
- **[`agent_reach/probe.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/probe.py)** – Implements `probe_command` for classifying executable states (missing, broken, ready).
- **[`tests/test_channels.py`](https://github.com/Panniantong/Agent-Reach/blob/main/tests/test_channels.py)** – Houses canonical tests for `can_handle` and `check` method validation.
- **[`docs/install.md`](https://github.com/Panniantong/Agent-Reach/blob/main/docs/install.md)** – Documentation file requiring updates when new channels are added.

## Summary

- **Create a subclass** of `Channel` in `agent_reach/channels/` with `name`, `description`, `backends`, and `tier` attributes.
- **Implement `can_handle()`** to filter URLs using `urllib.parse.urlparse` network location matching.
- **Implement `check()`** to iterate through `self.ordered_backends()`, probe each with `probe_command` or `shutil.which`, and set `self.active_backend` to the first healthy candidate.
- **Register the channel** by importing the class and adding an instance to `ALL_CHANNELS` in [`agent_reach/channels/__init__.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py).
- **Test thoroughly** using monkey-patching to simulate missing or broken backends, and update [`docs/install.md`](https://github.com/Panniantong/Agent-Reach/blob/main/docs/install.md) to reflect the new supported platform.

## Frequently Asked Questions

### What is the minimum code required to add a new platform channel?

You need a Python file in `agent_reach/channels/` containing a class that inherits from `Channel` (from `agent_reach.channels.base`), defines the four mandatory attributes (`name`, `description`, `backends`, `tier`), implements `can_handle()` for URL detection, implements `check()` for backend health validation, and is registered in [`agent_reach/channels/__init__.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py). Without registration in `ALL_CHANNELS`, the CLI will not discover your channel.

### How does Agent Reach choose which backend to use for a channel?

The `check()` method iterates over the `backends` list in order, probing each candidate using `agent_reach.probe.probe_command` or similar utilities. It selects the first backend reporting `"ok"` or `"warn"` status and assigns it to `self.active_backend`. If no backends pass the health check, the channel reports `"off"` and provides installation hints to the user.

### What do the tier values (0, 1, 2) represent in Agent Reach channels?

**Tier 0** indicates a zero-config channel that works immediately after installation. **Tier 1** requires a free API key or simple authentication. **Tier 2** requires complex setup, paid credentials, or additional infrastructure. These values help the `doctor` command prioritize troubleshooting messages and guide users through configuration requirements.

### Can I add operational methods like `read()` or `search()` to my custom channel?

Yes, you can implement optional methods such as `read(self, url)`, `search(self, query)`, or `transcribe(self, ...)` depending on the platform's capabilities. Keep these methods thin—they should delegate to the active backend via `subprocess.run` or library calls rather than implementing platform-specific parsing logic directly, maintaining the channel's role as a routing layer.