A gateway adapter to make SAM accessible as a MCP server
Project description
MCP Gateway Adapter
The MCP Gateway Adapter exposes Solace Agent Mesh (SAM) agents as a Model Context Protocol (MCP) server using FastMCP. This allows any MCP-compatible client to interact with SAM agents through a standardized interface.
About Solace Agent Mesh
Solace Agent Mesh (SAM) is an open-source framework for building event-driven, multi-agent AI systems where specialized agents collaborate on complex tasks. It provides a standardized way for agents to communicate, share data, and integrate with external systems while keeping components loosely coupled and production-ready.
SAM helps you:
- Build event-driven multi-agent systems on Solace Event Mesh
- Connect agents, tools, gateways, and services through a common runtime
- Extend projects with installable plugins such as
sam-mcp-server-gateway-adapter
Learn more in the Solace Agent Mesh documentation and the main project repository.
Overview
The MCP adapter:
- Dynamically discovers agents from the SAM agent registry
- Creates MCP tools automatically based on agent skills
- Streams responses in real-time back to MCP clients
- Supports HTTP and stdio transports for different deployment scenarios
Installation
Install the adapter plugin via SAM CLI:
sam plugin add <your-new-component-name> --plugin sam-mcp-server-gateway-adapter
Architecture
MCP Client → FastMCP Server → McpAdapter → SAM Agent Mesh
↓
[Dynamic Tool Registration]
- agent1_skill1
- agent1_skill2
- agent2_skill1
...
Configuration
See config.yaml for a complete configuration example.
Key Configuration Options
gateway_adapter: sam_mcp_server_gateway_adapter.McpAdapter
adapter_config:
# Server identity
mcp_server_name: "SAM MCP Gateway"
mcp_server_description: "Access to SAM agents via MCP"
# Transport: "http" or "stdio"
transport: http
# HTTP settings (when transport = "http")
host: "0.0.0.0"
port: 8000
# Authentication
default_user_identity: "mcp_user"
# Streaming
stream_responses: true
# Tool filtering (optional)
include_tools: [] # Include only these tools (empty = all)
exclude_tools: [] # Exclude these tools
Tool Filtering
The MCP adapter supports filtering which agent tools are exposed through the MCP server. This is useful for:
- Limiting tool visibility to specific agents or skills
- Hiding debug/internal tools from clients
- Creating specialized MCP servers with curated tool sets
Configuration
adapter_config:
# Include only data-related tools and one specific tool
include_tools:
- ".*" # Include all tools that exclude filters allows
- "data_.*" # Regex: any tool starting with "data_"
- "fetch_user_info" # Exact: specific tool name
# Exclude debug tools and test agents
exclude_tools:
- ".*_debug" # Regex: any tool ending with "_debug"
- "TestAgent" # Exact: specific agent name
- "test_tool" # Exact: specific tool name
How Filtering Works
What Gets Checked: Filters check against all three of:
- Original agent name (e.g.,
"DataAgent") - Original skill name (e.g.,
"Fetch User") - Final sanitized tool name (e.g.,
"data_agent_fetch_user")
Pattern Types: The adapter automatically detects pattern types:
- Regex patterns: Contain special characters like
.*+?[]{}()^$|\- Examples:
"data_.*",".*_debug","agent[0-9]+"
- Examples:
- Exact matches: Plain strings without special characters
- Examples:
"DataAgent","fetch_user_info","test_tool"
- Examples:
Both types can be mixed in the same array.
Priority Order (highest to lowest):
- Exclude exact match - If pattern matches exactly, tool is rejected
- Include exact match - If pattern matches exactly, tool is accepted
- Exclude regex match - If regex pattern matches, tool is rejected
- Include regex match - If regex pattern matches, tool is accepted
- Default - If
include_toolsis empty, accept; otherwise reject
Filter Examples
Example 1: Include only specific tools
include_tools:
- "weather_agent_.*" # All tools from weather_agent
- "code_assistant_review_code" # One specific tool
exclude_tools: []
Result: Only weather agent tools and the specific code review tool are exposed.
Example 2: Exclude debug/internal tools
include_tools: [] # Empty = include all
exclude_tools:
- ".*_debug" # Exclude all debug tools
- ".*_internal" # Exclude all internal tools
- "TestAgent" # Exclude entire test agent
Result: All tools except debug, internal, and test agent tools are exposed.
Example 3: Whitelist with exceptions
include_tools:
- "production_.*" # Include all production tools
exclude_tools:
- "production_agent_reset" # Except this dangerous one
Result: All production tools are exposed except production_agent_reset (exclude wins).
Example 4: Complex filtering
include_tools:
- ".*" # Include all tools that exclude filters allows
- "data_.*" # Include data tools
- "analytics_.*" # Include analytics tools
- "user_lookup" # Include specific lookup tool
exclude_tools:
- ".*_admin" # Exclude admin tools (even data/analytics)
- "data_agent_delete" # Exclude specific delete tool
- "DebugAgent" # Exclude debug agent by name
Result: All unmentioned tools, Data and analytics tools are included, but admin operations and specific dangerous tools are excluded.
Behavior Details
- Empty filters: If both
include_toolsandexclude_toolsare empty, all tools are included (default behavior) - Include-only: If only
include_toolsis specified, tools must match at least one pattern to be included - Exclude-only: If only
exclude_toolsis specified, all tools are included except those matching patterns - Case sensitivity: Exact matches are case-sensitive; regex follows pattern definition
- Invalid regex: If a regex pattern fails to compile, it's treated as an exact match
- Dynamic registration: Filters apply to both initial discovery and runtime agent registration
Checking Filter Results
When tools are filtered, the adapter logs debug messages:
DEBUG - Skipping tool weather_agent_debug (agent=WeatherAgent, skill=Debug) due to filter configuration
DEBUG - Registered MCP tool: weather_agent_forecast -> WeatherAgent/get_forecast
Enable debug logging to see which tools are being filtered:
log:
stdout_log_level: DEBUG
Tool Naming
Each agent skill becomes an MCP tool with the naming pattern:
{agent_name}_{skill_name}
For example:
- Agent:
weather_agent, Skill:get_forecast→ Tool:weather_agent_get_forecast - Agent:
code_assistant, Skill:review_code→ Tool:code_assistant_review_code
Tool names are automatically sanitized to be valid MCP identifiers (lowercase, alphanumeric with underscores).
Tool Parameters
All MCP tools accept a single parameter:
- message (string): The input message/query for the agent
How It Works
Initialization
- Adapter creates FastMCP server instance
- Queries
context.list_agents()to register any already-discovered agents - Registers callbacks with agent registry for dynamic updates
- Starts FastMCP server on configured transport
Dynamic Agent Discovery
As agents join and leave the SAM mesh:
-
Agent Joins: When a new agent publishes its AgentCard:
AgentRegistrydetects the new agent- Calls
GenericGatewayComponent._on_agent_added() - Component calls
McpAdapter.handle_agent_registered() - Adapter registers new MCP tools via
mcp_server.add_tool() - FastMCP sends
tools/list_changednotification to connected clients - MCP clients automatically refresh their tool list
-
Agent Leaves: When an agent is removed (e.g., TTL expiry):
AgentRegistrydetects the removal- Calls
GenericGatewayComponent._on_agent_removed() - Component calls
McpAdapter.handle_agent_deregistered() - Adapter removes tools via
mcp_server.remove_tool() - FastMCP sends
tools/list_changednotification to clients - Stale tools disappear from client's tool list
Tool Invocation
- MCP client calls a tool (e.g.,
weather_agent_get_forecast) - Adapter maps tool name back to agent and skill
- Creates a
SamTaskwith the message text - Submits task via
context.handle_external_input() - Returns task ID to track execution
Response Handling
_handle_tool_call()creates anasyncio.Futurefor the task- Task is submitted to SAM, and the method waits on the Future
- As agent processes the task,
handle_update()receives chunks:- Text parts are buffered for the final response
- Optionally streamed to MCP client via
mcp_context.info()(progress updates) - File and data parts are logged and reported
- On completion,
handle_task_complete():- Assembles final text from buffer
- Resolves the Future with the complete response
- This unblocks
_handle_tool_call(), which returns the result to MCP client
- On error,
handle_error()resolves the Future with an error message
File Handling in Tool Responses
The MCP gateway intelligently returns files based on their type and size, using appropriate MCP content types:
Content Type Strategy
Images (image/* MIME types):
- Small (< 5MB): Returned inline as
ImageContentwith base64 encoding - Large (≥ 5MB): Returned as
ResourceLinkfor separate download
Audio (audio/* MIME types):
- Small (< 10MB): Returned inline as
AudioContentwith base64 encoding - Large (≥ 10MB): Returned as
ResourceLink
Text Files (detected via MIME type using SAM's is_text_based_file utility):
- Small (< 1MB): Returned as
EmbeddedResourcewithTextResourceContents - Large (≥ 1MB): Returned as
ResourceLink
Other Binary Files:
- Small (< 512KB): Returned as
EmbeddedResourcewithBlobResourceContents(base64) - Large (≥ 512KB): Returned as
ResourceLink
Configuration
Size thresholds are configurable in adapter_config:
adapter_config:
inline_image_max_bytes: 5242880 # 5MB
inline_audio_max_bytes: 10485760 # 10MB
inline_text_max_bytes: 1048576 # 1MB
inline_binary_max_bytes: 524288 # 512KB
Mixed Content Responses
When a tool response includes both text and files, the MCP gateway returns a list of content blocks:
[
TextContent(type="text", text="Here is the result..."),
ImageContent(type="image", data="base64...", mimeType="image/png"),
ResourceLink(type="resource_link", uri="artifact://session/report.pdf", ...)
]
Artifact Resources
When files are too large to inline, or when explicitly requested, they are exposed as MCP resources that clients can fetch separately.
Resource URI Format
artifact://{session_id}/{filename}
Example: artifact://mcp-tool-abc123/report.pdf
Resource Features
- Session-scoped: Only accessible within the session that created them
- Auto-cleanup: Removed when the task completes
- Versioned (optional): Can access specific versions with
?version=Nparameter (future enhancement)
Accessing Resources
MCP clients can fetch resource contents using the standard resources/read request:
# Using FastMCP client
content = await client.read_resource("artifact://session_id/filename")
The resource returns either TextResourceContents or BlobResourceContents depending on the file type.
Configuration
adapter_config:
enable_artifact_resources: true # Enable/disable resource exposure
resource_uri_prefix: "artifact" # URI prefix for resources
Session Management and Execution Model
Connection-Based Sessions
The MCP gateway uses connection-based persistent sessions:
- Session Creation: When an MCP client connects and makes its first tool call, a session is created using FastMCP's
client_id - Session ID Format:
mcp-client-{client_id} - Session Lifetime: Persists for the entire MCP connection lifetime
- Cross-Tool Sharing: All tool calls from the same connection share the same session
- Isolation: Each MCP connection gets its own isolated session
RUN_BASED Execution
Each tool call uses RUN_BASED execution mode:
- No Chat History: Each tool call starts fresh with only the provided message
- Agents Don't Remember: Previous tool calls are not in the agent's context
- Stateless Tools: Each invocation is independent from previous calls
- How It Works: SAM creates a temporary session
{session}:{task_id}:runfor the LLM's chat history, then deletes it after the run completes
Artifact Persistence Across Tool Calls
Despite RUN_BASED execution, artifacts persist in the session:
- Session Storage: Artifacts are stored in the persistent connection session (not the temporary run session)
- Cross-Call Access: All artifacts created in any tool call remain accessible
- No Auto-Cleanup: Resources never expire (live until server restart)
- Accumulation: Artifacts accumulate across all tool calls from the same connection
Complete Example Flow
1. MCP Client Connects
→ FastMCP assigns client_id: "abc123"
→ Session created: "mcp-client-abc123"
2. Client calls: weather_agent_get_forecast("San Francisco weather")
→ Run session created: "mcp-client-abc123:task-xyz:run"
→ Agent generates response + forecast.png artifact (342 KB)
→ Artifact stored in: "mcp-client-abc123" session
→ forecast.png < 5MB → Returned inline as ImageContent
→ Run session deleted (no chat history kept)
→ Returns: [TextContent("The forecast..."), ImageContent(data="base64...")]
3. Client calls: data_agent_analyze("Generate report")
→ Run session created: "mcp-client-abc123:task-def:run"
→ Agent has NO memory of weather request (RUN_BASED)
→ Agent generates report.pdf (2.5 MB)
→ Artifact stored in: "mcp-client-abc123" session
→ report.pdf > 512KB → Registered as MCP resource
→ Run session deleted
→ Returns: [TextContent("Report generated"), ResourceLink(uri="artifact://mcp-client-abc123/report.pdf")]
4. Client fetches resource: resources/read(uri="artifact://mcp-client-abc123/report.pdf")
→ Successfully downloads report.pdf from session storage
5. Client calls: another_agent_process("Do something")
→ Run session created: "mcp-client-abc123:task-ghi:run"
→ Agent has NO memory of previous calls (RUN_BASED)
→ But forecast.png and report.pdf still exist in session storage
→ Can access via artifact service if needed
→ Creates output.json (145 KB)
→ output.json < 1MB + is text → Returned as EmbeddedResource
→ Run session deleted
→ Returns: [TextContent("Done"), EmbeddedResource(resource=TextResourceContents(...))]
6. Client Disconnects
→ Session artifacts remain accessible (no auto-cleanup implemented)
→ Resources live until server restart
Session Isolation and Security
- Each MCP connection gets a unique
client_idfrom FastMCP - Session IDs include the client_id:
mcp-client-{client_id} - Resources use session-scoped URIs:
artifact://{session_id}/{filename} - Result: Client A cannot access artifacts from Client B's session
- No cross-session data leakage
Troubleshooting
No tools appearing in MCP client
- Check that agents are registered in the agent registry
- Verify agents have skills defined in their AgentCard
- Check gateway logs for tool registration messages
Connection refused
- Verify the MCP server is running (check logs)
- Ensure the configured port is not in use
- Check firewall settings (for HTTP transport)
Streaming not working
- Ensure
stream_responses: truein config - Verify MCP client supports streaming
- Check that
mcp_contextis being passed through correctly
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sam_mcp_server_gateway_adapter-0.1.0-py3-none-any.whl.
File metadata
- Download URL: sam_mcp_server_gateway_adapter-0.1.0-py3-none-any.whl
- Upload date:
- Size: 42.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bb4db23462fc0b14c45f3271409af0a15d77c563f0abab028bebcefd40f6aed7
|
|
| MD5 |
3569653e27ec80285da1acab5c1d032f
|
|
| BLAKE2b-256 |
6f27490d598a5517aa8ae06e31d7528f1e8b8d11dcb98220c1e9d26f17309b28
|
Provenance
The following attestation bundles were made for sam_mcp_server_gateway_adapter-0.1.0-py3-none-any.whl:
Publisher:
release.yaml on SolaceLabs/solace-agent-mesh-core-plugins
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sam_mcp_server_gateway_adapter-0.1.0-py3-none-any.whl -
Subject digest:
bb4db23462fc0b14c45f3271409af0a15d77c563f0abab028bebcefd40f6aed7 - Sigstore transparency entry: 1219384115
- Sigstore integration time:
-
Permalink:
SolaceLabs/solace-agent-mesh-core-plugins@cd412e689d5be369c0072351e589c02e815096d3 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/SolaceLabs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@cd412e689d5be369c0072351e589c02e815096d3 -
Trigger Event:
workflow_dispatch
-
Statement type: