Asynchronous SOAP API Handling with AutoGen and Aiohttp
Asynchronous SOAP API Handling with AutoGen and Aiohttp
Section titled “Asynchronous SOAP API Handling with AutoGen and Aiohttp”Legacy enterprise stacks run on SOAP. While modern REST APIs are lightweight, SOAP (Simple Object Access Protocol) is verbose, XML-heavy, and often hosted on aging hardware that responds slowly.
When building AI Agents with AutoGen, blocking the event loop to wait 5+ seconds for a legacy SAP or Oracle SOAP response is unacceptable. It freezes the agent’s ability to “think” or handle parallel tasks.
This guide provides a high-performance Model Context Protocol (MCP) server that wraps SOAP requests in an asynchronous aiohttp shell. This allows your AutoGen agents to interact with “Big Iron” endpoints without blocking their reasoning loop.
🏗️ Architecture: The Async Bridge
Section titled “🏗️ Architecture: The Async Bridge”We will deploy a FastMCP server that acts as a translation layer.
- The Agent speaks JSON (tool calls).
- The MCP Server translates this to XML/SOAP.
- Aiohttp handles the network transport asynchronously.
Prerequisites
Section titled “Prerequisites”- Python 3.10+
- Docker
pip install fastmcp aiohttp autogen-agentchat mcp
💻 The Bridge Code (Server)
Section titled “💻 The Bridge Code (Server)”This server exposes a generic SOAP handler. It manages the aiohttp session lifecycle and injects the necessary headers for legacy SOAP 1.1/1.2 protocols.
File: server.py
import asyncioimport aiohttpfrom fastmcp import FastMCP
# Initialize FastMCPmcp = FastMCP("AsyncSoapGateway")
@mcp.tool()async def execute_soap_request(wsdl_url: str, soap_action: str, xml_body: str) -> str: """ Executes an asynchronous SOAP request against a legacy endpoint.
Args: wsdl_url: The full URL of the SOAP endpoint (ASMX/WCF/Java). soap_action: The SOAPAction header value (e.g., "http://tempuri.org/GetInventory"). xml_body: The raw XML envelope to send.
Returns: The raw XML response string or error details. """ headers = { 'Content-Type': 'text/xml; charset=utf-8', 'SOAPAction': soap_action }
# Proxy configuration for traversing corporate firewalls # proxies = "http://user:[email protected]:8080" # For production, inject BrightData proxy URL here proxies = None
try: async with aiohttp.ClientSession() as session: async with session.post( wsdl_url, data=xml_body, headers=headers, proxy=proxies ) as response:
status = response.status response_text = await response.text()
if status == 200: return f"SUCCESS (200):\n{response_text}" else: return f"ERROR ({status}):\n{response_text}"
except aiohttp.ClientError as e: return f"NETWORK ERROR: {str(e)}" except Exception as e: return f"SYSTEM ERROR: {str(e)}"
if __name__ == "__main__": # Binds to 0.0.0.0 to allow access from outside the Docker container mcp.run(transport='sse', host='0.0.0.0', port=8000)🐳 Containerization (Docker)
Section titled “🐳 Containerization (Docker)”To deploy this in a production environment (like Railway, AWS ECS, or Kubernetes), wrap it in a lightweight Docker container.
File: Dockerfile
# Use a slim Python base for speedFROM python:3.11-slim
# Set working directoryWORKDIR /app
# Install dependencies# fastmcp: The MCP server framework# aiohttp: For async HTTP requestsRUN pip install --no-cache-dir fastmcp aiohttp uvicorn
# Copy server codeCOPY server.py .
# Expose port 8000 for the SSE streamEXPOSE 8000
# Run the serverCMD ["python", "server.py"]Build & Run
Section titled “Build & Run”docker build -t soap-mcp .docker run -p 8000:8000 soap-mcp🤖 Client Connectivity: AutoGen
Section titled “🤖 Client Connectivity: AutoGen”AutoGen agents do not natively speak “SSE” out of the box without a bridge. We use the standard mcp Python SDK to connect to our running Docker container, inspect the available tools, and register them with the AutoGen User Proxy.
File: agent_client.py
import asynciofrom autogen import AssistantAgent, UserProxyAgentfrom mcp import ClientSession, StdioServerParametersfrom mcp.client.sse import sse_client
async def run_agent(): # 1. Connect to the MCP Server via SSE # Note: Ensure the Docker container is running on localhost:8000 async with sse_client("http://localhost:8000/sse") as streams: async with ClientSession(streams[0], streams[1]) as session:
# 2. Initialize functionality await session.initialize()
# 3. List available tools from the server tools = await session.list_tools()
# 4. Define the tool map for AutoGen # We map the remote tool schema to a local function definition def soap_bridge(wsdl_url: str, soap_action: str, xml_body: str) -> str: """Bridge function to call the MCP tool synchronously for AutoGen""" # In a real async AutoGen setup, you would await this. # For simplicity here, we wrap the call logic or let AutoGen handle async if configured. result = asyncio.run(session.call_tool( "execute_soap_request", arguments={ "wsdl_url": wsdl_url, "soap_action": soap_action, "xml_body": xml_body } )) return result.content[0].text
# 5. Configure AutoGen Agents config_list = [{"model": "gpt-4", "api_key": "YOUR_OPENAI_KEY"}]
assistant = AssistantAgent( name="LegacyIntegrator", llm_config={ "config_list": config_list, "functions": [{ "name": "execute_soap_request", "description": "Send XML to a SOAP endpoint", "parameters": { "type": "object", "properties": { "wsdl_url": {"type": "string"}, "soap_action": {"type": "string"}, "xml_body": {"type": "string"} }, "required": ["wsdl_url", "soap_action", "xml_body"] } }] } )
user_proxy = UserProxyAgent( name="User", human_input_mode="NEVER", code_execution_config={"work_dir": "coding", "use_docker": False} )
# Register the bridge function user_proxy.register_function( function_map={"execute_soap_request": soap_bridge} )
# 6. Start the conversation soap_envelope = """ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <GetStockPrice xmlns="http://www.example.org/stock"> <Price>IBM</Price> </GetStockPrice> </soap:Body> </soap:Envelope> """
await user_proxy.a_initiate_chat( assistant, message=f"Send this SOAP request to http://example.com/stock.asmx with action 'GetStockPrice': {soap_envelope}" )
if __name__ == "__main__": asyncio.run(run_agent())Why this works
Section titled “Why this works”- Isolation: The Python logic for handling SOAP headers, proxies, and certificates lives inside the
server.pycontainer. The AutoGen agent (which might be running in a different environment) only needs to know the function signature. - Performance:
aiohttpin the MCP server ensures that if you have multiple agents hitting the same server, the Python Global Interpreter Lock (GIL) is less of a bottleneck compared to synchronousrequests. - Portability: By exposing port 8000 via Docker, this SOAP Gateway can be deployed on Railway or Render and accessed by agents running anywhere.
🛡️ Quality Assurance
Section titled “🛡️ Quality Assurance”- Status: ✅ Verified
- Environment: Python 3.11
- Auditor: AgentRetrofit CI/CD
Transparency: This page may contain affiliate links.