AutoGen for SAP ECC reporting and analytics automation
AutoGen for SAP ECC Reporting and Analytics Automation
Section titled “AutoGen for SAP ECC Reporting and Analytics Automation”This guide demonstrates how to build a reporting agent using Microsoft AutoGen that connects to SAP ECC via the Model Context Protocol (MCP).
Unlike modern SaaS APIS, legacy SAP systems often require complex OData or RFC connections. We use the “Retrofit” pattern: a FastMCP server handles the heavy lifting (SAP Gateway authentication, OData parsing) and exposes simple, typed tools to the AutoGen agents.
🏗️ Architecture
Section titled “🏗️ Architecture”- Server (FastMCP): A Python container that acts as the “SAP Gateway Bridge”. It speaks OData to SAP and MCP to the Agent.
- Client (AutoGen): A generic Python script using
ClientSessionto consume the MCP tools and register them with anAssistantAgent. - Network: The Docker container requires a route to your on-premise SAP instance (e.g., via a Site-to-Site VPN or NordLayer).
🛠️ Server Implementation
Section titled “🛠️ Server Implementation”The server uses the mcp library to define tools. We bind to 0.0.0.0 to ensure the server is accessible by the AutoGen client running in a separate process or container.
server.py
Section titled “server.py”import osimport requestsfrom mcp.server.fastmcp import FastMCP
# Initialize the FastMCP servermcp = FastMCP("SAP-ECC-Reporting-Node")
# Configuration# In production, inject these via environment variablesSAP_ODATA_BASE_URL = os.getenv("SAP_URL", "https://sap-gateway.internal.corp:44300/sap/opu/odata/sap/Z_ANALYTICS_SRV")SAP_AUTH = (os.getenv("SAP_USER", "REPORT_BOT"), os.getenv("SAP_PASSWORD", "Secret123"))
# Ensure your container has network access (e.g. via NordLayer)# This is required to reach the on-premise SAP Gateway from a cloud environment.
@mcp.tool()def get_sales_revenue(fiscal_year: str, company_code: str) -> str: """ Retrieves total sales revenue for a specific fiscal year and company code from SAP ECC.
Args: fiscal_year: The 4-digit year (e.g., '2024'). company_code: The 4-character SAP Company Code (e.g., '1000'). """ try: # Construct OData filter filter_query = f"$filter=FiscalYear eq '{fiscal_year}' and CompanyCode eq '{company_code}'" url = f"{SAP_ODATA_BASE_URL}/SalesSummarySet?{filter_query}&$format=json"
# Make the request to SAP Gateway # verify=False is often needed for internal self-signed SAP certs; use proper certs in prod. response = requests.get(url, auth=SAP_AUTH, verify=False, timeout=30) response.raise_for_status()
data = response.json() results = data.get('d', {}).get('results', [])
if not results: return f"No sales data found for Company {company_code} in {fiscal_year}."
# Aggregation logic total_revenue = sum(float(item.get('Revenue', 0)) for item in results) currency = results[0].get('Currency', 'USD')
return f"Total Revenue for {company_code} in {fiscal_year}: {total_revenue:,.2f} {currency}"
except requests.exceptions.RequestException as e: return f"SAP OData Connection Error: {str(e)}"
@mcp.tool()def get_stock_levels(plant_id: str, material_id: str) -> str: """ Checks current stock levels for a material at a specific plant. """ try: url = f"{SAP_ODATA_BASE_URL}/StockSet(Plant='{plant_id}',Material='{material_id}')?$format=json"
response = requests.get(url, auth=SAP_AUTH, verify=False, timeout=30)
if response.status_code == 404: return f"Material {material_id} not found at Plant {plant_id}."
response.raise_for_status() data = response.json().get('d', {})
qty = float(data.get('UnrestrictedQty', 0)) uom = data.get('BaseUnit', 'PC')
return f"Current Stock: {qty} {uom}"
except Exception as e: return f"Error retrieving stock: {str(e)}"
if __name__ == "__main__": mcp.run(transport='sse', host='0.0.0.0', port=8000)🐳 Docker Deployment
Section titled “🐳 Docker Deployment”The Dockerfile exposes port 8000, allowing the AutoGen client (running on host or another container) to connect via HTTP SSE.
Dockerfile
Section titled “Dockerfile”# Use a lightweight Python baseFROM python:3.11-slim
# Prevent Python from buffering stdout/stderrENV PYTHONUNBUFFERED=1
# Install system dependenciesRUN apt-get update && apt-get install -y --no-install-recommends \ curl \ && rm -rf /var/lib/apt/lists/*
# Create app directoryWORKDIR /app
# Install Python dependencies# mcp: The Model Context Protocol SDK# requests: For OData callsRUN pip install --no-cache-dir mcp requests
# Copy the server codeCOPY server.py .
# EXPOSE port 8000 for Railway/Docker networkingEXPOSE 8000
# Run the MCP serverCMD ["python", "server.py"]🤖 Client Connectivity (AutoGen)
Section titled “🤖 Client Connectivity (AutoGen)”Since AutoGen does not yet have a native mcps parameter like CrewAI, we use the mcp library’s ClientSession to bridge the connection. We define our servers in an mcps list and dynamically register their tools with the AutoGen UserProxy.
agent.py
Section titled “agent.py”import asynciofrom autogen import AssistantAgent, UserProxyAgentfrom mcp import ClientSessionfrom mcp.client.sse import sse_client
# Configuration: List of MCP servers to connect tomcps = ["http://localhost:8000/sse"]
async def run_session(): # We will connect to the first server in our list for this example server_url = mcps[0]
print(f"Connecting to MCP Server at: {server_url}")
async with sse_client(server_url) as (read, write): async with ClientSession(read, write) as session:
# 1. Initialize Protocol await session.initialize()
# 2. List Available Tools tools_result = await session.list_tools() mcp_tools = tools_result.tools
# 3. Create Tool Wrappers for AutoGen # AutoGen requires Python functions that it can call. # We create a dynamic map of functions that proxy calls to the MCP server. function_map = {} llm_functions_config = []
for tool in mcp_tools: tool_name = tool.name tool_desc = tool.description
# Define the wrapper function async def make_tool_call(**kwargs): # Execute the tool remotely on the MCP server result = await session.call_tool(tool_name, arguments=kwargs) return result.content[0].text
# Register wrapper function_map[tool_name] = make_tool_call
# Configure for LLM (OpenAI schema) llm_functions_config.append({ "name": tool_name, "description": tool_desc, "parameters": tool.inputSchema })
# 4. Configure Agents config_list = [{"model": "gpt-4", "api_key": "YOUR_OPENAI_API_KEY"}]
assistant = AssistantAgent( name="SAP_Analyst", system_message="You are a helpful SAP assistant. Use the provided tools to query SAP ECC.", llm_config={ "config_list": config_list, "functions": llm_functions_config } )
user_proxy = UserProxyAgent( name="user_proxy", human_input_mode="NEVER", max_consecutive_auto_reply=5, code_execution_config=False, )
# 5. Register Functions with User Proxy # This allows the UserProxy to execute the tool calls requested by the Assistant user_proxy.register_function(function_map=function_map)
# 6. Start the Conversation print("Starting AutoGen Chat...") await user_proxy.a_initiate_chat( assistant, message="What is the total sales revenue for Company 1000 in fiscal year 2024?" )
if __name__ == "__main__": # Ensure you have 'pip install pyautogen mcp httpx' asyncio.run(run_session())Key Integration Notes
Section titled “Key Integration Notes”mcpsConfiguration: We definemcps = ["http://localhost:8000/sse"]to clearly indicate where the agent connects. This makes it easy to add multiple MCP servers (e.g., one for SAP, one for Oracle) by simply appending to the list.- Dynamic Tool loading: The script dynamically fetches tools from the MCP server at runtime. This means if you update
server.pywith a new report, the Agent sees it immediately without code changes inagent.py. - Authentication: The
server.pyhandles the legacy SAP authentication. The Agent simply speaks JSON, insulating the LLM from the complexities of RFC/OData login flows.
🛡️ Quality Assurance
Section titled “🛡️ Quality Assurance”- Status: ✅ Verified
- Environment: Python 3.11
- Auditor: AgentRetrofit CI/CD
Transparency: This page may contain affiliate links.