Skip to content

Semantic Kernel plugins for SOAP API integration (.NET)

Semantic Kernel plugins for SOAP API integration (.NET)

Section titled “Semantic Kernel plugins for SOAP API integration (.NET)”

As enterprises modernize, they often find themselves in a “hybrid hell”: building cutting-edge AI agents in .NET 8/9 with Microsoft Semantic Kernel, but needing those agents to talk to 20-year-old SOAP services (ASMX, WCF) that don’t play nicely with modern generated clients.

While you can generate WCF clients in .NET Core, it often leads to “DLL hell” or compatibility issues with dynamic, poorly-documented WSDLs.

The AgentRetrofit pattern for .NET shops is the Sidecar Approach:

  1. The Brain: Your Semantic Kernel agent runs in C#/.NET.
  2. The Hands: The SOAP integration runs in a lightweight, containerized Python MCP Server.

This architecture allows you to use Python’s superior zeep library for dynamic WSDL handling while keeping your main application logic in the robust .NET ecosystem. The Python container exposes the SOAP operations as “Tools” that your .NET Semantic Kernel agent can invoke via the Model Context Protocol (MCP).

This Python server acts as the “driver” for your legacy SOAP API. It handles the XML serialization, WSDL parsing, and proxy tunneling, exposing simple, clean functions to your AI agent.

from fastmcp import FastMCP
from zeep import Client, Settings
from zeep.transports import Transport
import requests
from typing import Optional, Dict, Any
# Initialize the FastMCP server
mcp = FastMCP("SoapGateway")
# CONSTANTS
# In a real deployment, these should be environment variables
WSDL_URL = "http://legacy-system.internal:8080/cmn/Service.asmx?wsdl"
def _get_soap_client() -> Client:
"""
Helper to initialize the Zeep client with proper session and proxy settings.
"""
session = requests.Session()
# PROXY CONFIGURATION
# For production, inject BrightData proxy URL here
# proxies = {
# 'http': 'http://user:pass@brightdata-proxy-url:port',
# 'https': 'http://user:pass@brightdata-proxy-url:port'
# }
# session.proxies.update(proxies)
# Disable strict WSDL validation for legacy compatibility
settings = Settings(strict=False, xml_huge_tree=True)
transport = Transport(session=session)
client = Client(wsdl=WSDL_URL, transport=transport, settings=settings)
return client
@mcp.tool()
def query_customer_status(customer_id: str) -> str:
"""
Queries the legacy SOAP service for a customer's current status.
Args:
customer_id: The 6-digit legacy customer ID (e.g., 'C99012')
Returns:
A string summary of the customer status or error message.
"""
try:
client = _get_soap_client()
# Zeep creates a dynamic proxy for the SOAP operation
# Ensure 'GetCustomerStatus' matches your actual WSDL operation name
response = client.service.GetCustomerStatus(CustID=customer_id)
# Serialize the SOAP object to a simple string/dict for the agent
return f"Status: {response.Status}, LastOrder: {response.LastOrderDate}, Balance: {response.OutstandingBalance}"
except Exception as e:
return f"SOAP Error for customer {customer_id}: {str(e)}"
@mcp.tool()
def create_support_ticket(customer_id: str, issue_description: str, priority: str = "Normal") -> str:
"""
Creates a support ticket in the legacy system via SOAP.
Args:
customer_id: The legacy customer ID.
issue_description: Text description of the problem.
priority: 'Low', 'Normal', or 'High'.
"""
try:
client = _get_soap_client()
# Construct the complex type required by the WSDL
ticket_data = {
'CustID': customer_id,
'Description': issue_description,
'PriorityLevel': priority,
'Source': 'AI_AGENT'
}
result = client.service.CreateTicket(TicketData=ticket_data)
return f"Ticket Created Successfully. Ticket ID: {result.TicketID}"
except Exception as e:
return f"Failed to create ticket: {str(e)}"
if __name__ == "__main__":
mcp.run()

To deploy this alongside your .NET application (e.g., in a generic Docker Compose setup or on Railway), use this Dockerfile. It ensures zeep and its XML dependencies are properly installed.

# Use a slim Python image to keep the sidecar lightweight
FROM python:3.11-slim
# Set working directory
WORKDIR /app
# Install system dependencies required for lxml (Zeep dependency)
RUN apt-get update && apt-get install -y \
libxml2-dev \
libxslt-dev \
gcc \
&& rm -rf /var/lib/apt/lists/*
# Copy the server code
COPY server.py .
# Install Python libraries
# fastmcp: The MCP server framework
# zeep: The industry standard SOAP client for Python
# lxml: Fast XML processing library
RUN pip install --no-cache-dir fastmcp zeep lxml requests
# Expose the standard FastMCP port (8000 for SSE/HTTP interaction)
EXPOSE 8000
# Run the server
ENTRYPOINT ["python", "server.py"]

Once this container is running (e.g., at http://localhost:8000), your .NET Semantic Kernel application treats it as an OpenAI Plugin or generic API tool.

In your C# Program.cs, you would typically import the plugin:

// Conceptual .NET Integration
var kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(...)
.Build();
// Import the MCP-bridge functions as a plugin
// (Requires an MCP client adapter or exposing the server as OpenAPI)
await kernel.ImportPluginFromOpenApiAsync(
pluginName: "SoapGateway",
uri: new Uri("http://localhost:8000/openapi.json")
);

This separation of concerns ensures your modern .NET agent stays clean, while the “dirty work” of XML SOAP handling is offloaded to the environment best suited for it.


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

Transparency: This page may contain affiliate links.