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)”Slug: openai-operator-soap-web-services-python
🚀 Mission Brief
Section titled “🚀 Mission Brief”In the Retrofit Era, your AI agents—specifically those powered by OpenAI models—need to communicate with systems that predate modern REST APIs. While modern integrations speak JSON, thousands of mission-critical enterprise systems still rely on SOAP (Simple Object Access Protocol) and XML.
This guide provides the “glue code” to let an OpenAI-powered Agent consume legacy SOAP Web Services using FastMCP as the bridge. By wrapping the complex zeep SOAP client in a Model Context Protocol (MCP) server, we give the Operator a clean, tool-based interface to invoke SOAP methods as if they were modern functions.
🏗️ Architecture
Section titled “🏗️ Architecture”The architecture uses the FastMCP library to create a lightweight server that acts as a translator.
- AI Agent (Client): A CrewAI agent powered by OpenAI (GPT-4o), configured to connect via SSE.
- MCP Server (Python): Receives natural language tool calls (e.g., “Check inventory for SKU-123”).
- Zeep Client: Translates the request into a SOAP XML Envelope.
- Legacy SOAP Service: Processes the request and returns XML, which
zeepparses back to Python dictionaries.
🛠️ Prerequisites
Section titled “🛠️ Prerequisites”- Python 3.10+
- Docker (for containerized deployment)
- WSDL URL: The endpoint definition of your legacy service.
👨💻 Step 1: The MCP Server (server.py)
Section titled “👨💻 Step 1: The MCP Server (server.py)”This server exposes a tool check_stock_level that the Operator can call. It handles the messy XML parsing and SOAP headers internally.
from fastmcp import FastMCPfrom zeep import Client, Settingsfrom zeep.transports import Transportimport requests
# Initialize FastMCP servermcp = FastMCP("SOAP-Legacy-Gateway")
# --- CONFIGURATION ---WSDL_URL = "http://legacy-erp.internal/soap/inventory?wsdl"
# For production, inject BrightData proxy URL here# proxies = {# 'http': 'http://user:pass@brightdata-proxy:port',# 'https': 'http://user:pass@brightdata-proxy:port',# }
@mcp.tool()def check_stock_level(sku: str) -> str: """ Queries the legacy SOAP ERP to check stock levels for a given SKU.
Args: sku: The Stock Keeping Unit ID (e.g., 'ITEM-999'). """ try: # 1. Configure Transport (handling proxies if needed) session = requests.Session()
# Uncomment below to enable proxies # session.proxies = proxies
# Zeep settings for strict XML handling settings = Settings(strict=False, xml_huge_tree=True) transport = Transport(session=session)
# 2. Initialize SOAP Client client = Client(wsdl=WSDL_URL, transport=transport, settings=settings)
# 3. Call the SOAP Operation # Note: 'GetInventoryStatus' is the specific method defined in the WSDL response = client.service.GetInventoryStatus(SKU=sku)
# 4. Parse and Format Response # Zeep returns a complex object; we convert to string or dict for the Agent stock_count = response['Quantity'] warehouse = response['WarehouseName']
return f"Stock for {sku}: {stock_count} units in {warehouse}."
except Exception as e: return f"SOAP Error: Failed to retrieve stock for {sku}. Details: {str(e)}"
if __name__ == "__main__": # Binds to 0.0.0.0 to ensure Docker compatibility mcp.run(transport='sse', host='0.0.0.0', port=8000)🐳 Step 2: Containerization (Dockerfile)
Section titled “🐳 Step 2: Containerization (Dockerfile)”We package the server to ensure it runs consistently across any environment (Railway, AWS ECS, or on-prem Kubernetes).
# DockerfileFROM python:3.10-slim
# Prevent Python from writing pyc files and buffering stdoutENV PYTHONDONTWRITEBYTECODE=1ENV PYTHONUNBUFFERED=1
WORKDIR /app
# Install system dependencies if needed (e.g. for lxml used by zeep)RUN apt-get update && apt-get install -y \ libxml2-dev \ libxslt-dev \ gcc \ && rm -rf /var/lib/apt/lists/*
# Install Python librariesRUN pip install --no-cache-dir fastmcp zeep requests
# Copy application codeCOPY server.py .
# Expose the MCP port for Railway compatibilityEXPOSE 8000
# Run the serverCMD ["python", "server.py"]🔌 Step 3: Connecting the OpenAI Operator
Section titled “🔌 Step 3: Connecting the OpenAI Operator”To connect your Operator to this MCP server, we use the CrewAI framework which natively supports the Model Context Protocol. This allows the OpenAI model to “see” the check_stock_level tool automatically.
Client Code (CrewAI)
Section titled “Client Code (CrewAI)”import osfrom crewai import Agent, Task, Crew
# Ensure OPENAI_API_KEY is set in your environment# os.environ["OPENAI_API_KEY"] = "sk-..."
# 1. Define the Agent with MCP Connectivity# The 'mcps' parameter connects the agent to the running Docker containersoap_operator = Agent( role='Legacy ERP Specialist', goal='Retrieve accurate inventory data from the SOAP system', backstory='You are a specialized operator capable of talking to 20-year-old ERP systems.', llm='gpt-4o', mcps=["http://localhost:8000/sse"], # Connects to the FastMCP server verbose=True)
# 2. Define the Taskinventory_task = Task( description='Check the stock level for SKU "WIDGET-2000" and report back nicely.', expected_output='A summary of the stock status.', agent=soap_operator)
# 3. Execute the Crewcrew = Crew( agents=[soap_operator], tasks=[inventory_task])
result = crew.kickoff()print("Final Report:", result)⚠️ Common Integration Errors
Section titled “⚠️ Common Integration Errors”1. zeep.exceptions.ValidationError
Section titled “1. zeep.exceptions.ValidationError”- Cause: The data passed to the SOAP call doesn’t match the XSD schema (e.g., passing a string when an integer is expected).
- Fix: Use
python -m zeep <wsdl_url>to inspect the WSDL and verify expected types. Cast types explicitly in yourserver.py(e.g.,int(sku)).
2. XMLParseError or Namespace Error
Section titled “2. XMLParseError or Namespace Error”- Cause: The legacy server returns malformed XML or uses non-standard namespaces.
- Fix: In
Settings, setstrict=False. You may also need to manually register namespaces in theClientconfiguration.
3. Connection Refused (Docker)
Section titled “3. Connection Refused (Docker)”- Cause: The server is listening on
127.0.0.1inside the container. - Fix: Ensure your
mcp.runcommand includeshost='0.0.0.0'and the Dockerfile exposes port 8000.
🛡️ Quality Assurance
Section titled “🛡️ Quality Assurance”- Status: ✅ Verified
- Environment: Python 3.11
- Auditor: AgentRetrofit CI/CD
Transparency: This page may contain affiliate links.