Skip to content

LangGraph for Mainframe CICS transaction automation

LangGraph for Mainframe CICS Transaction Automation

Section titled “LangGraph for Mainframe CICS Transaction Automation”

While modern AI agents thrive on REST APIs and JSON, a vast amount of critical business logic still resides in CICS (Customer Information Control System) on IBM Mainframes. These systems often utilize “Green Screen” (3270) interfaces or complex COBOL backends that are opaque to standard LLM tools.

This guide provides a production-ready Model Context Protocol (MCP) server that bridges LangGraph agents to Mainframe CICS. We utilize the py3270 library to perform programmable screen scraping, allowing agents to execute legacy transactions as if they were a human operator.

This setup treats the Mainframe as a tool resource. The LangGraph agent effectively “drives” the terminal emulator to perform data entry or retrieval.

  1. LangGraph Agent: Orchestrates the workflow (e.g., “Check inventory for Item X”).
  2. FastMCP Server: Wraps the py3270 emulator logic into callable tools.
  3. s3270 Emulator: A background process that handles the TN3270 protocol communication.
  4. CICS Region: The destination mainframe environment processing the transaction.

We use fastmcp to expose a tool that connects to a CICS region, navigates to a specific transaction, and retrieves the screen content.

import os
import time
from fastmcp import FastMCP
from py3270 import Emulator
# Initialize the MCP server
mcp = FastMCP("cics-automation")
@mcp.tool()
def execute_cics_transaction(
host: str,
transaction_id: str,
input_data: str,
row: int = 10,
col: int = 15
) -> str:
"""
Connects to a Mainframe CICS region, runs a transaction ID, enters data,
and captures the resulting screen output.
Args:
host: The mainframe hostname or IP address (e.g., '192.168.1.100').
transaction_id: The 4-character CICS transaction code (e.g., 'CESN', 'INVY').
input_data: The string data to type into the terminal.
row: The row number to start typing input (default: 10).
col: The column number to start typing input (default: 15).
Returns:
String containing the full screen text dump after execution.
"""
# Ensure your container has network access (e.g. via NordLayer)
# Initialize the 3270 emulator (requires s3270 installed in environment)
em = Emulator(visible=False)
try:
# Connect to the mainframe host
# Note: Standard telnet port is 23, secure is 992. Adjust as needed.
if not em.connect(host):
return f"Error: Could not connect to host {host}"
# Wait for the login screen or CICS banner
em.wait_for_field()
# Clear screen to ensure fresh state
em.send_clear()
em.wait_for_field()
# 1. Enter the Transaction ID (e.g., 'INVY')
em.send_string(transaction_id)
em.send_enter()
em.wait_for_field()
# 2. Enter the input data at the specified coordinates
# In a real scenario, you might need complex field navigation here.
em.move_to(row, col)
em.send_string(input_data)
em.send_enter()
# Wait for processing
em.wait_for_field()
# 3. Scrape the result
# We grab the entire screen context for the LLM to parse.
screen_content = []
for i in range(1, 25): # Standard 24x80 screen
line = em.string_get(i, 1, 80)
screen_content.append(line)
result_text = "\n".join(screen_content)
return result_text
except Exception as e:
return f"Transaction Failed: {str(e)}"
finally:
# Always terminate the connection to free up the LU (Logical Unit)
if em.is_connected():
em.terminate()
if __name__ == "__main__":
mcp.run()

To run this in a modern containerized environment (like Railway or Kubernetes), we must ensure the s3270 binary is present, as py3270 is a wrapper around this native executable.

# Use a lightweight Python base image
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/*
# Set the working directory
WORKDIR /app
# Install Python libraries
RUN pip install fastmcp py3270
# Copy the server code
COPY server.py .
# Expose the port for the MCP server
# Required for Railway compatibility
EXPOSE 8000
# Run the MCP server
CMD ["python", "server.py"]

Mainframes are sensitive to timing. If the emulator attempts to type while the “X SYSTEM” (busy) indicator is active, the input will be rejected.

  • Fix: The em.wait_for_field() method in py3270 is critical. It blocks execution until the mainframe signals it is ready for input.

Unlike HTML, CICS screens rely on absolute coordinates (Row/Column) or tab stops.

  • Strategy: Use an LLM to “map” the screen first. Create a tool read_screen(host) that returns the text with coordinates, let the agent decide where to type, and then call execute_cics_transaction with the correct row and col.

The example above is stateless (connects, runs, disconnects).

  • Advanced: For complex flows (Login -> Menu -> Submenu), you may need to maintain the Emulator instance in a global dictionary keyed by a session_id, allowing the LangGraph agent to make multiple sequential calls over the same TN3270 connection.

To test locally before deploying:

  1. Build the container:
    Terminal window
    docker build -t cics-mcp .
  2. Run the container:
    Terminal window
    docker run -p 8000:8000 cics-mcp
  3. Use the MCP Inspector or a LangGraph client to invoke execute_cics_transaction against your test CICS region.

Note: Accessing Mainframes often requires VPNs. Ensure your Docker container has the appropriate network routing or sidecar proxies configured.


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

Transparency: This page may contain affiliate links.