How to Add New Messaging Platform Adapters to the Hermes Agent Gateway

To add a new messaging platform adapter to Hermes Agent, create a Python module in gateway/platforms/ that subclasses BasePlatform, implement the required async methods for message handling and sending, register the class in the PLATFORMS dictionary in gateway/platforms/__init__.py, and add any required environment variables to the configuration schema.

The Hermes Agent gateway serves as the bridge between the LLM-driven assistant core and external messaging services like Telegram, Discord, and WhatsApp. For the NousResearch/hermes-agent repository, extending support to additional platforms follows a strict modular architecture that keeps platform-specific logic isolated from the core agent. This guide explains the exact process for adding new messaging platform adapters to the Hermes Agent gateway while preserving the existing abstraction patterns.

Understanding the BasePlatform Interface

All messaging adapters in Hermes Agent must inherit from the abstract base class defined in gateway/platforms/base.py. This interface enforces a consistent contract for platform initialization, inbound message processing, and outbound message delivery. Your implementation must provide concrete versions of three critical async methods: handle_update() for processing incoming webhooks, send_message() for delivering replies to users, and __init__() for initializing platform-specific credentials. The base class also provides helper utilities like _post() for HTTP requests, which your adapter can leverage for API calls to the external messaging service.

Step-by-Step Implementation Process

1. Create the Platform Module

Create a new file under gateway/platforms/ using a descriptive name for your service (e.g., whatsapp.py or signal.py). This module will house your adapter class and any platform-specific utility functions. Keep the file focused solely on the messaging interface—authentication logic, webhook parsing, and message formatting should all live here to maintain the single-responsibility principle established by existing adapters like gateway/platforms/telegram.py.

2. Implement the Adapter Class

Subclass BasePlatform and define the required attributes and methods. You must set a name class attribute (e.g., name = "whatsapp") that serves as the platform identifier throughout the gateway. Implement __init__() to accept and store configuration tokens, which the gateway injects from environment variables automatically. The handle_update() method receives raw webhook payloads and must return a canonical dictionary with user_id, content, and platform keys. Finally, implement send_message() to transmit text responses back to the user through the platform's API.

3. Register in the Platform Registry

Update gateway/platforms/__init__.py to import your new class and add it to the PLATFORMS dictionary. This registry acts as the discovery mechanism for gateway/run.py. The dictionary maps platform name strings to class references, enabling the gateway to instantiate the correct adapter dynamically at runtime based on configuration.

4. Configure Environment Variables

Add any required authentication tokens or API secrets to the .env.example file for documentation purposes. Then update hermes_cli/config.py to include these variables in the configuration schema, allowing the CLI setup wizard to prompt users for credentials during installation. The gateway automatically loads these environment variables when instantiating your platform class through the PLATFORMS registry.

5. Implement Custom Commands (Optional)

If your platform requires specific slash commands (e.g., /whatsapp status), add handlers in gateway/commands.py using the @register_command decorator. These commands allow users to interact with platform-specific features directly from the chat interface. Alternatively, you can map commands within the platform module itself if the logic is tightly coupled to the adapter's functionality.

Integration Flow and Architecture

When a message arrives from your new platform, the flow follows this path: the external service sends a webhook to the gateway, gateway/run.py loads the appropriate class from the PLATFORMS dictionary, instantiates it using stored credentials, and calls handle_update() to normalize the payload. The normalized message passes to the core AIAgent for processing. Once the agent generates a response, the gateway calls your adapter's send_message() method to deliver the reply back to the user.

Code Implementation Examples

Here is a minimal skeleton for a new platform adapter:


# gateway/platforms/example.py

from .base import BasePlatform

class ExamplePlatform(BasePlatform):
    """Adapter for the Example messaging service."""

    name = "example"

    def __init__(self, token: str):
        super().__init__()
        self.token = token

    async def handle_update(self, raw_update: dict) -> dict:
        """Convert a raw webhook payload into a canonical message dict."""
        user_id = raw_update["user"]["id"]
        text = raw_update["message"]["text"]
        return {"user_id": user_id, "content": text, "platform": self.name}

    async def send_message(self, user_id: str, content: str) -> None:
        """Send a textual reply back to the user."""
        payload = {"to": user_id, "text": content}
        await self._post("/send", json=payload)

Register the adapter in the platform registry:


# gateway/platforms/__init__.py

from .telegram import TelegramPlatform
from .discord import DiscordPlatform
from .example import ExamplePlatform

PLATFORMS = {
    "telegram": TelegramPlatform,
    "discord": DiscordPlatform,
    "example": ExamplePlatform,
}

The gateway runner dynamically loads your adapter:


# gateway/run.py excerpt

from .platforms import PLATFORMS

def _create_platform(name: str):
    platform_cls = PLATFORMS[name]
    return platform_cls()

Add optional slash commands:


# gateway/commands.py

@register_command("/example")
async def cmd_example(agent, args):
    """Echo the arguments back to the Example platform."""
    await agent.send_to_platform("example", args.text)
    return "Message sent to Example platform."

Testing Your Adapter

Place unit tests under tests/gateway/test_<platform_name>.py following the pattern established in existing test files. Mock the external messaging API using unittest.mock or pytest-asyncio fixtures to verify that handle_update() correctly parses webhooks and that send_message() formats API requests properly. Test edge cases like malformed payloads, authentication failures, and rate limiting to ensure production reliability.

Summary

  • Inherit from BasePlatform: All adapters must subclass the abstract base class in gateway/platforms/base.py and implement handle_update(), send_message(), and __init__().
  • Register in PLATFORMS: Add your class to the dictionary in gateway/platforms/__init__.py to enable dynamic discovery by gateway/run.py.
  • Isolate platform logic: Keep authentication, webhook parsing, and API calls contained within your gateway/platforms/<name>.py module.
  • Configure credentials: Add environment variables to .env examples and hermes_cli/config.py for CLI integration.
  • Test thoroughly: Create comprehensive mocks in tests/gateway/ to verify inbound and outbound message handling without hitting live APIs.

Frequently Asked Questions

What methods are absolutely required when subclassing BasePlatform?

You must implement three async methods: __init__() to accept configuration tokens, handle_update() to parse incoming webhooks into canonical dictionaries, and send_message() to transmit replies back to users. Missing any of these will raise NotImplementedError or cause runtime failures in gateway/run.py.

Where does the gateway load platform credentials from?

The gateway loads credentials from environment variables automatically. Define your required variables (e.g., EXAMPLE_TOKEN) in hermes_cli/config.py so the setup CLI can prompt for them, then access them via os.environ or the config object within your adapter's __init__() method.

Can I add custom slash commands specific to my messaging platform?

Yes. Register platform-specific commands in gateway/commands.py using the @register_command decorator with a unique prefix (e.g., /whatsapp status). These handlers receive the agent instance and command arguments, allowing you to trigger platform-specific actions before returning control to the core agent loop.

How do I test my adapter without connecting to the live messaging service?

Create unit tests in tests/gateway/ that mock your platform's API responses using unittest.mock.patch or aioresponses for async HTTP calls. Verify that handle_update() correctly transforms sample webhook JSON into the expected canonical format, and assert that send_message() calls the expected API endpoints with properly formatted payloads.

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 →