Skip to main content
Glama
NotHarshhaa

mainframe-mcp-server

by NotHarshhaa
README.md
<h1 align="center">
  <strong>mainframe-mcp-server</strong>
</h1>

<p align="center">
  MCP Server for IBM z/OS using Zowe APIs
</p>

<p align="center">
  <a href="https://github.com/NotHarshhaa/mainframe-mcp-server/actions/workflows/ci.yml">
    <img src="https://img.shields.io/github/actions/workflow/status/NotHarshhaa/mainframe-mcp-server/ci.yml?branch=master&label=CI&style=for-the-badge" alt="CI status" />
  </a>
  <a href="https://www.npmjs.com/package/@notharshhaa/mainframe-mcp-server">
    <img src="https://img.shields.io/npm/v/@notharshhaa/mainframe-mcp-server?style=for-the-badge&color=FBBF24" alt="npm version" />
  </a>
  <a href="https://www.typescriptlang.org/">
    <img src="https://img.shields.io/badge/TypeScript-5.x-3178C6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" />
  </a>
  <img src="https://img.shields.io/badge/Node-18%2B-339933?style=for-the-badge&logo=node.js&logoColor=white" alt="Node.js 18+" />
  <a href="https://github.com/modelcontextprotocol/typescript-sdk">
    <img src="https://img.shields.io/badge/MCP-SDK-8B5CF6?style=for-the-badge" alt="MCP SDK" />
  </a>
  <a href="https://github.com/zowe/zowe-cli">
    <img src="https://img.shields.io/badge/Zowe-SDK-22C55E?style=for-the-badge" alt="Zowe SDK" />
  </a>
  <a href="./LICENSE">
    <img src="https://img.shields.io/badge/License-MIT-FBBF24?style=for-the-badge" alt="MIT License" />
  </a>
  <img src="https://img.shields.io/badge/Status-Stable-22C55E?style=for-the-badge" alt="Status: Stable" />
</p>

> **An enterprise-grade Model Context Protocol (MCP) server for IBM z/OS environments.**
> Bridges MCP-compatible AI agents with mainframe resources — jobs, datasets, USS, and operational diagnostics — using the Zowe SDK (Open Mainframe Project) as the connectivity layer.

<p align="center">
  <img src="./public/banner.png" alt="mainframe-mcp-server — Bridge AI agents with IBM z/OS via MCP and Zowe SDK" width="100%" />
</p>

---

## Contents

- [What it does](#what-it-does)
- [How it's different](#how-its-different)
- [z/OS prerequisites](#zos-prerequisites)
- [Tools](#tools)
- [Prompts & resources (MCP-native surface)](#prompts--resources-mcp-native-surface)
- [Enterprise security](#enterprise-security)
  - [Enterprise features](#enterprise-features)
- [Per-user identity & SSO](#per-user-identity--sso)
- [Governed writes](#governed-writes)
- [Trust model & prompt injection](#trust-model--prompt-injection)
- [How the intelligence tools work](#how-the-intelligence-tools-work)
- [Incident management & runbooks](#incident-management--runbooks)
- [Quick start](#quick-start)
- [Transports](#transports)
- [Limits & timeouts](#limits--timeouts)
- [Troubleshooting & FAQ](#troubleshooting--faq)
- [Examples](#examples)
- [Project & community](#project--community)
- [License](#license)
- [Trademarks](#trademarks)

---

## What it does

`mainframe-mcp-server` exposes z/OS capabilities as [Model Context Protocol](https://modelcontextprotocol.io/) tools. An AI agent can list jobs, read datasets, inspect USS paths, submit JCL, and run operational diagnostics — without you writing Zowe CLI commands by hand.

Connectivity is handled by the [Zowe SDK](https://github.com/zowe/zowe-cli) (Open Mainframe Project) over z/OSMF REST. This project adds the MCP tool layer, input validation, safety limits, and AI-friendly response formatting on top.

<p align="center">
  <img src="./docs/assets/demo.gif" alt="Demo: an agent analyzing a failed job and predicting batch failure risk" width="640" />
</p>

---

## How it's different

| | **mainframe-mcp-server** | **Zowe CLI + scripts** | **Host IDE tooling** (IDz, Z Open Editor) | **Other z/OS MCP bridges** |
|---|---|---|---|---|
| **Interface** | MCP tools for AI agents | Terminal commands, shell scripts | Eclipse/VS Code GUIs | MCP tools for AI agents |
| **Output** | Structured sections & tables tuned for LLM reasoning | Raw terminal text you must parse yourself | IDE views | Varies by project |
| **Guardrails** | Read-only by default, tool allow/block lists, dataset & USS boundaries, audit logging, JCL size cap | None built in — you script them | Client-side only | Varies by project |
| **Failure knowledge** | Curated abend-code reference, failing-step heuristics, risk scoring | Manual message lookup | Interactive debugging | Rare |
| **Install** | `npx` — no server-side footprint beyond z/OSMF access | Node + Zowe CLI + your scripts | Full IDE estate | Often heavier runtimes |
| **Coverage** | Jobs, datasets, USS, CICS, Db2, SMF/RMF, RACF audit (30+ tools) | Full Zowe command set | Edit, debug, compile | Varies |

Honest positioning: the z/OS MCP ecosystem is early. IBM and community authors publish their own MCP bridges — evaluate them against your requirements. This project's differentiators are **zero-dependency npm install**, **guardrails enforced at the server layer**, and **output shaped for LLM consumption** rather than human eyes. It is **not** a replacement for Zowe CLI or IBM Z Open Editor — it is an MCP bridge over the same z/OSMF APIs.

---

## z/OS prerequisites

The server is a pure client — nothing is installed on z/OS. It requires REST services that are enabled in standard z/OSMF estates.

### z/OSMF (required — all core tools)

| z/OSMF REST service | Used by | Notes |
|---|---|---|
| **Jobs REST** (`/zosmf/restjobs/jobs`) | `list_jobs`, `get_job_status`, `get_job_output`, `get_job_jcl`, `submit_jcl`, all operations & intelligence tools | List, query, spool content, JES submit |
| **Files REST — datasets** (`/zosmf/restfiles/ds`) | `list_datasets`, `search_dataset`, `read_dataset`, `search_members`, `get_dataset_info` | Catalog list, members, content read |
| **Files REST — USS** (`/zosmf/restfiles/fs`) | `list_uss_directory`, `read_uss_file`, `search_uss_files`, RACF audit source | Directory list, file read |
| **z/OSMF info** (`/info`) | `verify_zosmf_connection` | Optional; version read-out only |
| **RMF REST** (`/rmf/mvs/GPO/`) | `get_smf_metrics` | Optional; disabled with `RMF_METRICS_ENABLED=false` |

**Baseline:** z/OS 2.4 LTS or later with APAR-current z/OSMF — the target the Zowe SDK (v8) is built against. Core job/dataset/USS tools are expected to work on z/OSMF levels from z/OS 2.3 onward; if you run an older or unusual level, [open an issue](https://github.com/NotHarshhaa/mainframe-mcp-server/issues) with your results so the matrix can be updated.

### CICS via CMCI (optional — `list_cics_regions`, `get_cics_region_status`, `list_cics_transactions`)

- CICS TS 5.4 or later with **CMCI** active (a CICSPlex SM WUI region, or a single-region CMCI setup).
- Set `CMCI_CONTEXT` (CICSplex name or region APPLID); optionally `CMCI_HOST`, `CMCI_PORT` (default `1490`), `CMCI_BASE_PATH` (default `/CICSSystemManagement`).
- The service account needs RACF/SAF authority to the CMCI data sets (`XCSDATA`/`CICSPLEX` resource classes as configured at your site).

### Db2 REST (optional — `list_db2_subsystems`, `search_db2_catalog`)

- Db2 12 for z/OS or later with the **Db2 REST services** capability exposed at `DB2_HOST`:`DB2_PORT` (default `50400`, base path `/dbrest`) — direct DDF-served REST or fronted by your API mediation layer.
- Set `DB2_LOCATION` (the subsystem location name). Tools run **read-only catalog SQL** against `SYSIBM.SYSTABLES` with literal-escaped, size-capped input.

### RACF audit source (optional — `query_racf_audit`)

- An existing audit extract the service account can read: a USS log file (`RACF_AUDIT_USS_PATH`) or a sequential dataset (`RACF_AUDIT_DATASET`), e.g. ADUINFOS/IRRADU00 output written to file. The server parses the text; it does not run IRRADU00 for you.

### Service account

Use a dedicated z/OS user with least-privilege RACF profiles (job list/read authority, submit authority only if `SECURITY_READ_ONLY=false`).

---

## Tools

**Type legend:** **READ** — reads z/OS state, never modifies anything · **WRITE** — submits work to JES · **OFFLINE** — no mainframe I/O at all.

### Jobs

| Tool | Type | Description |
|---|---|---|
| `list_jobs` | READ | List jobs by owner, prefix, or status |
| `get_job_status` | READ | Status and return code for a job |
| `get_job_output` | READ | Spool inventory or DD content |
| `submit_jcl` | **WRITE** | Submit inline JCL — governed by pre-flight scan, change ticket, change window, and optional human approval |
| `approve_write` | **WRITE** | Execute a pending JCL submission after human review (requires `SECURITY_WRITE_APPROVAL=true`) |
| `analyze_job_failure` | READ | Abend code, failing step, and suggested fix |
| `get_job_jcl` | READ | Retrieve submitted JCL from job spool |

### Datasets

| Tool | Type | Description |
|---|---|---|
| `list_datasets` | READ | List datasets matching an HLQ pattern |
| `read_dataset` | READ | Read a sequential dataset or PDS member |
| `search_dataset` | READ | Search datasets by pattern |
| `search_members` | READ | Search or list PDS members |
| `get_dataset_info` | READ | Catalog attributes without reading content |

### USS

| Tool | Type | Description |
|---|---|---|
| `list_uss_directory` | READ | List a USS directory |
| `read_uss_file` | READ | Read a USS file |
| `search_uss_files` | READ | Search files under a USS path |

### Operations

| Tool | Type | Description |
|---|---|---|
| `find_failed_jobs` | READ | Failed jobs in a time window |
| `summarize_abends` | READ | Abend code breakdown across recent jobs |
| `system_health_summary` | READ | Active, queued, and failed job snapshot |
| `investigate_incident` | READ | Full incident bundle for a job |
| `lookup_abend_code` | OFFLINE | Abend reference lookup (S0C7, S806, S222, S0C4, …) |
| `verify_zosmf_connection` | READ | Test z/OSMF connectivity and auth |
| `compare_jobs` | READ | Side-by-side comparison of two jobs |
| `get_user_jobs_summary` | READ | Job counts by status for an owner |

### CICS

Requires optional CMCI configuration — see [z/OS prerequisites](#zos-prerequisites).

| Tool | Type | Description |
|---|---|---|
| `list_cics_regions` | READ | List CICS regions via CMCI REST |
| `get_cics_region_status` | READ | Status and attributes of a CICS region |
| `list_cics_transactions` | READ | Transaction definitions in a region |
| `get_cics_region_stats` | READ | Region statistics/attributes from the CMCI `CICSRegion` resource |
| `cics_newcopy` | WRITE | NEWCOPY a program via CMCI action (CICS TS 5.6+) |
| `cics_disable_transaction` | WRITE | Disable/enable a local transaction via CMCI SET action |

### Db2

Requires optional Db2 REST configuration — see [z/OS prerequisites](#zos-prerequisites).

| Tool | Type | Description |
|---|---|---|
| `list_db2_subsystems` | READ | List Db2 locations via Db2 REST |
| `search_db2_catalog` | READ | Read-only catalog search (tables, views, aliases) |

### Console & JES

Operator commands run through an extended MCS console (`ZOSMF_CONSOLE_NAME`);
command tools are gated writes subject to read-only mode, policy roles,
change tickets, and change windows.

| Tool | Type | Description |
|---|---|---|
| `issue_console_command` | WRITE | MVS commands (`D A,L`, `D GRS,RES=…`) with follow-up message collection |
| `get_console_messages` | READ | Operations-log tail (SYSLOG-style console feed) |
| `hold_job` / `release_job` | WRITE | JES2 `$HJ` / `$OJ` job hold and release |
| `cancel_job` | WRITE | Cancel a job via the z/OSMF jobs API |
| `get_wlm_status` | WRITE | `D WLM` display (service policy / resources) |
| `ims_command` | WRITE | IMS commands via the system console (`/DIS ACTIVE`, `/DIS TRAN …`) |
| `get_hsm_activity` | READ | DFSMShsm recall/migration activity from the operations log |
| `analyze_logrec` | WRITE | Submit an EREP report against LOGREC and summarize error records |

### Schedulers

| Tool | Type | Description |
|---|---|---|
| `scheduler_job_status` | READ | Control-M / CA 7 / IBM WLS job status via their REST APIs (`SCHEDULER_ADAPTERS`) |
| `job_dependencies` | READ | JCL dataset-flow analysis — inputs, outputs, updates, PROCs (predecessor/successor material) |

### Messaging (IBM MQ)

Requires `MQ_REST_HOST` (mqweb server) and optional `MQ_REST_PORT`/`_USER`/`_PASSWORD`.

| Tool | Type | Description |
|---|---|---|
| `mq_queue_status` | READ | Queue curdepth vs maxdepth with high-water warnings |
| `mq_channel_status` | READ | Channel status (RUNNING/RETRYING/STOPPED) and connection counts |

### Storage & Capacity

| Tool | Type | Description |
|---|---|---|
| `get_volume_space` | READ | DASD volume space via z/OSMF SMI, with low-space alerts (`STORAGE_FREE_THRESHOLD`) |
| `get_job_capacity` | READ | Per-step and total CPU from JESMSGLG (IEF374I/IEF376I) + MSU estimate (`CAPACITY_MSU_RATING`) |
| `estimate_mlc_cost` | OFFLINE | MLC cost attribution from CPU/MSU (`MLC_MSU_MONTHLY_PRICE`) |

### Dumps

| Tool | Type | Description |
|---|---|---|
| `summarize_dump` | READ | CEEDUMP / SYSMDUMP / SYSUDUMP digest — completion codes, failing module, LE conditions |

### SMF / Security

| Tool | Type | Description |
|---|---|---|
| `get_smf_metrics` | READ | SMF/RMF performance metrics snapshot |
| `query_racf_audit` | READ | Query RACF audit records from a log source |
| `security_posture_summary` | OFFLINE | Report active security controls and recommendations |

### Intelligence

Built in — see [How the intelligence tools work](#how-the-intelligence-tools-work).

| Tool | Type | Description |
|---|---|---|
| `analyze_root_cause` | READ | Deep root-cause analysis with correlation and action items |
| `predict_batch_failures` | READ | Predictive risk scoring for recurring batch failures |

### Governance

| Tool | Type | Description |
|---|---|---|
| `list_environments` | OFFLINE | Named z/OS environments (DEV/TEST/UAT/PROD, LPARs) with per-environment read-only and role policy |
| `whoami` | OFFLINE | Acting identity, resolved role, permission summary, active environment, data-protection status |

### Incident management

Optional — file tickets from investigation bundles. Configure one provider with the `INCIDENT_*` variables (see [Incident management & runbooks](#incident-management--runbooks)); the tool registers only when a provider is set.

| Tool | Type | Description |
|---|---|---|
| `create_incident_ticket` | WRITE | Rebuild the `investigate_incident` bundle for a job and file it as a ServiceNow incident, Jira issue, or PagerDuty incident |

### Internal documentation (RAG)

Optional — retrieval over your own runbooks and playbooks. Point `DOCS_SOURCES` at local `.md`/`.txt` files; the tool registers only when sources are set.

| Tool | Type | Description |
|---|---|---|
| `search_internal_docs` | OFFLINE | Retrieve the most relevant excerpts from internal documentation, with file and heading attribution |

See [docs/tools-reference.md](./docs/tools-reference.md) for full input/output details.

---

## Prompts & resources (MCP-native surface)

Beyond tools, the server exposes the full MCP surface so clients can offer
reusable playbooks and address mainframe data directly:

**Prompts** — guided, multi-step playbooks your client surfaces like slash commands:

| Prompt | Purpose |
|---|---|
| `triage_failed_job` | Triage one failed job (or every failure in a window) end-to-end: investigate, check runbooks and internal docs, optionally file a ticket |
| `morning_batch_health_check` | Overnight health check for the morning standup: system health, failures, abend trends, prediction, capacity and storage |
| `investigate_abend` | Deep-dive one abend code: reference entry, internal docs, recent occurrences, remediation plan |

**Resources** — addressable read-only URIs (gated, masked, and audited exactly
like the equivalent tools):

| URI | Returns |
|---|---|
| `mainframe://jobs/{jobId}/spool` | Spool file (DD) inventory for a job |
| `mainframe://jobs/{jobId}/spool/{ddName}` | Content of one spool DD |
| `mainframe://datasets/{dsn}` | Member listing of a PDS/PDSE |
| `mainframe://datasets/{dsn}/members/{member}` | Content of one member |
| `mainframe://reference/abend-codes` | The built-in abend reference catalog |

**Tool annotations** — every tool advertises `readOnlyHint`, `destructiveHint`,
`idempotentHint`, and a human-readable `title`, derived from the same
write-tool registry the server enforces. Clients that confirm based on hints
(reactive confirmations, plan approval, etc.) agree with the server's own
gates: reads are `readOnlyHint: true`; `submit_jcl`, `cancel_job`,
`issue_console_command`, and the other destructive operations are flagged
`destructiveHint: true`.

---

## Packaging & supply chain

Everything a security team asks for on day one is built into the release pipeline
(see [docs/deployment.md — Supply-chain assurance](./docs/deployment.md#supply-chain-assurance)):

| Artifact | Where | Assurance |
|---|---|---|
| npm package | `@notharshhaa/mainframe-mcp-server` | Published **with SLSA provenance** (GitHub Actions OIDC) — `npm audit signatures` |
| Official image | `ghcr.io/notharshhaa/mainframe-mcp-server` (version / minor / SHA tags) | **Non-root**, signed with **Sigstore keyless cosign**, BuildKit provenance `mode=max` + SBOM attestations |
| SBOMs | GitHub release assets | SPDX **and** CycloneDX for both the package and the image |
| Checksums | `sha256sums.txt` on the release | Signed with cosign (`sha256sums.txt.sig`) |
| Helm chart | [`helm/mainframe-mcp-server/`](./helm/mainframe-mcp-server/README.md) | Non-root + read-only rootfs + dropped caps by default; policy file and SIEM sinks wired via values |

Verify the image before you run it:

```bash
cosign verify ghcr.io/notharshhaa/mainframe-mcp-server:3.0.0 \
  --certificate-identity-regexp 'https://github.com/NotHarshhaa/mainframe-mcp-server/\.github/workflows/release\.yml@refs/tags/v' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
```

The docker-compose file and the Helm chart ship hardened defaults
(`read_only`, `cap_drop: ALL`, `no-new-privileges`, seccomp `RuntimeDefault`,
non-root uid 1000).

---

## Enterprise security

Security is enforced at the **MCP server layer**, in addition to RACF/z/OSMF permissions on the mainframe itself. Every control below is enabled through environment variables:

| Control | Env variable | Purpose |
|---|---|---|
| Read-only mode | `SECURITY_READ_ONLY` | **Default `true`**: `submit_jcl`, `approve_write`, and write operations are blocked unless you explicitly opt in |
| Tool allowlist | `SECURITY_ALLOWED_TOOLS` | Only permit specific tools (least privilege) |
| Tool blocklist | `SECURITY_BLOCKED_TOOLS` | Explicitly deny dangerous tools |
| Dataset boundaries | `SECURITY_ALLOWED_DATASET_PATTERNS` | Restrict dataset access by HLQ pattern |
| USS boundaries | `SECURITY_ALLOWED_USS_PATHS` | Restrict USS paths by prefix |
| Audit logging | `SECURITY_AUDIT_LOGGING=true` | Log every tool call (credentials redacted) |
| JCL size limit | `SECURITY_MAX_JCL_BYTES` | Cap inline JCL submit size |
| TLS verification | `ZOSMF_REJECT_UNAUTHORIZED=true` | Enforce certificate validation (default `true`) |

### Enterprise features

Four scale-up capabilities build on these controls — see
[docs/enterprise.md](./docs/enterprise.md) for the full guide and
[config/policy.example.yaml](./config/policy.example.yaml) for a worked policy:

- **Multi-environment support** — name several systems (DEV/TEST/UAT/PROD,
  multiple LPARs) via a policy file or `ZOSMF_ENVIRONMENTS`; every tool takes
  an `environment` argument; each environment carries its own credentials and
  policy, e.g. `readOnly: true` on PROD.
- **Policy-as-code & RBAC** — one YAML file (`SECURITY_POLICY_FILE`) maps
  roles to tool allow/deny lists, dataset/USS globs, change-ticket rules, and
  users to roles per environment; `${env:VAR}` keeps credentials out of the
  file; unknown users fail closed.
- **Audit & SIEM** — structured JSON audit records with correlation ids (user,
  MCP client, tool, redacted arguments, outcome, duration, result size),
  exported to a file, Splunk HEC, any HTTP webhook (ELK/syslog bridges), or
  OpenTelemetry. Records are SHA-256 hash-chained (tamper-evident); verify
  with `npm run verify:audit`.
- **Data protection** — PII, credit-card numbers (Luhn-validated), SSNs,
  embedded secrets, JWTs, and private keys are masked in job output and
  dataset/USS reads by default; policy classifications add per-resource
  redaction rules, line caps, head/tail sampling, and role-gated access.

---

## Per-user identity & SSO

By default the server authenticates to z/OSMF with one configured credential.
Enterprise deployments usually need RACF to see **who asked**, not "the MCP
service account". Four mechanisms are built in:

**PassTicket identity propagation** — the strongest fit for shared deployments:

```bash
ZOSMF_USER_ID=PAYUSR            # the end user RACF should see
ZOSMF_PASSTICKET_APP=ZOSMF      # PTKTDATA application (RDEFINE PTKTDATA ZOSMF ...)
```

The server authenticates with the service credential, calls the z/OSMF
Generate PassTicket API for `PAYUSR`, and issues every z/OSMF call under a
PassTicket-authenticated session **as that user**. PassTickets are cached for
8 minutes and regenerated transparently; CICS/Db2 REST calls keep the service
identity. Requires the service account to be permitted to generate
PassTickets for the target user.

**mTLS client certificates:**

```bash
ZOSMF_CERT=/pki/client.pem
ZOSMF_CERT_KEY=/pki/client.key
```

Certificate authentication takes precedence over token/basic. Combine with a
RACF IDID mapping (certificate → user) for certificate-based identity.

**JWT / APIML tokens (SSO):**

```bash
ZOSMF_TOKEN=$JWT
ZOSMF_TOKEN_TYPE=APIML     # or JWT — both travel as Authorization: Bearer
ZOSMF_BASE_PATH=/api/v1    # APIML gateway prefix
```

`ZOSMF_TOKEN_TYPE=LTPA2` (default) keeps classic z/OSMF session-token
behavior. Full OIDC/SSO flows run in your IdP/gateway — inject the issued JWT
via `ZOSMF_TOKEN_FILE` so a vault or credential-helper agent can refresh it
without touching the config file.

**Zowe team config import** — reuse existing Zowe connection settings:

```bash
ZOWE_TEAM_CONFIG=/root/.zowe/zowe.config.json
ZOSMF_PROFILE=lpar1.zosmf    # default: defaults.zosmf
```

Any z/OSMF setting (host, port, base path, credentials, `rejectUnauthorized`)
not set via environment variables is filled from the profile. Values marked
`@secure` are honored when stored in the config file itself; secrets held by
an OS-keychain credential manager are not readable by this process and are
skipped. Environment variables always win.

---

## Governed writes

`submit_jcl` is the only way an agent can mutate anything on z/OS — so it runs
through a governance pipeline before JES ever sees the job. Every layer is
independent; enable what your change process requires.

| Gate | Env variable | Default |
|---|---|---|
| Pre-flight JCL scan | `SECURITY_JCL_SCAN` | on |
| Strict scan mode (WARN blocks) | `SECURITY_JCL_SCAN_STRICT` | off |
| Blocked utilities | `SECURITY_BLOCKED_PROGRAMS` | `ADRDSSU, ICKDSF, IEHINITT` |
| Restricted job classes | `SECURITY_BLOCKED_JOB_CLASSES` | none |
| Change-ticket requirement | `SECURITY_CHANGE_TICKET_PATTERN` | off |
| Change window | `SECURITY_WRITE_WINDOW` / `SECURITY_WRITE_DAYS` / `SECURITY_WRITE_WINDOW_TZ` | off / every day / UTC |
| Human-in-the-loop approval | `SECURITY_WRITE_APPROVAL` | off |

**The pre-flight scan blocks**, with rule name and JCL line number:

- `IDCAMS DELETE` in SYSIN instream data (`IDCAMS ALTER` is a WARN; blocks in strict mode)
- `IEFBR14` steps that delete datasets via `DISP=(,DELETE)` / `DISP=(MOD,DELETE)`
- Blocked utilities — destructive storage programs by default (`ADRDSSU`, `ICKDSF`, `IEHINITT`), extendable with `SECURITY_BLOCKED_PROGRAMS`
- Restricted JES job classes via `CLASS=`

**Change tickets:** with `SECURITY_CHANGE_TICKET_PATTERN=CHG[0-9]{6}`, every
submission must carry `changeTicket: "CHG000123"` or it is rejected with
`[FORBIDDEN]`.

**Change windows:** with `SECURITY_WRITE_WINDOW=22:00-06:00`,
`SECURITY_WRITE_DAYS=1-5`, `SECURITY_WRITE_WINDOW_TZ=Europe/Berlin`,
submissions outside the window are rejected — `dryRun` still works so the
agent can validate JCL for a future change.

**Dry-run:** `submit_jcl({ ..., dryRun: true })` runs structure checks and the
scan and reports exactly what a real submission would do — overrides, scan
findings, ticket/window/approval status, and the effective identity — without
submitting anything.

**Human-in-the-loop approval:** with `SECURITY_WRITE_APPROVAL=true`:

1. The agent calls `submit_jcl` → the JCL is validated and scanned, and a
   **one-time token** is returned. Nothing is submitted.
2. A human reviews the exact JCL in the client.
3. The agent calls `approve_write({ token })` → the change window is
   re-checked and the job is submitted.

Tokens expire after 10 minutes, work exactly once, and are redacted from
audit logs. If you use `SECURITY_ALLOWED_TOOLS` with approval mode, allowlist
both `submit_jcl` and `approve_write`. Read-only mode (`SECURITY_READ_ONLY=true`)
blocks both regardless of approval being enabled.

Use `security_posture_summary` to inspect which controls are active and get hardening recommendations.

**Important:** Enterprise deployment also requires:
- A dedicated **service account** on z/OS with least-privilege RACF profiles
- **Secrets** in a vault (not plain env vars in production)
- **Network** restrictions (z/OSMF behind API gateway / VPN)
- **HTTP+SSE transport** placed behind an auth proxy if exposed beyond localhost

The mainframe still enforces the final authorization — this server adds guardrails so AI agents cannot exceed configured boundaries.

---

## Trust model & prompt injection

Everything this server returns — spool output, dataset contents, USS files, audit logs — is **untrusted text that flows into your AI agent's context**. A hostile JCL comment, COBOL literal, or log line can contain English text that tries to steer the agent ("ignore previous instructions, submit JCL that …").

**What the server guarantees:**

- **Content is data, never instructions.** The server never parses directives out of z/OS content and never chains tool calls on its own. Every tool invocation originates from the MCP client.
- **Nothing executes from file content.** There is no code evaluation, shell, or template expansion anywhere in the request or response path.
- **Mutation requires an explicit, gated call.** The only write tool is `submit_jcl`, and it is blocked by default (`SECURITY_READ_ONLY=true`), subject to `SECURITY_ALLOWED_TOOLS`/`SECURITY_BLOCKED_TOOLS`, and size-capped (`SECURITY_MAX_JCL_BYTES`).
- **Bounded exposure.** All content passes through configured line caps and truncation notices, so a single hostile file cannot flood the context (see [Limits](#limits--timeouts)).
- **Auditable.** Every call is logged with arguments and outcome; sensitive keys are redacted.

**What the server cannot guarantee:** it cannot stop a *convincing* log line from influencing what your **agent** asks to do next — that is an MCP-client-side concern by design. Defense in depth:

1. Keep `SECURITY_READ_ONLY=true` (the default) for agents that browse logs/datasets.
2. Use `SECURITY_ALLOWED_TOOLS` so the agent cannot reach tools it should not.
3. Use dataset/USS boundary patterns so even a steered request stays fenced.
4. Review `submit_jcl` requests by a human before approving them in the client (most MCP clients support per-call approval prompts).

---

## How the intelligence tools work

**No LLM, no external AI API.** The "intelligence" layer is deterministic, rule-based analysis that runs inside the server so results are reproducible and auditable. The MCP client's LLM interprets the structured report.

**`analyze_job_failure` / `analyze_root_cause`:**
- Extracts the abend code from the job's return code and spool text (recognizes `ABEND Sxxx`/`Uxxxx`, system/user completion codes, bare tokens), then matches it against a **curated reference table** of common z/OS abend codes (S0C1, S0C4, S0C7, S806, S322, S222, U4038, …) with explanations and remediations.
- Identifies the failing/cancelled step and program from JES message patterns (IEF450I, IEF142I, IEF473I, CSV0nnnI, module-not-found).
- Correlates the failure with other failed jobs currently visible in the JES output queue within the look-back window (`lookbackHours`, default 72, max 720).
- **Confidence** means exactly: `HIGH` = abend code + failing step + spool evidence all found; `MEDIUM` = abend code or failing step found; `LOW` = neither — treat the report as a starting point.
- **Health score** (0–100) = 100 − 8 × similar incidents (max −40) − 5 × correlated abend types (max −30), with a confidence-based floor. It is a trend indicator, not an SLA metric.

**`predict_batch_failures`:**
- Groups recent failed jobs by job name and scores each with a **recency-weighted count**: every failure contributes 3 if it happened in the newest quarter of the window, 2 in the newest half, 1 otherwise; +10 when a known abend code is involved; scaled and capped at 100.
- Risk levels: `CRITICAL ≥ 75`, `HIGH ≥ 50`, `MEDIUM ≥ 25`, else `LOW`.
- **Trend** compares failure counts in the newest vs oldest third of the window (INCREASING / STABLE / DECREASING).

**Known limitation (important for reviewers):** JES only exposes jobs still in the system. "History" means *failures currently listed in the OUTPUT queue* (capped at `MAX_FAILED_JOB_RESULTS`), not a query of SMF archives. For long-horizon analytics, feed SMF data into a real analytics platform — this tool answers "what is failing *right now*, repeatedly?"

---

## Incident management & runbooks

Bridge investigations into your ITSM flow. Configure **one** provider and
`create_incident_ticket` registers automatically; without configuration the
server behaves exactly as before.

```jsonc
// ServiceNow — Table API with basic auth
"INCIDENT_PROVIDER": "servicenow",
"INCIDENT_BASE_URL": "https://acme.service-now.com",
"INCIDENT_USER": "svc-mcp",
"INCIDENT_TOKEN": "<password or OAuth token>",
"INCIDENT_SERVICENOW_TABLE": "incident",          // default
"INCIDENT_SERVICENOW_CALLER_ID": "<sys_id>",       // optional

// Jira — REST API v3, basic auth with account email + API token
"INCIDENT_PROVIDER": "jira",
"INCIDENT_BASE_URL": "https://acme.atlassian.net",
"INCIDENT_USER": "mcp-bot@acme.com",
"INCIDENT_TOKEN": "<API token>",
"INCIDENT_JIRA_PROJECT": "OPS",
"INCIDENT_JIRA_ISSUE_TYPE": "Bug",                 // default

// PagerDuty — Events API v2 (routing-key auth, dedup per job)
"INCIDENT_PROVIDER": "pagerduty",
"INCIDENT_PAGERDUTY_ROUTING_KEY": "<routing key>"
```

Shared settings: `INCIDENT_SEVERITY` (default `error`), `INCIDENT_LABELS`
(comma-separated; applied to ServiceNow comments and Jira labels), and
`INCIDENT_RUNBOOKS` — a JSON map linking abend codes to your own runbooks:

```json
"INCIDENT_RUNBOOKS": "{\"S0C7\": \"https://wiki.acme.com/runbooks/s0c7\", \"S2*\": \"https://wiki.acme.com/runbooks/jes-timeouts\", \"U*\": \"https://wiki.acme.com/runbooks/user-abends\"}"
```

Exact codes win over patterns; matches attach to `lookup_abend_code` output,
`investigate_incident` bundles, and every created ticket. Creating a ticket is
a **governed write**: blocked by `SECURITY_READ_ONLY`, subject to policy RBAC,
and audited like every other tool call. The ticket embeds the full incident
bundle — failure analysis, abend reference, runbook link, internal-doc
excerpts, spool inventory, and diagnostic excerpts.

### RAG over internal documentation

Point `DOCS_SOURCES` at comma-separated local `.md`/`.txt` files or
directories (walked recursively; `node_modules` and dot-directories are
skipped):

```
"DOCS_SOURCES": "/opt/mcp-docs/runbooks,/opt/mcp-docs/playbooks",
"DOCS_MAX_RESULTS": "3",        // excerpts per search / bundle
"DOCS_MAX_EXCERPT_CHARS": "1200",
"DOCS_REFRESH_SECONDS": "300"   // re-index TTL (0 = index once at startup)
```

Retrieval is local TF-IDF — **zero dependencies, no external embedding API,
and document contents never leave the host**. Excerpts (with file and heading
attribution) attach automatically to incident bundles and abend lookups next
to the built-in reference, and `search_internal_docs` queries them directly.

---

## Quick start

**Requirements:** Node.js 18+, a reachable z/OSMF endpoint, and valid z/OS credentials (see [z/OS prerequisites](#zos-prerequisites)).

### Install via npm

```bash
npx -y @notharshhaa/mainframe-mcp-server
```

When you add this server in your IDE (Cursor, VS Code, Claude Desktop, etc.), the
installer prompts for connection settings (driven by `server.json`). Those values
are stored in your MCP config file — not in a `.env` file.

Example MCP config — token auth, the recommended setup. Source the token from your vault or the IDE's secret storage rather than hardcoding it (see also [config/mcp-client.example.json](./config/mcp-client.example.json)):

```json
{
  "mcpServers": {
    "mainframe": {
      "command": "npx",
      "args": ["-y", "@notharshhaa/mainframe-mcp-server"],
      "env": {
        "ZOSMF_HOST": "your-zosmf-host.internal",
        "ZOSMF_PORT": "443",
        "ZOSMF_TOKEN": "your-zosmf-token",
        "ZOSMF_TOKEN_TYPE": "LTPA2"
      }
    }
  }
}
```

Basic auth (`ZOSMF_USER` + `ZOSMF_PASSWORD`) is also supported — if you use it, inject the values from a vault or secret manager instead of pasting them into the config file.

### Run from source

```bash
git clone https://github.com/NotHarshhaa/mainframe-mcp-server.git
cd mainframe-mcp-server
npm install
npm run build
```

Point your IDE at `dist/index.js` and set `env` in MCP config (copy from
`config/mcp-client.example.json`), or export variables in your shell before `npm start`.

### Environment variables

| Variable | Required | Description |
|---|---|---|
| `ZOSMF_HOST` | yes | z/OSMF hostname |
| `ZOSMF_TOKEN` | yes* | Token auth — recommended over basic credentials |
| `ZOSMF_TOKEN_TYPE` | no | Token type (default `LTPA2`) |
| `ZOSMF_USER` / `ZOSMF_PASSWORD` | yes* | Basic auth — alternative to token auth |
| `ZOSMF_PORT` | no | HTTPS port (default `443`) |
| `ZOSMF_REJECT_UNAUTHORIZED` | no | TLS verification (default `true`) |

\* Provide either a token **or** user/password.

Full configuration reference: [docs/configuration.md](./docs/configuration.md)

### Tested clients

Any MCP client that launches **stdio** servers works. Known-good setups:

| Client | Config location |
|---|---|
| Claude Desktop | `claude_desktop_config.json` → `mcpServers` |
| Cursor | `~/.cursor/mcp.json` (or project `.cursor/mcp.json`) |
| VS Code (Copilot / MCP) | user or workspace MCP settings |
| Claude Code / other CLIs | standard `mcpServers` block |

---

## Transports

Two MCP transports are supported, selected with `MCP_TRANSPORT`:

| Transport | How to enable | Use when |
|---|---|---|
| **stdio** (default) | Omit `MCP_TRANSPORT` | IDE / desktop MCP clients (Cursor, VS Code, Claude Desktop). Logs go to stderr only. |
| **HTTP+SSE** | `MCP_TRANSPORT=sse` | Containerized or shared deployments. Endpoints: `GET /sse` (open stream), `POST /messages?sessionId=…`, `GET /health`. Place it behind an auth proxy if exposed beyond localhost. |

> **Streamable HTTP is not supported yet.** The MCP spec has moved from HTTP+SSE to
> Streamable HTTP, and this server still implements the legacy HTTP+SSE transport.
> Clients that only speak Streamable HTTP should launch the server over stdio (or front
> it with a transport shim). See [docs/deployment.md](./docs/deployment.md).

---

## Limits & timeouts

Output is **truncated, never paginated** — every cap emits an explicit `[truncated N of M lines]` notice plus guidance to narrow filters.

| Limit | Env variable | Default | Applies to |
|---|---|---|---|
| Spool DD lines | `MAX_JOB_OUTPUT_LINES` | 5000 | One spool DD (`get_job_output`, JCL) |
| File lines | `MAX_DATASET_READ_LINES` | 2000 | One dataset / PDS member / USS file read |
| Diagnostic DDs | `MAX_JES_SPOOL_FILES` | 20 | Spool files fetched per failure analysis |
| Audit lines | `MAX_AUDIT_LINES` | 500 | RACF audit text parsed per query |
| Job list results | `MAX_JOB_LIST_RESULTS` | 500 | `list_jobs` before agent-side filters |
| Failed-job results | `MAX_FAILED_JOB_RESULTS` | 100 | `find_failed_jobs`, intelligence correlation |
| JCL payload | `SECURITY_MAX_JCL_BYTES` | 65536 | `submit_jcl` input size |

**Timeouts & concurrency:**

- `submit_jcl wait=true` polls JES every 2 s for up to `timeout` seconds (default 30, max 300).
- REST calls use the Zowe SDK / Node defaults — there is no client-side retry loop; transient failures surface as `[CONNECTION_ERROR]` results the agent can retry.
- One shared z/OSMF session; requests are executed as the MCP client dispatches them, with no client-side rate limiter. z/OSMF's own server-side concurrency limits apply — heavy parallel tool use should be throttled by the client or the gateway.

---

## Troubleshooting & FAQ

All tool errors come back as `[ERROR_CODE]: message` — codes are stable and listed in [docs/tools-reference.md](./docs/tools-reference.md).

**`[CONNECTION_ERROR]` — z/OSMF authentication failed (401/403)**
Credentials rejected by z/OSMF. Check: user/password or token validity; `ZOSMF_TOKEN_TYPE` matches how the token was issued (`LTPA2` vs JWT); the account is not revoked. An **expired TSO password** also surfaces here — log on via TSO once to reset it, then retry.

**`[CONNECTION_ERROR]` — certificate / TLS errors**
Self-signed or private-CA z/OSMF certificate. Preferred fix: trust the CA chain, e.g. `NODE_EXTRA_CA_CERTS=/path/to/ca.pem`. For throwaway dev only: `ZOSMF_REJECT_UNAUTHORIZED=false` (logs a warning; never in production).

**`[CONNECTION_ERROR]` — unable to reach z/OSMF (ENOTFOUND / ECONNREFUSED / ETIMEDOUT)**
Host/port wrong, VPN not connected, or you are behind an API mediation layer — set `ZOSMF_BASE_PATH` to your gateway prefix.

**`[CONFIG_ERROR]` — CICS/Db2/RACF tools refuse to run**
Those tools need optional setup: `CMCI_CONTEXT`, `DB2_LOCATION`, or `RACF_AUDIT_USS_PATH`/`RACF_AUDIT_DATASET`. See [z/OS prerequisites](#zos-prerequisites). The error message names the exact variable.

**`[NOT_FOUND]` — job/dataset not found**
Job ids are JES-qualified (`JOB01234`, `STC00012`, `TSU00123`) and jobs vanish when purged from the OUTPUT queue. For datasets, check the HLQ and that the name is cataloged (migrated/HSM datasets may still resolve but show `MIGRATED`).

**`[FORBIDDEN]` — tool blocked or write rejected**
Read-only mode (the default) blocks `submit_jcl` and `approve_write`; an allowlist/blocklist rule matched; the pre-flight scan found a destructive construct; the change ticket was missing or malformed; or the submission is outside the change window. The error message names the specific gate — each one is an operator decision (env variables), not something the agent can turn off.

**The client doesn't show any tools**
Check stderr for the startup log (`mainframe-mcp-server connected over stdio`). Invalid config aborts with a variable-by-variable message. Set `LOG_LEVEL=debug` for per-call tracing. Requires Node 18+.

**HTTP+SSE returns `400 Unknown or missing sessionId`**
The SSE session was restarted; reconnect `GET /sse` and use the fresh `sessionId`.

---

## Examples

**Why did a job fail?**

```
User:  Why did PAYJOB01 fail?
Agent: analyze_job_failure({ jobId: "JOB01234" })
```

```
Job:     PAYJOB01 (JOB01234)
Status:  ABEND S806
Step:    STEP030 - LOADPGM
Reason:  Program load failure — module PAYRPTX not found
Fix:     Verify PAYRPTX is compiled and linked into the correct load library.
```

**Failed jobs in the last 24 hours**

```
User:  Show me all failed jobs from the last 24 hours
Agent: find_failed_jobs({ hours: 24 })
```

**Read a dataset member**

```
User:  Show me SYS1.PROCLIB(IKJEFT01)
Agent: read_dataset({ dsn: "SYS1.PROCLIB", member: "IKJEFT01" })
```

**What does abend S0C7 mean?**

```
User:  What is abend S0C7?
Agent: lookup_abend_code({ code: "S0C7" })
```

**Predict which batch jobs might fail next**

```
User:  Which batch jobs are at risk of failing?
Agent: predict_batch_failures({ hours: 168 })
```

---

## Project & community

- **[Website (GitHub Pages)](https://notharshhaa.github.io/mainframe-mcp-server/)** — overview, tools, setup, enterprise guide
- [Configuration](./docs/configuration.md) — all environment variables
- [Enterprise features](./docs/enterprise.md) — multi-environment, policy-as-code & RBAC, audit & SIEM, data protection
- [Tools reference](./docs/tools-reference.md) — inputs, outputs, and error codes
- [Deployment](./docs/deployment.md) — transports (stdio, HTTP+SSE), Docker, systemd
- [Contributing](./CONTRIBUTING.md) — workflow, project layout, how to add a tool
- [Security policy](./SECURITY.md) — supported versions and how to report vulnerabilities
- [Changelog](./CHANGELOG.md) — release history

---

## License

MIT — see [LICENSE](./LICENSE)

---

## Trademarks

Not affiliated with IBM or the Open Mainframe Project. IBM, z/OS, CICS, Db2 and RACF are trademarks of IBM.

TDQS

B3.3/5.0

Scored across 55 tools

Disambiguation3/5

Most tools target distinct resources or actions, but the failure/diagnosis cluster is crowded: find_failed_jobs, analyze_job_failure, investigate_incident, analyze_root_cause, summarize_abends, system_health_summary, and predict_batch_failures can be hard to choose between without careful reading. Similar overlap exists between list_datasets and search_dataset, and between list_uss_directory and search_uss_files, though descriptions generally clarify intent.

Naming Consistency4/5

The set is predominantly snake_case with a verb-first pattern such as get_job_status, list_jobs, read_dataset, and submit_jcl. There are readable deviations including resource-first or noun-first names like cics_newcopy, cics_disable_transaction, mq_queue_status, scheduler_job_status, system_health_summary, and whoami, but overall the convention is mostly predictable.

Tool Count2/5

At 55 tools, the surface is very large and imposes a significant selection burden on an agent even for a broad mainframe operations domain. While many tools are purpose-specific, the count goes well beyond the well-scoped 3-15 range and includes many niche diagnostics that could be consolidated.

Completeness3/5

The server has broad read coverage across JES, datasets, USS, CICS, Db2, RACF, SMF, console, MQ, storage, scheduling, and capacity. However, lifecycle coverage is uneven: dataset and USS operations lack create/update/delete, Db2 has catalog search but no query execution, and CICS has disable but no enable, creating notable gaps for a full mainframe operations surface.

Maintenance

ActivityMaintained
ResponsivenessNo issues