Agent Reach Channel Implementation Contract: Required Methods for Custom Channels

To implement a channel in Agent Reach, inherit from the Channel abstract base class in agent_reach/channels/base.py, define the class attributes name, description, backends, and tier, implement the abstract can_handle() method, and override check() to probe backends and set self.active_backend while returning a status tuple.

Agent Reach treats every supported Internet platform as a channel that discovers and interacts with URLs. By adhering to the Agent Reach channel implementation contract, developers ensure their custom channels integrate seamlessly with the core routing logic and pass the automated validation suite.

The Channel Base Class and Required Attributes

All channel classes must inherit from the abstract base class Channel located at agent_reach/channels/base.py. The contract enforces specific class-level and instance-level attributes that describe the channel and its operational state.

Required Class Attributes

Every concrete channel must define four class attributes (lines 32–36 of base.py):

  • name (str): A unique identifier for the channel.
  • description (str): A human-readable explanation of the platform.
  • backends (List[str]): A list of supported backend names (e.g., ["mycli", "OpenCLI"]).
  • tier (int): An integer in {0, 1, 2} indicating setup complexity—0 for zero-config, 1 for free key required, and 2 for full setup needed.

The active_backend Instance Attribute

Channels must maintain an instance attribute named active_backend, initialized to None (lines 37–39 of base.py). The check() method must set this attribute to the string name of the first usable backend, or leave it as None if no backends are available. This attribute is consumed by the routing logic in agent_reach/core.py to determine which tool handles a given URL.

Required Methods for Channel Implementation

The contract requires implementations for can_handle() and check(), while ordered_backends() is typically inherited unchanged.

can_handle(url: str) -> bool

This abstract method (lines 40–44 of base.py) determines whether a given URL belongs to the channel’s platform. It must return True if the domain or path matches the platform (e.g., checking if x.com is in the URL for Twitter).

Example implementation pattern from agent_reach/channels/twitter.py:

from urllib.parse import urlparse

def can_handle(self, url: str) -> bool:
    domain = urlparse(url).netloc.lower()
    return "x.com" in domain or "twitter.com" in domain

check(config=None) -> Tuple[str, str]

While the base class provides a concrete implementation (lines 61–70 of base.py), channels must override this method to probe their specific backends. The method must:

  1. Accept an optional config dictionary.
  2. Return a tuple of (status, message) where status is one of "ok", "warn", "off", or "error".
  3. Set self.active_backend to the first working backend name (or None).

The method should iterate over self.ordered_backends(config) to respect user overrides.

ordered_backends(config=None) -> List[str]

This helper method (lines 45–60 of base.py) returns the backends list reordered according to a user-configured preference (e.g., <channel>_backend). Channels typically inherit this implementation without modification. It ensures that if a user specifies a preferred backend, it moves to the front of the list.

Test Suite Validation

The contract is enforced by tests/test_channel_contracts.py, which contains specific test cases that every channel must pass:

  • test_channel_registry_contract: Verifies that name, description, backends are non-empty and tier is valid (lines 15–25).
  • test_channel_check_contract_with_minimal_runtime: Ensures check() returns a valid status string and non-empty message even when external tools are missing (lines 28–36).
  • test_channel_active_backend_attribute_contract and test_channel_active_backend_set_by_check: Confirm that active_backend exists, defaults to None, and is set to a string or None after check() runs (lines 39–70).
  • test_ordered_backends_contract and test_ordered_backends_override_moves_backend_to_front: Validate that ordered_backends returns a permutation of backends and respects configuration overrides (lines 72–99).

Practical Implementation Example

Below is a minimal skeleton demonstrating the contract requirements. This pattern mirrors the production implementation in agent_reach/channels/twitter.py:

from .base import Channel
from agent_reach.probe import probe_command

class MyPlatformChannel(Channel):
    name = "myplatform"
    description = "MyPlatform – posts & comments"
    backends = ["mycli", "OpenCLI"]
    tier = 1  # 1 = needs free key

    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 == "mycli":
                result = self._check_mycli()
            else:
                result = self._check_opencli()
            
            if result:
                findings.append((backend, *result))
        
        # Return first ok/warn, otherwise error/off

        for wanted in ("ok", "warn"):
            for backend, status, msg in findings:
                if status == wanted:
                    self.active_backend = backend
                    return status, msg
        return "off", "No MyPlatform back‑ends installed"
    
    def _check_mycli(self):
        probe = probe_command("mycli", ["status"], timeout=10, retries=1, package="mycli")
        if probe.status == "missing":
            return None
        if probe.ok:
            return "ok", "mycli is ready"
        return "warn", "mycli installed but not authenticated"

For complete reference implementations, examine the source files:

Summary

  • Inherit from the Channel ABC defined in agent_reach/channels/base.py.
  • Define name, description, backends, and tier as class attributes.
  • Initialize and set self.active_backend within the check() method.
  • Implement can_handle(url: str) -> bool to filter URLs for your platform.
  • Override check(config=None) to return a tuple of ("ok"|"warn"|"off"|"error", message) and set the active backend.
  • Use ordered_backends(config) to iterate backends in priority order.
  • Ensure compliance by running tests/test_channel_contracts.py.

Frequently Asked Questions

What happens if I don't set active_backend in the check() method?

The test suite will fail test_channel_active_backend_set_by_check, and the core routing logic in agent_reach/core.py will not be able to select your channel for processing URLs, effectively disabling the channel at runtime.

Can I add extra methods to my channel class beyond the required contract?

Yes. The contract specifies only the required interface. You may add private helper methods (like _check_mycli() in the example) or public utilities, provided you do not override required methods with incompatible signatures.

How does the tier attribute affect channel behavior?

The tier attribute is informational and used by diagnostic tools to indicate setup complexity. It does not change runtime logic, but users and the test suite expect valid values of 0, 1, or 2, where 0 requires zero configuration, 1 requires a free API key, and 2 requires full manual setup.

Where is the channel selection logic that uses can_handle()?

The core routing logic resides in agent_reach/core.py. This module iterates over all registered channels (retrieved via get_all_channels()), calls can_handle() on each to find a matching platform, and then checks active_backend to determine if the channel can process the request.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →