# How to Add a New Platform Channel to Agent Reach: A Complete Implementation Guide

> Learn to add a new platform channel to Agent Reach with this step-by-step guide. Implement required methods and register your channel for seamless integration.

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

---

**To add a new platform channel to Agent Reach, create a Python module in `agent_reach/channels/` that inherits from the abstract `Channel` base class, implement the `can_handle()` and `check()` methods, and register the class in [`agent_reach/channels/__init__.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py) to enable automatic discovery.**

Agent Reach treats every supported internet platform as a **channel** that lives in the `agent_reach/channels/` package. Each channel inherits from the abstract base class defined in [[`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py)](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py), which standardizes how the framework detects, probes, and routes platform-specific operations. This guide explains the architectural requirements and provides a production-ready template to add a new platform channel to Agent Reach.

## Understand the Channel Architecture

The [`Channel`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py) base class defines the lifecycle contract that every platform must implement. Subclasses must declare four metadata properties and override two critical methods.

**Metadata Properties:**
- **`name`**: A short identifier used in CLI arguments and configuration keys (e.g., `"twitter"`).
- **`description`**: Human-readable diagnostic text describing the platform.
- **`backends`**: An ordered list of command-line tools that can fulfill requests for this channel (e.g., `["twitter-cli", "OpenCLI"]`). The first healthy backend becomes the active instance.
- **`tier`**: An integer indicating setup complexity: `0` for zero-config, `1` for requiring a free API key, or `2` for full user setup.

**Core Methods:**
- **`can_handle(self, url: str) -> bool`**: Determines if a given URL belongs to this platform by parsing the domain or path.
- **`check(self, config)`**: Probes each candidate backend in `ordered_backends` and sets `active_backend` to the first usable tool. Returns a tuple of `(status, message)` where status is `"ok"`, `"warn"`, or `"error"`.

The base class also provides **`ordered_backends(self, config)`**, which returns the backend list respecting any user override via configuration variables or environment settings.

## Step-by-Step Implementation

### Step 1: Create the Channel Module

Create a new file at `agent_reach/channels/<platform>.py`. This module will contain your channel class and any helper functions for probing backends.

```python

# agent_reach/channels/myplatform.py

from .base import Channel
from agent_reach.probe import probe_command

```

### Step 2: Define Metadata and Class Properties

Declare the metadata fields as class attributes. The `backends` list should prioritize native CLIs over generic fallbacks.

```python
class MyPlatformChannel(Channel):
    name = "myplatform"
    description = "MyPlatform – short description"
    backends = ["myplatform-cli", "OpenCLI", "legacy-cli"]
    tier = 1  # Requires API key but minimal setup

    active_backend = None

```

### Step 3: Implement URL Detection with can_handle

The `can_handle` method must return `True` if the supplied URL belongs to your platform. Parse the hostname using `urllib.parse` to ensure robust matching.

```python
    def can_handle(self, url: str) -> bool:
        from urllib.parse import urlparse
        domain = urlparse(url).netloc.lower()
        return "myplatform.com" in domain or "mp.com" in domain

```

### Step 4: Implement Backend Probing with check

The `check` method iterates through `ordered_backends`, probes each candidate using `probe_command`, and selects the first healthy backend. Follow the pattern used in [[`agent_reach/channels/twitter.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/twitter.py)](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/twitter.py) for multi-backend support.

```python
    def check(self, config=None):
        """Probe each backend in order; the first usable backend becomes active."""
        self.active_backend = None
        findings = []

        for backend in self.ordered_backends(config):
            if backend == "myplatform-cli":
                result = self._check_myplatform_cli()
            elif backend == "OpenCLI":
                result = self._check_opencli()
            else:
                continue

            if result is None:
                continue
            findings.append((backend, *result))

        # Prefer "ok", then "warn"

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

        if findings:
            return "error", "\n".join(msg for _, _, msg in findings)

        return "warn", (
            "MyPlatform CLI not installed. Install it with:\n"
            "  pipx install myplatform-cli\n"
            "or use OpenCLI if you have a browser session."
        )

    def _check_myplatform_cli(self):
        """Return None if missing, or (status, message) tuple."""
        probe = probe_command(
            "myplatform",
            ["status"],
            timeout=15,
            retries=1,
            package="myplatform-cli"
        )
        if probe.status == "missing":
            return None
        if probe.status == "broken":
            return "error", f"myplatform-cli exists but failed to run.\n{probe.hint}"
        if probe.ok and "ready" in probe.output.lower():
            return "ok", "myplatform-cli ready (search, read, …)"
        return "warn", "myplatform-cli installed but not authenticated"

    def _check_opencli(self):
        from agent_reach.backends import opencli_status
        st = opencli_status()
        if not st.installed:
            return None
        if st.broken:
            return "error", st.hint
        if st.ready:
            return "ok", "OpenCLI usable (browser-based login)"
        return "warn", st.hint

```

### Step 5: Register in Package Index

Edit [[`agent_reach/channels/__init__.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py)](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py) to import your new class so the auto-discovery machinery can locate it.

```python
from .myplatform import MyPlatformChannel  # Add this line

```

### Step 6: Verify with Diagnostics

Run the built-in diagnostics to confirm your channel is discovered and its `check()` method passes.

```bash
python -m agent_reach.cli doctor

```

You should see output similar to:

```

✔ MyPlatform (myplatform) – ok – myplatform-cli

```

## Production-Ready Code Template

Copy the complete skeleton below into [`agent_reach/channels/myplatform.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/myplatform.py) to accelerate your implementation.

```python

# agent_reach/channels/myplatform.py

"""MyPlatform channel implementation."""

from .base import Channel
from agent_reach.probe import probe_command


class MyPlatformChannel(Channel):
    name = "myplatform"
    description = "MyPlatform – short description"
    backends = ["myplatform-cli", "OpenCLI"]
    tier = 1
    active_backend = None

    def can_handle(self, url: str) -> bool:
        from urllib.parse import urlparse
        domain = urlparse(url).netloc.lower()
        return "myplatform.com" in domain

    def check(self, config=None):
        self.active_backend = None
        findings = []

        for backend in self.ordered_backends(config):
            if backend == "myplatform-cli":
                result = self._check_native()
            elif backend == "OpenCLI":
                result = self._check_opencli()
            else:
                continue

            if result is None:
                continue
            findings.append((backend, *result))

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

        if findings:
            return "error", "\n".join(m for _, _, m in findings)

        return "warn", (
            "MyPlatform CLI not installed.\n"
            "  pipx install myplatform-cli\n"
            "or use OpenCLI if you have a logged-in browser."
        )

    def _check_native(self):
        probe = probe_command(
            "myplatform", ["status"], timeout=15, retries=1, package="myplatform-cli"
        )
        if probe.status == "missing":
            return None
        if probe.status == "broken":
            return "error", f"myplatform-cli broken.\n{probe.hint}"
        if probe.ok and "ready" in probe.output.lower():
            return "ok", "myplatform-cli ready"
        return "warn", "myplatform-cli installed but not authenticated"

    def _check_opencli(self):
        from agent_reach.backends import opencli_status
        st = opencli_status()
        if not st.installed:
            return None
        if st.broken:
            return "error", st.hint
        if st.ready:
            return "ok", "OpenCLI ready"
        return "warn", st.hint

    def read(self, url: str):
        """Delegate reading to the selected backend."""
        if self.active_backend == "myplatform-cli":
            return probe_command("myplatform", ["read", url]).output
        if self.active_backend == "OpenCLI":
            return probe_command("opencli", ["myplatform", "get", url]).output
        raise RuntimeError("No active backend for read")

```

## Key Source Files

- **[[`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py)](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py)**: Abstract `Channel` definition, helper methods, and default `check()` implementation.
- **[[`agent_reach/channels/twitter.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/twitter.py)](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/twitter.py)**: Reference implementation showing multi-backend probing and health check patterns.
- **[[`agent_reach/channels/__init__.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py)](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py)**: Package index where new channels must be imported for auto-discovery.
- **[[`agent_reach/probe.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/probe.py)](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/probe.py)**: Utility that executes command probes, capturing status (`missing`, `broken`, `ok`, `timeout`) and generating installation hints.
- **[[`agent_reach/core.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/core.py)](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/core.py)**: Central routing logic that selects a channel based on `can_handle` responses.

## Summary

- **Inherit from `Channel`**: All platform support modules must subclass the abstract base class in [`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py).
- **Implement `can_handle`**: This method routes URLs to the correct channel by inspecting domains or path patterns.
- **Implement `check`**: Probe candidate backends using `probe_command` and set `active_backend` to the first healthy tool.
- **Register the class**: Import the new channel in [`agent_reach/channels/__init__.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py) to enable discovery.
- **Verify diagnostics**: Run `python -m agent_reach.cli doctor` to validate the channel health and backend connectivity.

## Frequently Asked Questions

### What is the minimum required to implement a functional channel?

You must implement `can_handle(self, url)` to return `True` for your platform's URLs and `check(self, config)` to return a status tuple. While you can return hardcoded values from `check` for development, production channels should probe actual backend binaries using `probe_command` to ensure the tool is installed and authenticated.

### How does Agent Reach decide which channel handles a specific URL?

The [`AgentReach`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/core.py) router iterates through all registered channels and calls `can_handle(url)` on each instance. The first channel returning `True` receives the operation. Order matters, so ensure your URL patterns are specific enough to avoid collisions with generic channels.

### Can a single channel support multiple backend tools?

Yes. Define multiple entries in the `backends` class property. The `ordered_backends` method respects user overrides, and your `check` implementation should probe each candidate in order. The first backend reporting `"ok"` or `"warn"` status becomes `active_backend`, allowing graceful fallbacks from native CLIs to generic tools like OpenCLI.

### Why doesn't my new channel appear in the `doctor` diagnostics?

If `python -m agent_reach.cli doctor` skips your channel, verify that you imported the class in [[`agent_reach/channels/__init__.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py)](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py) and that the file exists at `agent_reach/channels/<name>.py`. Also ensure the class name follows the `*Channel` naming convention and that Python can parse the module without syntax errors.