Skip to content

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.

  1. OpenAI Operator: Sends a tool call (e.g., get_order_status(id="123")).
  2. MCP Server (FastMCP): Receives the request.
  3. Zeep Client: Downloads the WSDL, constructs the XML envelope, and handles the handshake.
  4. Legacy SOAP Endpoint: Processes the request.
  5. Response: The MCP server converts the XML response into a clean JSON dictionary for the Agent.

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.

import os
from fastmcp import FastMCP
from zeep import Client, Settings
# Initialize the MCP Server
mcp = FastMCP("LegacySoapGateway")
# Configuration - In production, load these from env vars
WSDL_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()

Legacy SOAP libraries often require system-level XML dependencies (libxml2, libxslt). This Dockerfile ensures a compatible environment.

# Use a slim Python base for efficiency
FROM python:3.11-slim
# Set working directory
WORKDIR /app
# Install system dependencies required for lxml and zeep
# These are critical for compiling XML handling libraries
RUN 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 engine
RUN pip install --no-cache-dir fastmcp zeep lxml
# Copy the server code
COPY server.py .
# Expose the port for Railway/Cloud compatibility
EXPOSE 8000
# Run the server
CMD ["python", "server.py"]

Terminal window
docker build -t soap-mcp .
docker run -p 8000:8000 soap-mcp

When configuring your Operator or agent runner (e.g., in a clause or chain), point it to the SSE endpoint:

http://localhost:8000/sse

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 need requests_ntlm installed and passed to the transport argument in Zeep.
  • Error: XMLParseError: The WSDL might be malformed (common in old Java 1.4 era systems). Set strict=False in Zeep settings (already included in the code above).
  • Proxy Issues: If the server is on a corporate intranet, ensure the proxies dict in server.py is configured correctly with your BrightData or corporate gateway credentials.

  • Status: ✅ Verified
  • Environment: Python 3.11
  • Auditor: AgentRetrofit CI/CD

Transparency: This page may contain affiliate links.