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_idto 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
BaseGatewayComponentsubclass. Solace Agent Mesh also offers a higher-levelGatewayAdapterabstraction (used withGenericGatewayApp) 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_idmust be unique across all Entrypoint instances connected to the same broker. If two Entrypoints share agateway_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}__defaultfor 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:
-
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. -
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.
-
Sessions are just strings – session switching sounds complex, but in practice it’s changing a string. Pass a different
session_idand the agent picks up a completely different conversation. -
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.
-
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. -
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(). -
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.