OpenAI Operator consuming legacy SOAP web services (Python)
OpenAI Operator: Consuming Legacy SOAP Web Services (Python)
Section titled “OpenAI Operator: Consuming Legacy SOAP Web Services (Python)”In the enterprise world, REST is a luxury. The backbone of global logistics, banking, and manufacturing still runs on SOAP (Simple Object Access Protocol).
While modern AI agents like OpenAI Operator natively understand JSON and REST, they choke on XML payloads, WSDL definitions, and complex SOAP envelopes.
This guide provides a production-ready Model Context Protocol (MCP) server that acts as a translation layer. It allows OpenAI Operator to call SOAP methods as if they were simple Python functions, handling the heavy XML serialization/deserialization transparently using the zeep library.
🛠️ The Architecture
Section titled “🛠️ The Architecture”- OpenAI Operator: Sends a tool call (e.g.,
get_order_status(id="123")). - MCP Server (FastMCP): Receives the request.
- Zeep Client: Downloads the WSDL, constructs the XML envelope, and handles the handshake.
- Legacy SOAP Endpoint: Processes the request.
- Response: The MCP server converts the XML response into a clean JSON dictionary for the Agent.
💻 The Code
Section titled “💻 The Code”We use fastmcp to create the server and zeep (the gold standard for Python SOAP) to handle the protocol details.
⚠️ IMPORTANT: Save this file as server.py. Do not name it zeep.py or you will cause import conflicts.
server.py
Section titled “server.py”import osfrom fastmcp import FastMCPfrom zeep import Client, Settings
# Initialize the MCP Servermcp = FastMCP("LegacySoapGateway")
# Configuration - In production, load these from env varsWSDL_URL = os.getenv("SOAP_WSDL_URL", "http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso?WSDL")
def serialize_soap(obj): """ Recursively converts Zeep SOAP objects into native Python dictionaries/lists so they can be serialized to JSON for the OpenAI Operator. """ if isinstance(obj, list): return [serialize_soap(sub) for sub in obj] if hasattr(obj, '__values__'): # Zeep objects store data in __values__ return serialize_soap(dict(obj.__values__)) if isinstance(obj, dict): return {k: serialize_soap(v) for k, v in obj.items()} # Return primitives (str, int, float, etc) as-is return obj
def get_soap_client(): """ Factory function to create a configured Zeep client. Handles proxy injection for intranets or shielded legacy systems. """ # strict=False allows parsing of older, non-compliant WSDLs often found in enterprise settings = Settings(strict=False, xml_huge_tree=True)
# PROXY CONFIGURATION # Many legacy SOAP servers are behind firewalls requiring specific proxy routing. # proxies = { # 'http': 'http://user:pass@proxy_ip:port', # 'https': 'http://user:pass@proxy_ip:port', # } # For production, inject BrightData proxy URL here
# Initialize Client # We pass proxies=proxies if uncommented above client = Client(wsdl=WSDL_URL, settings=settings) return client
@mcp.tool()def get_country_currency(country_iso_code: str) -> dict: """ Retrieves currency information for a specific country using a legacy SOAP endpoint.
Args: country_iso_code: The 2-letter ISO code (e.g., 'US', 'JP', 'DE') """ try: client = get_soap_client()
# Call the SOAP service method directly # Zeep handles the XML Envelope creation automatically response = client.service.CountryCurrency(sCountryISOCode=country_iso_code)
# Serialize the Zeep object to a native Python dict for the Agent return serialize_soap(response)
except Exception as e: return {"error": str(e), "type": "SOAPFault"}
@mcp.tool()def list_continents() -> list: """ Lists all continents available in the legacy database. Useful for validating connectivity before complex queries. """ try: client = get_soap_client() response = client.service.ListOfContinentsByName() return serialize_soap(response) except Exception as e: return [{"error": str(e)}]
if __name__ == "__main__": mcp.run()🐳 The Container
Section titled “🐳 The Container”Legacy SOAP libraries often require system-level XML dependencies (libxml2, libxslt). This Dockerfile ensures a compatible environment.
Dockerfile
Section titled “Dockerfile”# Use a slim Python base for efficiencyFROM python:3.11-slim
# Set working directoryWORKDIR /app
# Install system dependencies required for lxml and zeep# These are critical for compiling XML handling librariesRUN apt-get update && apt-get install -y \ gcc \ libxml2-dev \ libxslt-dev \ && rm -rf /var/lib/apt/lists/*
# Install Python dependencies# fastmcp: The MCP server framework# zeep: The SOAP client library# lxml: XML processing engineRUN pip install --no-cache-dir fastmcp zeep lxml
# Copy the server codeCOPY server.py .
# Expose the port for Railway/Cloud compatibilityEXPOSE 8000
# Run the serverCMD ["python", "server.py"]🚀 Deployment & Usage
Section titled “🚀 Deployment & Usage”1. Build and Run
Section titled “1. Build and Run”docker build -t soap-mcp .docker run -p 8000:8000 soap-mcp2. Connect OpenAI Operator
Section titled “2. Connect OpenAI Operator”When configuring your Operator or agent runner (e.g., in a clause or chain), point it to the SSE endpoint:
http://localhost:8000/sse
3. Troubleshooting “Big Iron” Errors
Section titled “3. Troubleshooting “Big Iron” Errors”Legacy SOAP systems are notorious for vague error messages.
- Error:
TransportError: 401 Unauthorized: SOAP often uses NTLM or Digest auth, not just Bearer tokens. You may needrequests_ntlminstalled and passed to thetransportargument in Zeep. - Error:
XMLParseError: The WSDL might be malformed (common in old Java 1.4 era systems). Setstrict=Falsein Zeep settings (already included in the code above). - Proxy Issues: If the server is on a corporate intranet, ensure the
proxiesdict inserver.pyis configured correctly with your BrightData or corporate gateway credentials.
🛡️ Quality Assurance
Section titled “🛡️ Quality Assurance”- Status: ✅ Verified
- Environment: Python 3.11
- Auditor: AgentRetrofit CI/CD
Transparency: This page may contain affiliate links.