2026-09-25 · CobolStack
Can MCP have a place in the mainframe world? Yes, and it already runs. The post's six areas and its failed-batch-job flow, mapped to real MCP tools on SteelFrame, our mainframe-compatible environment. Then the questions that actually matter: direct exposure or an integration layer, and how you govern an agent's identity, scopes, audit trail and writes.
Vidyut Saha, whose two earlier pieces on the 2030 mainframe talent model we answered in August and earlier this month, has moved on to a question that is more technical and, we think, more urgent: can the Model Context Protocol have a place in the mainframe world? His answer is a cautious yes. He sketches an AI assistant that works with z/OS, COBOL, JCL/JES, CICS, DB2 and VSAM. He walks through a failed batch job (find the job, read the JES output, identify the ABEND, trace the COBOL, check DB2, explain). He insists that RACF, authorization, auditing and governance stay at the centre. Then he asks three questions: whether the mainframe could become an active participant in an agent ecosystem, whether you would expose COBOL, CICS, DB2 and JES directly or put an integration layer in between, and where the biggest opportunity and the biggest risk lie.[1]
Infographic from Vidyut Saha's LinkedIn post. Reproduced here to credit and respond to it, not as our work.
We can answer from experience, because we built one. SteelFrame, our mainframe-compatible environment, has an MCP server in daily use. SteelFrame is an independent clean-room implementation, not IBM z/OS, and is not affiliated with or endorsed by IBM. It runs COBOL, JCL through a JES2-class spool, CICS-compatible transactions, VSAM and a DB2-compatible SQL engine to the published semantics. This post takes the six areas and the batch-failure flow, shows each one as real tool calls against a running system, and then answers the architecture and security questions the way we had to answer them in code.
MCP itself is small. It is an open protocol in which a server exposes tools
(named functions with a JSON Schema for their inputs), and an AI application's client
discovers them with tools/list and invokes them with tools/call, all over
JSON-RPC 2.0.[2] That simplicity is why mainframe
vendors have moved quickly:
zosconnect:mcp-1.0 feature" and that
enabling it in production is not recommended until it
is.[5]read-strict, tool
results are marked as untrusted data, and access is enforced through SAF/RACF on the
host.[6] An IBM Senior Technical Staff Member
describes the behaviour: "Read-only operations run without prompting. Operations with potential
side effects (like job submission) require
confirmation."[7]So the answer to "can MCP have a place?" is already yes. What is still open is what the architecture should look like, and what makes it safe.
These are the tools SteelFrame's MCP server exposes, taken from the live catalog the server publishes (99 tools on the persistent lane on the day we checked). The first column uses the post's own words. The last column is the question a security reviewer would ask about each area.
| Saha's area | SteelFrame MCP tools | Scope required | Governance control |
|---|---|---|---|
| z/OS: system & operational info | system_info, whoami, list_replies (see outstanding WTORs), reply_wtor |
system.read · jobs.read / jobs.write | read-only by default. system_info reports the engine's own security posture, so an agent can tell whether enforcement is on. Admin, operator and security verbs are left out of the MCP catalog whatever the token |
| COBOL: understanding & dependencies | search_source, find_program, explain_program, analysis_impact / graph / metrics, check_source |
datasets.read | facts come from a deterministic analysis model: copybook closure, the JCL steps that run a program, its CSD slice, the scheduler jobs. explain_program assembles those facts into a provider-agnostic request for the tenant's own configured LLM provider, so source only goes where the tenant has decided it may |
| JCL / JES: submit, monitor, output | list_jobs, get_job, read_spool, wait_job; submit_job, run_batch; hold, release, cancel, purge |
jobs.read · jobs.write | looking and acting need different scopes. read_spool returns only the last 500 lines unless asked for more, so a runaway SYSOUT cannot flood the model's context. Cancel and purge carry MCP's destructive hint |
| CICS: controlled invocation | cics_csd; cics_open_session / cics_send / cics_run_script; cics_link with a COMMAREA; cics_define |
cics.read · cics.exec · cics.admin | reading definitions, driving transactions and defining resources are three separate scopes. cics_define is refused on the live estate and works only inside an isolated workspace |
| DB2: governed data & metadata | sql (SPUFI-style: SQLCODE, message text and rows per statement) |
db2.exec | SQL execution is its own scope, separate from dataset and job access, and is granted only to agents that need SQL. Every SQL batch is recorded in the audit trail as the calling userid |
| VSAM: file & dataset analysis | vsam_info, dataset_info, read_records, decode_records (records through the copybook, beside the raw EBCDIC and packed bytes); write_records, define_cluster |
datasets.read · datasets.write | decoding is read-only. The bulk-seeding tool, load_dataset, carries the destructive hint and refuses system high-level qualifiers. Writes can be confined to a copy-on-write workspace |
Every row has a read tier and a separate write or execute tier, which is the most important design decision in the server. And there is no "run any TSO command" or "issue any console command" tool. That is deliberate, for reasons covered below.
Saha's flow is find the job → analyse JES output → identify the ABEND → trace the COBOL → check DB2 → explain. We ran it, read-only, against the hosted SteelFrame estate on 25 September 2026. The job is a deliberately broken 13-line test program left over from an earlier MCP test session, not a production failure, and we say so because the results below are quoted, not illustrated. Outputs are trimmed but otherwise verbatim.
// 1 · find the job: newest-first list, filtered on the abend field list_jobs {owner:""} → … JOB07444 IBMUSERR maxcc:"S0C7" abend:"S0C7" steps:[GO / S0C7TST] … // 2 · analyse the JES output: status + spool file list, then the one that matters get_job {jobid:"JOB07444"} → steps:[{name:"GO", pgm:"S0C7TST", rc:"S0C7"}] files: JESJCLIN · JESMSGLG · JESJCL · JESYSMSG · D0060.GO.SYSOUT (0 bytes) read_spool {jobid:"JOB07444", name:"JESYSMSG"} → CEE3207S The system detected a data exception (System Completion Code=0C7). From compile unit S0C7TST at entry point S0C7TST at statement 11 … IEF450I IBMUSERR GO - ABEND=S0C7 REASON=00000000 (the same log names the field, WS-BAD, PACKED-DECIMAL, bytes X'404040') // 3 · identify the ABEND: S0C7 = data exception; X'404040' is three EBCDIC spaces // sitting where a packed-decimal number should be // 4 · trace the COBOL find_program {name:"S0C7TST"} → source: IBMUSER.SOURCE(S0C7TST) · loadModules: IBMUSER.LOADLIB(S0C7TST) summary: {loc:13, calls:[], files:[], sql:0} · jclRunning:[] · csd: none read_member {dsn:"IBMUSER.SOURCE", member:"S0C7TST"} → 05 WS-BAD PIC S9(5) COMP-3. 05 WS-CHR REDEFINES WS-BAD PIC X(3). … MOVE X'404040' TO WS-CHR. COMPUTE WS-RES = WS-BAD + 1. ← statement 11 // 5 · check DB2: skipped on evidence. find_program says sql:0. (The door is `sql`; // a read-only catalog SELECT we ran on the same box returned SQLCODE 0.) // 6 · explain explain_program {program:"S0C7TST"} → context pack: {loc:13, paragraphs:["(MAIN) (lines 10-13, 4 stmts)"], callers:[], jobs:[], datasets:[], sql:[], complexity:1} + source, assembled as a provider-agnostic request for the tenant's configured LLM
The diagnosis needed no language model at all. The spool named the statement and the bytes. The source showed a REDEFINES that let an alphanumeric MOVE put spaces into a COMP-3 field. The analysis model said there was no SQL, so the DB2 step was correctly skipped instead of being done out of ritual. The agent's job here is sequencing and reading, and the tools give it facts to sequence. The explanation step reasons over those same facts, grounded in the analysis model, rather than replacing them.
jobs.read and datasets.read. A token
minted with those two scopes can run the whole diagnosis, and if its prompt is hijacked it still
cannot submit, purge, write or define anything. Least privilege is not a policy slide. It is two
strings in a token request.Saha's sharpest question is whether you would expose COBOL, CICS, DB2 and JES "directly through MCP", or put APIs and an integration layer between MCP and the mainframe. Our answer, reached by building it, is neither in its naive form. MCP should be a governed projection of the platform's own API. It should be neither a raw pipe into the subsystems nor a new, separately governed integration tier.
Concretely, SteelFrame has one REST route registry, and the MCP catalog is generated from it.
Every route that opts in becomes exactly one tool, whose name, description, input schema, scope and
read or destructive annotation all come from the route definition. Nothing is hand-written per
tool, so the catalog cannot drift from the dispatcher. Both MCP doors (a local stdio bridge and a
remote POST /mcp endpoint) authenticate through the same function as the REST API and
dispatch every tool call through the same call path, so scope checks, audit and metrics are
identical by construction.[9] IBM's z/OS Connect design
follows a similar instinct: the tools are the API operations you already deploy and govern, and
"no new access paths" is the point.[3][4]
Three rules came out of that design, and we would apply them on any estate, ours or IBM's:
read_spool is a verb. "Run any TSO command" is
a system. A verb has a schema, a scope and an audit shape. A general command channel has none of
them, and you are left hoping the model stays well-behaved. Zowe does expose TSO and console, and
it wraps them in capability tiers and pattern checks.[6]
We chose not to expose them at all.cics_link with a COMMAREA, or a
scripted session that returns fields by BMS name). It is not an agent scraping a 3270
stream.The MCP specification is blunt about where responsibility sits. Servers MUST validate inputs, implement access controls, rate-limit and sanitize outputs. There SHOULD "always be a human in the loop with the ability to deny tool invocations". And tool annotations are to be treated as untrusted unless they come from a trusted server.[2][10] Here is how those obligations land on a mainframe-shaped system.
| Concern | What we do in SteelFrame | What we'd insist on in production |
|---|---|---|
| Identity | every call carries a bearer token: a scoped API token or a logon session. Scope lists fail closed (no list means no scopes). Self-service tokens are clamped to the minter's own scopes. RACF-style revocation applies to both modes | one identity per agent, never a shared or SPECIAL userid. The MCP spec also forbids passing through tokens not issued to the MCP server itself[11] |
| Over-broad scopes | separate read, write and execute scopes per area. A triage agent needs jobs.read + datasets.read. SQL execution (db2.exec), CICS execution and scheduler control are each separate grants |
grant each agent only the scopes its job needs. On real DB2, a look-only agent's authorization ID should carry SELECT-only grants |
| RACF-style resource checks | RACF-style dataset and resource checks run under the agent's own userid. With enforcement switched on, a violation is denied and written to the audit trail as an ICH408I entry | enforcement on, with the agent's userid profiled like any batch ID. Zowe's server enforces through SAF/RACF on the host, which is the right place[6] |
| Audit | every mutating call (POST, PUT or DELETE), denials included, is written to the audit trail with user, token kind (mcp), operation, path and status. Every call is counted in per-operation metrics. Passwords never reach the log |
the audit trail fed into the same SIEM as your SMF records, reviewed like any other privileged-access log |
| Human in the loop | every tool carries MCP annotations: 41 read-only, 19 destructive, 39 non-destructive writes in today's catalog. MCP clients use these to decide what to confirm | treat the annotations as hints for the confirmation prompt. The enforcement belongs in the server's scopes. Confirm every write, and double-confirm destructive ones |
| Isolation & change control | create_workspace clones the estate copy-on-write, and every tool can run inside it while the live estate stays untouched. A private lane runs RAM-only with zero retention. cics_define and scheduler reset are workspace-only |
agents change copies, and promotion to production goes through the change control you already have. MCP is not a change-management system |
| Prompt injection | tool results are returned as data, and the blast radius is set by the token, not by the model's good behaviour | assume spool, source comments and record contents are attacker-reachable text. OWASP calls this indirect prompt injection, and its mitigations are least privilege and human approval for high-risk actions[12] |
| Data residency / PII | the private lane's parent-side audit keeps sizes, return codes and workspace IDs, never content. explain_program calls only the tenant's configured LLM provider, or nobody |
mask production data before it reaches any environment an agent can read. The model provider is a data processor, and your DPA should say so |
If we had to name the biggest risk, it is not a new MCP-specific attack. It is an old mainframe mistake repeated at machine speed: giving a non-human worker a powerful human identity. An agent logged on as a SPECIAL user can do whatever that user can, faster, driven by whatever text it read last. The MCP specification warns against wildcard scopes for the same reason.[11] The control is one the mainframe community already knows: a dedicated userid, profiled narrowly and audited, the way RACF has handled batch IDs for decades.
Saha's larger question is whether governed MCP access could make the mainframe "an active participant in an AI-agent ecosystem, rather than simply being a system that AI helps modernize." We think it already is one, in three roles that do not compete with each other:
run_batch returns a whole run as data, and export_state dumps datasets,
tables and spool with sha256 hashes for your harness to compare.What makes the mainframe a participant is not the protocol. It is the discipline that already governs a production LPAR: narrow identities, separate read and write privileges, an audit trail, and no change without evidence and a human signature. MCP makes it cheap for agents to ask questions. Governance decides which answers they may act on.
The SteelFrame MCP documentation is public. It covers both doors (stdio bridge and remote
POST /mcp), both authentication modes, the scope sets, the persistent and zero-retention
lanes, a worked compile → run → spool example, and a live tool reference rendered from the same
catalog your MCP client will receive: steelframe.cobolstack.com/mcp-docs.html ↗.
The same design runs on the midrange side. SteelFrame X is our IBM i-compatible (AS/400-style) environment, also an independent clean-room implementation that is not affiliated with or endorsed by IBM. It has its own MCP server over CL, RPG IV and RPG III, COBOL, DDS physical, logical, display and printer files, DB2 for i SQL, job queues, spooled files and 5250 display sessions driven one AID key at a time. Every call runs as a real user profile in a real job, under that profile's own object and special authorities, so nothing an MCP caller does can exceed what the profile could do at a 5250: steelframex.cobolstack.com/mcp-docs.html ↗.
Our MCP page puts the mainframe server and its IBM i companion side by side and describes the evaluation, agent-loop and governance engagements we run around them. Access is issued per organisation, with scopes, after a conversation, so ask for evaluation access and bring the batch failure you would most like an agent to have diagnosed.
This post responds to Vidyut Saha's LinkedIn post asking whether MCP can have a place in the mainframe world and how its architecture and security should work. It is a practitioner's opinion and question, which is how we have cited it: for its framing and its questions, never for a figure. Our responses to his earlier pieces are here and here.
zosconnect:mcp-1.0 feature; not recommended for production until it is (as read in September 2026).read-strict default capability tier; untrusted-data marking; SAF/RACF enforcement.GET /api/mcp/tools.Vendor capabilities are reported as their own documentation states them, as read in September 2026. None implies endorsement of CobolStack. SteelFrame facts (tool names, scopes, annotation counts, audit behaviour) come from the live MCP catalog, the public documentation and the server source. The batch-failure trace was run read-only against the hosted estate on 25 September 2026 and is quoted, trimmed, from its actual output. IBM, z/OS, CICS, Db2, IBM i, AS/400, RPG, RACF and other marks are trademarks of their respective owners. CobolStack is not affiliated with, endorsed by, or sponsored by IBM. SteelFrame and SteelFrame X are independent clean-room mainframe- and IBM i-compatible implementations that conform to the published specifications but are not identical to them; your licensed system remains the sole conformance authority.