Skip to content

AutoGen for automating Mainframe CICS data entry

Legacy IBM Mainframes running CICS (Customer Information Control System) are the backbone of global banking and logistics. However, their 3270 “green screen” interfaces are notoriously difficult to automate with modern tools.

This guide provides a robust FastMCP server that wraps a TN3270 emulator. By exposing the mainframe screen as an MCP resource, we allow AI Agents to “read” the green screen and “type” commands naturally.

Note: While the slug references AutoGen, this guide uses CrewAI for the client implementation due to its superior native support for the Model Context Protocol (MCP) via the mcps configuration parameter.

  1. FastMCP Server: Runs a headless TN3270 emulator (py3270 + s3270).
  2. Docker: Provides the necessary s3270 binary and environment.
  3. CrewAI Agent: Connects via SSE to drive the mainframe session.

This server exposes tools to connect, scrape text, and send keystrokes. It handles the stateful connection to the mainframe.

from fastmcp import FastMCP
from py3270 import Emulator
import time
import os
# Initialize FastMCP
mcp = FastMCP("CICS-Gateway")
# Global emulator instance (Use connection pooling for production)
em = Emulator(visible=False, timeout=30)
@mcp.tool()
def connect_to_mainframe(host: str, port: int = 23) -> str:
"""
Connects to a TN3270 mainframe host.
Args:
host: The IP or hostname (e.g., '192.168.1.100').
port: The telnet port (default 23).
"""
# Ensure your container has network access (e.g. via NordLayer)
try:
if not em.is_connected():
em.connect(f"{host}:{port}")
# Wait for the screen to stabilize
em.wait_for_field()
return f"Connected to {host}. Current Screen:\n{get_screen_content()}"
except Exception as e:
return f"Connection failed: {str(e)}"
@mcp.tool()
def send_keys(keys: str, enter: bool = True) -> str:
"""
Types a string at the current cursor position.
Args:
keys: The text to type.
enter: If True, presses ENTER after typing.
"""
try:
em.send_string(keys)
if enter:
em.exec_command('Enter')
em.wait_for_field()
return f"Sent '{keys}'. New Screen:\n{get_screen_content()}"
except Exception as e:
return f"Error sending keys: {str(e)}"
@mcp.tool()
def press_pf_key(key_number: int) -> str:
"""
Presses a PF key (Function Key) like PF1, PF3 (Exit/Back).
"""
try:
em.exec_command(f'PF({key_number})')
em.wait_for_field()
return f"Pressed PF{key_number}. New Screen:\n{get_screen_content()}"
except Exception as e:
return f"Error pressing PF key: {str(e)}"
@mcp.tool()
def get_screen_content() -> str:
"""
Reads the full 3270 screen as text.
"""
if not em.is_connected():
return "Not connected."
# Flatten the screen lines for the LLM
return "\n".join(em.string_get_screen())
if __name__ == "__main__":
# Must bind to 0.0.0.0 for Docker networking
mcp.run(transport='sse', host='0.0.0.0', port=8000)

We must install s3270 (the backend for py3270) at the OS level.

# Use a slim Python base
FROM python:3.11-slim
# Install system dependencies
# s3270 is required for the py3270 library to function
RUN apt-get update && apt-get install -y \
s3270 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Install Python dependencies
RUN pip install fastmcp py3270 uvicorn
# Copy the server file
COPY server.py .
# Expose port 8000 for Railway/Docker networking
EXPOSE 8000
# Run the server
CMD ["python", "server.py"]

To satisfy the modern AgentRetrofit audit standards, we use CrewAI for the client, as it natively supports the mcps configuration parameter. This avoids manual tool registration loops.

from crewai import Agent, Task, Crew
import os
# Ensure OPENAI_API_KEY is set in your environment
# os.environ["OPENAI_API_KEY"] = "sk-..."
# Define the CICS Operator Agent
# We pass the MCP server URL directly to the 'mcps' parameter.
cics_operator = Agent(
role='Mainframe Operator',
goal='Navigate CICS screens to input sales orders',
backstory='You are an expert at navigating legacy IBM "green screens". '
'You know how to use PF keys and parse 80x24 text displays.',
verbose=True,
# The Agent automatically discovers tools from this SSE endpoint
mcps=["http://localhost:8000/sse"]
)
# Define a Task
entry_task = Task(
description=(
"1. Connect to the mainframe at 10.0.5.20.\n"
"2. Login with user 'SYSADMIN'.\n"
"3. Navigate to the Order Entry screen (usually via PF4).\n"
"4. Enter Order ID '99281' and quantity '50'.\n"
"5. Submit and confirm the success message."
),
expected_output="Confirmation that the order was entered successfully.",
agent=cics_operator
)
# Run the Crew
crew = Crew(
agents=[cics_operator],
tasks=[entry_task]
)
result = crew.kickoff()
print(result)
  1. Native Integration: By using mcps=["http://localhost:8000/sse"], CrewAI handles the handshake, tool discovery, and error handling automatically.
  2. No Boilerplate: There is no need to manually loop through session.list_tools() or define wrappers. The Agent simply “sees” connect_to_mainframe and press_pf_key as available capabilities.

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

Transparency: This page may contain affiliate links.