Skip to content

AutoGen agents for Mainframe CICS batch job monitoring and control

AutoGen Agents for Mainframe CICS Batch Job Monitoring

Section titled “AutoGen Agents for Mainframe CICS Batch Job Monitoring”

Bridging the gap between modern autonomous agents and legacy “Big Iron” infrastructure requires a robust translation layer. While AutoGen agents are excellent at planning and reasoning, they cannot natively speak the JCL or JES2 protocols used by IBM Mainframes.

This guide implements a FastMCP server (Model Context Protocol) that acts as a sidecar, exposing Mainframe batch operations as standard tools. We then connect an AutoGen workflow to this server, allowing agents to monitor and control CICS batch jobs via a clean abstraction.

  1. Bridge Server (FastMCP): A Dockerized Python server that wraps Mainframe interactions (z/OSMF REST API) into MCP tools.
  2. Agent Client (AutoGen): A standard AutoGen workflow that connects to the Bridge Server via SSE (Server-Sent Events) to dynamically discover and utilize Mainframe tools.

Create server.py. This server defines the tools the agent can use. It handles the specific headers, authentication, and error parsing required by the Mainframe.

import os
import logging
from fastmcp import FastMCP
# Pro Tip: Wrap this server with Helicone for production logging
# from helicone.helpers import log_to_helicone
# log_to_helicone()
# Initialize FastMCP
mcp = FastMCP("Mainframe-Batch-Controller")
# Configuration
MAINFRAME_HOST = os.getenv("MAINFRAME_HOST", "https://mainframe.internal.corp:443")
MAINFRAME_USER = os.getenv("MAINFRAME_USER", "IBMUSER")
MAINFRAME_PASS = os.getenv("MAINFRAME_PASS", "SYS1")
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@mcp.tool()
def submit_jcl_job(jcl_content: str) -> str:
"""
Submits a JCL (Job Control Language) batch job to the Mainframe (JES).
Args:
jcl_content: The full JCL script as a string.
Returns:
The Job ID (e.g., JOB00123) or error message.
"""
logger.info("Agent is submitting a JCL job...")
# Ensure your container has network access (e.g. via NordLayer)
# url = f"{MAINFRAME_HOST}/zosmf/restjobs/jobs"
try:
# In production, use requests to POST to z/OSMF
# resp = requests.put(url, data=jcl_content, auth=(MAINFRAME_USER, MAINFRAME_PASS))
# MOCK RESPONSE
if "ERROR" in jcl_content:
return "submit_jcl_job failed: JCL Syntax Error in line 4."
return "JOB00451"
except Exception as e:
return f"Connection Error to Mainframe: {str(e)}"
@mcp.tool()
def check_job_status(job_id: str) -> str:
"""
Checks the status of a submitted batch job.
Args:
job_id: The ID returned by submit_jcl_job (e.g., JOB00451).
Returns:
Status string (e.g., 'ACTIVE', 'CC 0000' (Success), 'ABEND S0C4').
"""
logger.info(f"Checking status for {job_id}...")
# MOCK RESPONSE
if job_id == "JOB00451":
return "OUTPUT (CC 0000) - Job Completed Successfully"
return "ACTIVE - Job is currently executing in CICS partition"
if __name__ == "__main__":
# Must bind to 0.0.0.0 for Docker compatibility
mcp.run(transport='sse', host='0.0.0.0', port=8000)

Create a Dockerfile to containerize the server. We expose port 8000 for the SSE stream.

# Use a lightweight Python runtime
FROM python:3.11-slim
# Prevent Python from buffering stdout/stderr
ENV PYTHONUNBUFFERED=1
WORKDIR /app
# Install dependencies
RUN pip install --no-cache-dir fastmcp requests uvicorn
# Copy the server code
COPY server.py .
# Expose the MCP SSE port
EXPOSE 8000
# Ensure your container has network access (e.g. via NordLayer)
# Run the server
CMD ["python", "server.py"]

Create agent.py. This script configures the AutoGen agents and connects them to the MCP server defined in the mcps list.

import asyncio
from autogen import AssistantAgent, UserProxyAgent, register_function
from mcp import ClientSession, StdioServerParameters
from mcp.client.sse import sse_client
# Configuration for MCP Servers
mcps = ["http://localhost:8000/sse"]
# Configuration for the LLM
llm_config = {
"config_list": [{"model": "gpt-4-turbo", "api_key": "YOUR_OPENAI_KEY"}],
"temperature": 0
}
async def run_autogen_session():
# 1. Iterate through MCP servers and connect
# In a real app, you might manage multiple sessions.
# Here we demonstrate connecting to the Mainframe bridge.
mcp_url = mcps[0]
print(f"🔌 Connecting to MCP Server at {mcp_url}...")
async with sse_client(mcp_url) as streams:
async with ClientSession(streams[0], streams[1]) as session:
await session.initialize()
# 2. Discover Tools
tools = await session.list_tools()
print(f"🛠️ Found {len(tools.tools)} Mainframe Tools:")
for tool in tools.tools:
print(f" - {tool.name}: {tool.description}")
# 3. Define Agents
# The 'UserProxy' acts as the executor (admin)
mainframe_admin = UserProxyAgent(
name="Mainframe_Admin",
human_input_mode="NEVER",
max_consecutive_auto_reply=10,
code_execution_config={"work_dir": "coding", "use_docker": False}
)
# The 'Assistant' acts as the planner (analyst)
analyst = AssistantAgent(
name="Operations_Analyst",
llm_config=llm_config,
system_message="""
You are a Mainframe Operations Analyst.
Your goal is to submit a JCL job and ensure it finishes with 'CC 0000'.
Use the available tools.
"""
)
# 4. Bridge MCP Tools to AutoGen
# We map the MCP tool calls to AutoGen's register_function dynamically
# or explicitly as shown below for clarity.
async def call_submit_job(jcl_content: str) -> str:
result = await session.call_tool("submit_jcl_job", arguments={"jcl_content": jcl_content})
return result.content[0].text
async def call_check_status(job_id: str) -> str:
result = await session.call_tool("check_job_status", arguments={"job_id": job_id})
return result.content[0].text
# Register the wrappers
register_function(
call_submit_job,
caller=analyst,
executor=mainframe_admin,
name="submit_jcl_job",
description="Submits JCL to the Mainframe."
)
register_function(
call_check_status,
caller=analyst,
executor=mainframe_admin,
name="check_job_status",
description="Checks status of a Job ID."
)
# 5. Start the conversation
print("\n🚀 Starting AutoGen Workflow...\n")
await mainframe_admin.a_initiate_chat(
analyst,
message="Submit the 'NIGHTLY_UPDATE' JCL job and verify success."
)
if __name__ == "__main__":
asyncio.run(run_autogen_session())

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

Transparency: This page may contain affiliate links.