Skip to content

Connecting CrewAI to legacy SOAP APIs with Python `zeep`

Connecting CrewAI to Legacy SOAP APIs with Python zeep

Section titled “Connecting CrewAI to Legacy SOAP APIs with Python zeep”

In the world of modern AI agents, REST and GraphQL are the lingua franca. However, the enterprise world still runs on SOAP (Simple Object Access Protocol). If you are trying to get a CrewAI agent to check inventory in an old SAP system, validate a bank transaction on a mainframe, or pull patient records from a legacy EHR, you are likely facing an XML-heavy SOAP interface.

Most modern LLMs struggle to generate syntactically perfect XML for SOAP envelopes. The solution? Don’t let the LLM write the XML. Use Python’s zeep library to handle the protocol heavy lifting, and wrap it in a FastMCP server so your CrewAI agents can interact with it as a simple tool.

Instead of teaching the Agent how to format XML, we give it a tool: query_inventory(sku).

  1. CrewAI sends a JSON request to the MCP Server.
  2. FastMCP routes this to a Python function.
  3. Zeep reads the remote WSDL, constructs the SOAP XML envelope, handles authentication, and sends the request.
  4. The Legacy System responds with XML.
  5. Zeep parses the XML back into a Python dictionary.
  6. FastMCP returns the clean data to the Agent.

This server uses fastmcp to expose a SOAP endpoint. It uses zeep to inspect the WSDL (Web Services Description Language) dynamically or statically.

In this example, we model a standard “Inventory Check” against a legacy ERP.

Prerequisites:

Terminal window
pip install fastmcp zeep

File: server.py

from fastmcp import FastMCP
from zeep import Client, Settings, helpers
import json
# Initialize the MCP Server
mcp = FastMCP("LegacySoapGateway")
# Configuration for the Legacy ERP
# In production, load these from env vars
WSDL_URL = "http://legacy-erp.internal/services/InventoryService?wsdl"
@mcp.tool()
def check_inventory(sku: str, warehouse_id: str = "WH-01") -> str:
"""
Queries the legacy SOAP ERP to check stock levels for a specific SKU.
Args:
sku: The stock keeping unit ID (e.g., '123-ABC').
warehouse_id: The warehouse identifier (defaults to 'WH-01').
"""
try:
# Proxy Configuration for secure enterprise tunneling
# proxies = {
# 'http': 'http://user:pass@brightdata_proxy_url:port',
# 'https': 'http://user:pass@brightdata_proxy_url:port'
# } # For production, inject BrightData proxy URL here
# Initialize Zeep Client
# 'strict=False' is often needed for older, non-compliant WSDLs
settings = Settings(strict=False, xml_huge_tree=True)
# Initialize client (inject proxies if uncommented above)
# client = Client(WSDL_URL, settings=settings, proxies=proxies)
# For this demo, we assume the client connects directly or via system proxy
# Note: If WSDL is unreachable during dev, Zeep will raise an error.
client = Client(WSDL_URL, settings=settings)
# Create the complex type if required by the WSDL
# Many SOAP APIs require a request object rather than positional args
request_data = {
'SKU': sku,
'WarehouseID': warehouse_id,
'RequestTimestamp': '2025-01-01T12:00:00'
}
# Call the SOAP operation.
# Assume the operation name is 'GetStockLevel' defined in the WSDL.
# Check your specific WSDL for the exact operation name.
response = client.service.GetStockLevel(**request_data)
# Serialize the Zeep object to a native Python dict for the Agent
# Zeep returns custom objects that LLMs can't parse directly.
result_dict = helpers.serialize_object(response)
return json.dumps(result_dict, default=str)
except Exception as e:
return f"SOAP Interface Error: {str(e)}"
if __name__ == "__main__":
mcp.run()

To deploy this to a cloud environment (like Railway, Render, or a corporate K8s cluster), we need to containerize it.

Critical Note: We explicitly EXPOSE 8000 to ensure the MCP protocol is accessible.

File: Dockerfile

# Use a slim Python image to keep the footprint small
FROM python:3.11-slim
# Set working directory
WORKDIR /app
# Install system dependencies if needed (e.g. libxml2 for zeep speed)
RUN apt-get update && apt-get install -y --no-install-recommends \
libxml2-dev \
libxslt-dev \
&& rm -rf /var/lib/apt/lists/*
# Copy requirements (simulated here for brevity)
# In production, COPY requirements.txt . and RUN pip install -r requirements.txt
RUN pip install --no-cache-dir fastmcp zeep lxml
# Copy the server code
COPY server.py .
# Expose the FastMCP port
EXPOSE 8000
# Run the server
CMD ["python", "server.py"]

Once your Docker container is running (e.g., at http://soap-gateway:8000), you can connect it to your CrewAI agent using the MCP integration.

If you are running locally:

from crewai import Agent, Task, Crew
from crewai_tools import BaseTool
# CrewAI allows generic tool usage.
# If using the specialized MCP connector (coming in CrewAI vX), you config it there.
# For now, you might wrap the remote call if not using local execution.
# Example of defining the agent intent
inventory_agent = Agent(
role='Inventory Manager',
goal='Ensure stock levels are adequate',
backstory='You monitor the legacy SAP ECC system via the SOAP gateway.',
tools=[] # Your MCP tools are injected here or via the MCP protocol connection
)
  1. WSDL Parsing: It reads the “instructions” from the server automatically. You don’t need to manually map input types.
  2. Type Safety: It validates data before sending it, preventing “Garbage In, Garbage Out” errors that confuse AI agents.
  3. Transport Security: It handles WS-Security (WS-SE) and other complex SOAP auth standards that are difficult to implement with requests alone.
  • “Server not found”: SOAP servers often live behind corporate firewalls. You will likely need the proxies dictionary commented out in server.py to route traffic through a residential or static IP proxy (like BrightData) to whitelist the connection.
  • XML Parse Errors: Legacy systems often emit invalid XML. Set Settings(strict=False) in Zeep to force it to be lenient.

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

Transparency: This page may contain affiliate links.