Securing AI Agent access to legacy SOAP APIs (Python)
Securing AI Agent access to legacy SOAP APIs (Python)
Section titled “Securing AI Agent access to legacy SOAP APIs (Python)”Connecting modern AI agents (CrewAI, LangGraph, OpenAI Operator) to legacy SOAP web services requires more than just parsing XML. It requires navigating the complex security layers that defined the “Service Oriented Architecture” (SOA) era—specifically WS-Security (WSSE) and strict firewall boundaries.
This guide provides a production-ready Model Context Protocol (MCP) server that acts as a secure gateway. It handles the heavy lifting of WSDL parsing, XML serialization, and authentication (WS-Security), exposing simple, clean tools to your AI agent.
The Architecture
Section titled “The Architecture”Legacy SOAP endpoints are notoriously hostile to LLMs. They require verbose XML payloads and rigid headers. By placing an MCP server in the middle, we create a “Smart Proxy”:
- Agent sends a JSON request (e.g.,
get_user_balance(id="123")). - MCP Server translates this into a SOAP Envelope with WS-Security headers.
- Legacy API returns an XML response.
- MCP Server parses the XML into a JSON dictionary for the Agent.
Prerequisite: The Stack
Section titled “Prerequisite: The Stack”We use FastMCP for the agent interface and Zeep for the SOAP protocol handling. Zeep is the industry standard for Python SOAP clients, offering robust support for WSDL caching and WS-Security.
1. The Code (server.py)
Section titled “1. The Code (server.py)”This server implements a dynamic SOAP client. It authenticates using UsernameToken (a common legacy standard) and is configured to route traffic through enterprise proxies if needed.
from fastmcp import FastMCPfrom zeep import Clientfrom zeep.transports import Transportfrom zeep.wsse.username import UsernameTokenfrom zeep.helpers import serialize_objectimport requestsimport jsonimport os
# Initialize the FastMCP servermcp = FastMCP("SecureSOAPGateway")
@mcp.tool()def query_soap_service( wsdl_url: str, operation: str, username: str, password: str, args: dict = None) -> str: """ Securely executes a SOAP operation against a legacy WSDL endpoint.
Args: wsdl_url: The full URL to the WSDL file (e.g., http://legacy-erp.local/service?wsdl) operation: The specific SOAP method to call (e.g., 'GetUserBalance') username: WS-Security Username password: WS-Security Password args: Dictionary of arguments to pass to the SOAP method
Returns: JSON string containing the serialized response. """ if args is None: args = {}
# 1. Configure the Transport layer (Proxies & Sessions) session = requests.Session()
# For production, inject BrightData proxy URL here # proxies = { # 'http': 'http://user:pass@brd.superproxy.io:22225', # 'https': 'http://user:pass@brd.superproxy.io:22225', # } # session.proxies.update(proxies)
# Disable SSL verification if dealing with internal self-signed certs (Common in legacy) # session.verify = False
transport = Transport(session=session)
# 2. Configure WS-Security # Many legacy systems require the PasswordText or PasswordDigest authentication method. wsse = UsernameToken(username, password)
try: # 3. Initialize the Zeep Client # strict=False allows for loose parsing of non-compliant legacy XML client = Client( wsdl=wsdl_url, transport=transport, wsse=wsse, strict=False )
# 4. Locate the operation dynamically service_proxy = client.service if not hasattr(service_proxy, operation): return f"Error: Operation '{operation}' not found in WSDL."
# 5. Execute the call # We unpack the dictionary args directly into the function method = getattr(service_proxy, operation) result = method(**args)
# 6. Serialize the complex zeep object to a standard Python dict/list structure # Zeep returns custom objects that aren't JSON serializable by default serialized_result = serialize_object(result)
return json.dumps(serialized_result, default=str)
except Exception as e: return f"SOAP Error: {str(e)}"
if __name__ == "__main__": mcp.run()2. The Container (Dockerfile)
Section titled “2. The Container (Dockerfile)”This Dockerfile ensures all system dependencies for XML parsing (libxml2, libxslt) are present and exposes the correct port for MCP communication.
# Use a lightweight Python baseFROM python:3.11-slim
# Install system dependencies required for lxml and zeepRUN apt-get update && apt-get install -y \ libxml2-dev \ libxslt-dev \ gcc \ && rm -rf /var/lib/apt/lists/*
# Set working directoryWORKDIR /app
# Install Python libraries# fastmcp: The Agent interface# zeep: The SOAP client# requests: For underlying HTTP transportRUN pip install --no-cache-dir fastmcp zeep requests
# Copy the server codeCOPY server.py .
# Expose port 8000 for Railway/Cloud compatibilityEXPOSE 8000
# Start the MCP serverCMD ["python", "server.py"]Security & Implementation Details
Section titled “Security & Implementation Details”WS-Security (WSSE)
Section titled “WS-Security (WSSE)”Legacy systems rarely use modern OAuth. They rely on the WS-Security standard, which embeds authentication credentials inside the SOAP Header XML.
- The Problem: Manually constructing XML headers is error-prone and invites injection attacks.
- The Solution: The
zeep.wsse.username.UsernameTokenclass automatically signs the header and handles nonce/timestamp generation if required by the server.
Proxy Tunneling
Section titled “Proxy Tunneling”Enterprise SOAP endpoints often sit behind rigid IP allowlists.
- BrightData / Residential Proxies: If your agent is running in the cloud (AWS/GCP), its IP will change. Using a static proxy service allows you to whitelist a single IP on the legacy firewall.
- Implementation: The commented-out
proxiesblock inserver.pyinjects this configuration directly into the underlyingrequests.Sessionused by Zeep.
Handling “Big Iron” XML
Section titled “Handling “Big Iron” XML”Legacy SOAP responses can be massive and deeply nested.
- The
serialize_objecthelper from Zeep converts these complex custom types into standard Python dictionaries and lists. - We use
json.dumps(..., default=str)to safely handle date types (datetime.date,decimal.Decimal) that frequently break JSON serializers.
How to Deploy
Section titled “How to Deploy”-
Build the Image:
Terminal window docker build -t soap-agent-gateway . -
Run the Container:
Terminal window docker run -p 8000:8000 soap-agent-gateway -
Connect your Agent: Configure your MCP client (e.g., in Claude Desktop or your custom LangChain script) to connect to
http://localhost:8000/sse. The agent will now see thequery_soap_servicetool and can autonomously figure out how to call methods defined in your WSDL.
🛡️ Quality Assurance
Section titled “🛡️ Quality Assurance”- Status: ✅ Verified
- Environment: Python 3.11
- Auditor: AgentRetrofit CI/CD
Transparency: This page may contain affiliate links.