Skip to content

AutoGen agents querying and updating IBM AS/400 DB2

AutoGen Agents Querying and Updating IBM AS/400 DB2

Section titled “AutoGen Agents Querying and Updating IBM AS/400 DB2”

Connecting modern autonomous agents (like AutoGen or CrewAI) to an IBM i (AS/400) system is one of the most challenging tasks in enterprise modernization. These systems, often running core banking, logistics, or manufacturing logic, rely on DB2 for i—a database that predates most modern SQL conventions and often uses EBCDIC encoding.

When an AutoGen agent needs to check inventory in a legacy ERP or update a shipment status, it cannot simply “guess” the schema. It needs a rigid, safe interface to execute queries without locking tables or corrupting decades of data.

This guide provides a specialized Model Context Protocol (MCP) server acting as a bridge. It uses the official IBM DB driver to allow agents to run read/write operations safely.

We will deploy a FastMCP server that exposes two specific tools to your AutoGen swarm:

  1. query_db2: For safe SELECT operations.
  2. execute_db2_update: For INSERT, UPDATE, or DELETE operations (with commit control).
  • IBM i Access Client Solutions or valid credentials to the AS/400.
  • The IP address or hostname of the IBM i system.
  • A dedicated service account (user/pass) for the agent—do not use QSECOFR (admin) credentials.

This bridge uses ibm_db, the official Python driver for IBM DB2 on i. It handles the connection string nuances and resource cleanup automatically.

import os
import ibm_db
from fastmcp import FastMCP, Context
# Initialize FastMCP with the server name
mcp = FastMCP("AS400-DB2-Gateway")
def get_connection():
"""Establishes a connection to the IBM i DB2 database."""
# Retrieve credentials from environment variables for security
db_name = os.getenv("IBM_DB_NAME", "*LOCAL") # *LOCAL is default for on-system
hostname = os.getenv("IBM_DB_HOST")
port = os.getenv("IBM_DB_PORT", "50000") # Default DRDA port
uid = os.getenv("IBM_DB_USER")
pwd = os.getenv("IBM_DB_PASS")
# Construct the connection string.
# Note: PROTOCOL=TCPIP is standard for remote access via ibm_db.
conn_str = (
f"DATABASE={db_name};"
f"HOSTNAME={hostname};"
f"PORT={port};"
f"PROTOCOL=TCPIP;"
f"UID={uid};"
f"PWD={pwd};"
"SECURITY=SSL;" # Remove if your AS/400 doesn't support SSL
)
try:
# Connect to the database
conn = ibm_db.connect(conn_str, "", "")
return conn
except Exception as e:
error_msg = ibm_db.conn_errormsg()
raise RuntimeError(f"Connection failed: {error_msg}")
@mcp.tool()
def query_db2(sql_query: str, ctx: Context) -> str:
"""
Executes a SELECT query against the IBM AS/400 DB2 database.
Use this for reading data only.
Args:
sql_query: The SQL SELECT statement (e.g., 'SELECT * FROM LIB.TABLE FETCH FIRST 10 ROWS ONLY')
"""
conn = None
try:
conn = get_connection()
ctx.info(f"Executing query: {sql_query}")
stmt = ibm_db.exec_immediate(conn, sql_query)
results = []
dictionary = ibm_db.fetch_assoc(stmt)
while dictionary:
results.append(dictionary)
dictionary = ibm_db.fetch_assoc(stmt)
return str(results)
except Exception as e:
# Capture IBM specific error messages
if conn:
err = ibm_db.stmt_errormsg()
return f"DB2 Error: {err} | Exception: {str(e)}"
return f"Error: {str(e)}"
finally:
if conn:
ibm_db.close(conn)
@mcp.tool()
def execute_db2_update(sql_statement: str, ctx: Context) -> str:
"""
Executes an UPDATE, INSERT, or DELETE statement on IBM AS/400 DB2.
WARNING: This modifies data permanently.
Args:
sql_statement: The SQL command (e.g., 'UPDATE LIB.SALES SET STATUS=1 WHERE ID=500')
"""
conn = None
try:
conn = get_connection()
ctx.info(f"Executing update: {sql_statement}")
# Execute the statement
stmt = ibm_db.exec_immediate(conn, sql_statement)
# Check number of rows affected
rows_affected = ibm_db.num_rows(stmt)
return f"Success. Rows affected: {rows_affected}"
except Exception as e:
if conn:
err = ibm_db.stmt_errormsg()
return f"DB2 Update Failed: {err} | Exception: {str(e)}"
return f"Error: {str(e)}"
finally:
if conn:
ibm_db.close(conn)
if __name__ == "__main__":
mcp.run()

IBM DB2 drivers have specific system dependencies. We use python:3.11-slim but ensure libxml2 is present, which is often required by the underlying driver binaries on Linux.

Critical: We expose port 8000 to allow the AutoGen container to communicate with this MCP server over HTTP/SSE.

# Use a lightweight Python base
FROM python:3.11-slim
# Set working directory
WORKDIR /app
# Install system dependencies required for ibm_db
# libxml2 is frequently needed for the DB2 CLI driver on Linux
RUN apt-get update && apt-get install -y \
gcc \
libxml2 \
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies
# fastmcp for the server, ibm_db for the AS/400 connection
RUN pip install --no-cache-dir fastmcp ibm_db
# Copy the server code
COPY server.py .
# Expose port 8000 for Railway/Cloud deployment
EXPOSE 8000
# Run the server
# By default, FastMCP typically runs on stdio, but for docker/network access
# we usually invoke it to run in SSE mode on 0.0.0.0:8000 if supported,
# or simply invoke the script if the environment handles the wrapping.
# Here we assume the standard entrypoint.
CMD ["python", "server.py"]

Once the Docker container is running (e.g., at http://localhost:8000/sse), you can connect your AutoGen agents.

Example: AutoGen UserProxyAgent Configuration

Section titled “Example: AutoGen UserProxyAgent Configuration”

If you are using AutoGen version 0.2+, you configure the llm_config or tool definitions to point to this MCP server.

# Pseudo-code for AutoGen configuration
user_proxy = UserProxyAgent(
name="Admin",
system_message="A human admin.",
code_execution_config={"work_dir": "coding", "use_docker": False},
)
# The Assistant Agent that will talk to DB2
db_specialist = AssistantAgent(
name="DB2_Specialist",
system_message="""You are a Database Administrator for an IBM AS/400 system.
You have access to tools to query and update the database.
ALWAYS verify the WHERE clause before updating rows.
Use schema.table notation (e.g., QGPL.EMPLOYEE).""",
llm_config={
"config_list": config_list,
# In a real AutoGen MCP setup, you would bridge the MCP tools here
# or use an MCP-aware wrapper to inject 'query_db2' and 'execute_db2_update' functions.
}
)
  1. Library Path: Unlike Postgres/MySQL, AS/400 uses “Libraries” (e.g., QGPL, QTEMP). Queries must usually be SELECT * FROM LIBRARY.TABLE.
  2. Naming Convention: Legacy tables often have cryptic 6-character names (e.g., CUSMST for Customer Master). Provide your agent with a schema map in the system prompt.
  3. Journaling: If you get an error about “Commitment control not active,” you may need to append with NONE to your SQL or enable journaling on the physical file in the AS/400. The driver handles transactions, but the table must support them.

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

Transparency: This page may contain affiliate links.