Tip: How Do Entrypoints (also known as Gateways) Work in Solace Agent Mesh?

Tip: How do Entrypoints (also known as Gateways) work in Solace Agent Mesh?

Solace Agent Mesh is an event-driven framework for building distributed ecosystems of collaborative AI agents, powered by the Solace event broker for enterprise-grade agent orchestration. Agents do the thinking – they have skills, tools, and LLM access. But agents don’t talk to users directly. That’s where Entrypoints come in.

An Entrypoint is a point of entry and exit between an external stimulus – whether it’s a prompt, an event, or a webhook – and the Solace Agent Mesh framework. It translates platform-specific input into the format agents understand, and translates agent responses back to the platform. Entrypoints make zero LLM calls. All intelligence lives on the agent side.

In this post, we’ll explore what Entrypoints are, how to create one using the CLI, the configuration options available, and the key concepts you need to know when building a custom Entrypoint.


What Does an Entrypoint Do?

An Entrypoint acts as a translation layer between an external platform and the agent mesh. Think of it as an adapter: it receives input from the outside world (a chat message, an API call, a Slack message, an event), translates it into a task that agents can process, and then translates the agent’s response back to the platform.

The Entrypoint itself is not intelligent. It doesn’t reason, summarize, or make decisions. It handles:

  • Inbound: identifying the user and converting platform input into an agent task
  • Outbound: converting agent responses back to the platform’s format
  • Session management: deciding which session_id to assign to each conversation

Everything else – broker connections, the A2A protocol, topic routing, agent discovery, conversation history, artifact storage, and embed resolution – is handled by the framework.


Creating an Entrypoint

There are two paths depending on whether you’re adding a gateway to an existing Solace Agent Mesh project or building a reusable plugin.

Option 1: Add a Gateway to Your Project

The fastest way to create an Entrypoint is with the sam add gateway command:

sam add gateway my-gateway

This walks you through an interactive setup and generates the following files:

configs/gateways/my_gateway_config.yaml   # Gateway configuration
src/my_gateway/app.py                     # App class (extends BaseGatewayApp)
src/my_gateway/component.py              # Component class — your logic goes here
src/my_gateway/__init__.py

The command supports several options for non-interactive use:

sam add gateway my-gateway \
  --namespace "myorg/dev" \
  --gateway-id "my-gw-01" \
  --artifact-service-type filesystem \
  --artifact-service-scope namespace \
  --skip

You can also use --gui to configure via the web UI instead of the terminal prompts.

Option 2: Package as a Reusable Plugin

If you want to distribute your Entrypoint as a reusable package:

sam plugin create my-gateway-plugin --type gateway

If you omit --type, the CLI will prompt you to select from: agent, gateway, tool, or custom. This scaffolds a full plugin package with the same app/component structure, plus pyproject.toml, README.md, and build tooling.

When you’re ready to distribute:

sam plugin build

Tip: Both paths generate a BaseGatewayComponent subclass. Solace Agent Mesh also offers a higher-level GatewayAdapter abstraction (used with GenericGatewayApp) where you implement fewer methods. See the generated code comments or the source code for details on both approaches.


Understanding the Generated Code

The generated component.py is where your platform-specific logic lives. It extends BaseGatewayComponent and contains a handful of methods you fill in. The scaffolded code includes detailed comments and implementation examples for each method.

Here’s what each method does:

Starting and Stopping Your Listener

def _start_listener(self) -> None:
    """Start listening for external input.
    This is where you set up your polling loop, webhook server,
    SDK callback, or any other mechanism to receive input."""
    pass

def _stop_listener(self) -> None:
    """Clean up on shutdown. Close connections, stop servers."""
    pass

Use case: If you’re building a Slack Entrypoint, _start_listener() would initialize the Slack Socket Mode client. For a webhook, it would start an HTTP server.

Identifying the User

async def _extract_initial_claims(self, external_event_data) -> Optional[Dict[str, Any]]:
    """Extract user identity from the incoming event.
    Return a dict with at least an 'id' field."""
    return {"id": "user@example.com", "name": "User", "source": "my_platform"}

Use case: For Slack, this would extract the user ID from the Slack event payload. For a REST API, it might validate a bearer token.

Translating Input

async def _translate_external_input(self, external_event) -> Tuple[str, List, Dict]:
    """Convert platform input into agent task parameters.
    Returns: (target_agent_name, a2a_parts_list, external_request_context)"""
    pass

Use case: This is where you parse your platform’s message format and convert it into the parts list (text, files, data) that agents understand.

Sending Responses Back

async def _send_final_response_to_external(self, external_request_context, task_data) -> None:
    """Send the agent's final response back to the platform."""
    pass

async def _send_error_to_external(self, external_request_context, error_data) -> None:
    """Handle and display errors."""
    pass

async def _send_update_to_external(self, external_request_context, event_data, is_final) -> None:
    """Handle streaming updates (optional)."""
    pass

Use case: For a terminal Entrypoint, _send_final_response_to_external() would print the response. For Slack, it would post a message to the channel.

Tip: You don’t need to implement every method to get started. Begin with _translate_external_input() and _send_final_response_to_external() to get a message flowing end-to-end, then add streaming, file handling, and error handling incrementally.


Configuration

The generated config.yaml is where you wire your Entrypoint to Solace Agent Mesh. Here’s the structure with the key options explained:

Basic Configuration

apps:
  - name: my_gateway_app
    app_module: my_gateway.app
    broker:
      broker_url: ${SOLACE_BROKER_URL, ws://localhost:8008}
      broker_username: ${SOLACE_BROKER_USERNAME, default}
      broker_password: ${SOLACE_BROKER_PASSWORD, default}
      broker_vpn: ${SOLACE_BROKER_VPN, default}

    app_config:
      namespace: "${NAMESPACE}"
      gateway_id: my-gateway-01
      default_agent_name: "OrchestratorAgent"
      artifact_service:
        type: "filesystem"
        base_path: "/tmp/samv2"
        artifact_scope: namespace
      enable_embed_resolution: true
      system_purpose: >
        Your system prompt here...

Configuration Options Explained

Option Purpose Example
app_module Python import path to your App class my_gateway.app
namespace Solace Agent Mesh namespace "myorg/dev"
gateway_id Unique identifier for this Entrypoint instance my-gateway-01
default_agent_name Agent to route messages to by default "OrchestratorAgent"
artifact_service.type Artifact storage backend "filesystem", "memory", "gcs"
artifact_service.artifact_scope How artifacts are scoped "namespace", "app", "custom"
enable_embed_resolution Resolve artifact embeds in the Entrypoint true
system_purpose System prompt sent to agents Multi-line string
response_format Response formatting instructions Multi-line string

Example: Multiple Entrypoints Sharing a Broker

A common pattern is running multiple Entrypoints against the same Solace Agent Mesh deployment. Each Entrypoint needs a unique gateway_id:

shared_config:
  - broker_connection: &broker_connection
      broker_url: ${SOLACE_BROKER_URL, ws://localhost:8008}
      broker_username: ${SOLACE_BROKER_USERNAME, default}
      broker_password: ${SOLACE_BROKER_PASSWORD, default}
      broker_vpn: ${SOLACE_BROKER_VPN, default}

  - services:
      artifact_service: &default_artifact_service
        type: "filesystem"
        base_path: "/tmp/samv2"
        artifact_scope: namespace

apps:
  - name: cli_gateway
    app_module: cli_gateway.app
    broker: *broker_connection
    app_config:
      namespace: "${NAMESPACE}"
      gateway_id: ${CLI_GATEWAY_ID, cli-gw-01}
      artifact_service: *default_artifact_service
      default_agent_name: "OrchestratorAgent"

Important: The gateway_id must be unique across all Entrypoint instances connected to the same broker. If two Entrypoints share a gateway_id, responses will be routed unpredictably.

Example: Gateway with Custom Adapter Config

If you’re using the GatewayAdapter abstraction (via GenericGatewayApp), you can pass platform-specific settings through adapter_config:

apps:
  - name: my_gateway_app
    app_module: solace_agent_mesh.gateway.generic.app
    broker: *broker_connection
    app_config:
      namespace: "${NAMESPACE}"
      gateway_adapter: my_package.adapter.MyAdapter
      adapter_config:
        host: "0.0.0.0"
        port: 8080
        api_key: ${MY_API_KEY}
      gateway_id: ${MY_GATEWAY_ID, my-gw-01}
      artifact_service: *default_artifact_service
      default_agent_name: "OrchestratorAgent"

The adapter_config section is passed directly to your adapter. If your adapter defines a ConfigModel (a Pydantic BaseModel subclass), the framework validates the config automatically.


Key Concepts

Sessions

Every task includes a session_id – a string that scopes conversation history and artifacts. The framework manages conversation history keyed by this string. Pass a different session_id, and the agent sees an entirely different conversation. No reconnection, no state transfer, no replay logic needed in your Entrypoint.

Different Entrypoints use different session strategies:

Entrypoint Session Strategy
WebUI Cookie-based sessions via SessionMiddleware
CLI Deterministic default ({gateway_id}__default) plus user-created named sessions
Slack Thread ID mapped to session ID – each thread is a natural conversation boundary
REST Client provides session ID in the request, or one is generated per request

Tip: Use a deterministic session ID format like {gateway_id}__default for default sessions. This way, the same user reconnecting to the same Entrypoint automatically resumes their conversation.

Artifacts

Files flow through Entrypoints in both directions:

  • Inbound: files from the user are attached to the task as file parts
  • Outbound: files created by agents arrive in your response handler with either inline bytes or an artifact URI (never both)

Artifacts are scoped by session – switch sessions and you see a different set. The framework handles storage and retrieval; your Entrypoint just needs to render or forward the files.

Streaming

Agent responses can arrive as a stream of updates. Your _send_update_to_external() method receives each chunk as it arrives. You have two choices:

  • Stream in real time: forward each chunk to the user immediately (good for terminal or SSE Entrypoints)
  • Accumulate and render: collect all chunks and send a complete response in _send_final_response_to_external() (good for platforms like Slack that need a complete message)

Tip: For streaming tasks, text and file parts are delivered incrementally during _send_update_to_external() calls and are filtered from the final response to avoid duplication. Only data parts (like tool results) remain in the final response.


Entrypoints That Ship with Solace Agent Mesh

The WebUI gateway (HTTP/SSE) ships in the core package. It runs an embedded FastAPI server with Server-Sent Events for real-time streaming, cookie-based sessions, file upload/download, agent discovery, artifact management, and feedback collection.

Additional Entrypoints are available as plugins:

Entrypoint Description
Slack Socket Mode for real-time messaging, streaming updates, user identity extraction, thumbs-up/down feedback
MCP Exposes agents as MCP tools for Claude Desktop, IDEs, and other MCP-compatible clients. Dynamically registers tools as agents join and leave the mesh
REST and Webhook HTTP endpoints for programmatic access
Event Mesh Connects to Solace Event Mesh brokers for event-driven processing

You can find more information here:


Last Words of Wisdom

Here are the key principles to keep in mind when building Entrypoints:

  1. Start with two methods – implement _translate_external_input() and _send_final_response_to_external() first. Get a message flowing end-to-end before adding streaming, files, or error handling.

  2. The Entrypoint is a transport layer – it doesn’t reason, summarize, or make decisions. It translates input and output. All intelligence lives on the agent side.

  3. Sessions are just strings – session switching sounds complex, but in practice it’s changing a string. Pass a different session_id and the agent picks up a completely different conversation.

  4. Check before you build – the framework manages conversation history, artifact storage, agent discovery, and embed resolution. Before building a feature in your Entrypoint, check if the framework already handles it.

  5. Scope your gateway_id – every Entrypoint instance needs a unique ID. Use environment variables (gateway_id: ${MY_GATEWAY_ID, my-gw-01}) so you can run multiple instances safely.

  6. Choose your streaming model intentionally – decide early whether your platform benefits from real-time streaming or complete-message delivery. This choice shapes how you implement _send_update_to_external() and _send_final_response_to_external().

  7. Leverage the Solace Agent Mesh WebUI – use the WebUI’s agent visualizer and message inspector to debug your Entrypoint’s interactions with the mesh.


For more information about Solace Agent Mesh, visit the repository:

An event-driven framework designed to build and orchestrate multi-agent AI systems. It enables seamless integration of AI agents with real-world data sources and systems, facilitating complex, multi-step workflows.


2 Likes