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)”

Slug: openai-operator-soap-web-services-python

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.


The architecture uses the FastMCP library to create a lightweight server that acts as a translator.

  1. AI Agent (Client): A CrewAI agent powered by OpenAI (GPT-4o), configured to connect via SSE.
  2. MCP Server (Python): Receives natural language tool calls (e.g., “Check inventory for SKU-123”).
  3. Zeep Client: Translates the request into a SOAP XML Envelope.
  4. Legacy SOAP Service: Processes the request and returns XML, which zeep parses back to Python dictionaries.

  • 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.

server.py
from fastmcp import FastMCP
from zeep import Client, Settings
from zeep.transports import Transport
import requests
# Initialize FastMCP server
mcp = 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).

# Dockerfile
FROM python:3.10-slim
# Prevent Python from writing pyc files and buffering stdout
ENV PYTHONDONTWRITEBYTECODE=1
ENV 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 libraries
RUN pip install --no-cache-dir fastmcp zeep requests
# Copy application code
COPY server.py .
# Expose the MCP port for Railway compatibility
EXPOSE 8000
# Run the server
CMD ["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_agent.py
import os
from 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 container
soap_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 Task
inventory_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 Crew
crew = Crew(
agents=[soap_operator],
tasks=[inventory_task]
)
result = crew.kickoff()
print("Final Report:", result)

  • 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 your server.py (e.g., int(sku)).
  • Cause: The legacy server returns malformed XML or uses non-standard namespaces.
  • Fix: In Settings, set strict=False. You may also need to manually register namespaces in the Client configuration.
  • Cause: The server is listening on 127.0.0.1 inside the container.
  • Fix: Ensure your mcp.run command includes host='0.0.0.0' and the Dockerfile exposes port 8000.

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

Transparency: This page may contain affiliate links.