Mainframe Modernization Toolkit
Deterministic COBOL and JCL navigation, impact analysis, migration evidence, and code-generation tools for Python modernization projects.
The package includes:
- A Python CLI with 18 deterministic analysis, campaign, and generation commands.
- A bundled VS Code extension and COBOL/JCL language server.
- GitHub Copilot Language Model Tools for dependency-aware agent workflows.
- A packaged migration skill and specialized VS Code agent.
The parsers and graph operations do not use an AI model. Given the same source and configuration, they produce the same result.
Quick start
1. Install the VS Code extension
Python 3.10 or newer is required. The VSIX contains the Python toolkit, so no pip package is needed for extension commands or language-model tools. Install from the Marketplace:
code --install-extension mainframe-migration-toolkit.mainframe-migration-toolkit
You can also download the VSIX from the GitHub release and install it manually:
code --install-extension mainframe-migration-toolkit-0.1.14.vsix
The extension tries mainframeMigration.pythonPath, workspace virtual
environments, active virtual/Conda environments, common platform locations,
then python3, python, or py. Python versions older than 3.10 are skipped.
Installed CLI and module launches remain fallbacks. A non-default
mainframeMigration.executablePath is exact-only and bypasses auto mode.
2. Optional terminal CLI
Install the PyPI package when you want mainframe-toolkit in a terminal or
the Claude Code MCP server. Normal VSIX commands do not require this separate
installation:
pipx install mainframe-modernization-toolkit
# or
python -m pip install mainframe-modernization-toolkit
mainframe-toolkit --version
The wheel still contains the matching VSIX for export-based installation:
mainframe-toolkit vsix export --output mainframe-migration-toolkit.vsix
mainframe-toolkit vsix verify mainframe-migration-toolkit.vsix
code --install-extension mainframe-migration-toolkit.vsix
3. Initialize a mainframe workspace
Use Mainframe Migration: Initialize Workspace from the Command Palette, or run the optional terminal CLI from the workspace containing COBOL, copybooks, and JCL:
mainframe-toolkit workspace init .
This adds, without overwriting existing files:
mainframe-migration.jsonand its schema.- The relational IR schema.
.github/skills/mainframe-jcl-migration/..github/agents/mainframe-jcl-migrator.agent.md.
For Claude Code, initialize its main-conversation skill and project MCP server, or install both client integrations:
mainframe-toolkit workspace init . --target claude
mainframe-toolkit workspace init . --target all
The initializer merges the mainframe-toolkit entry into .mcp.json while
preserving unrelated servers. The default target is vscode. Claude Code asks
you to approve project-scoped MCP servers on first use; verify the result with
claude mcp list or /mcp. The generated server command requires the
PyPI/pipx-installed mainframe-toolkit executable on PATH.
The Claude skill does not install a custom subagent. Migration runs in the main
conversation so tool calls remain visible; subagents require an explicit
parallel-agents request. If an older initialization left
.claude/agents/mainframe-jcl-migrator.md, review and remove that legacy file.
Edit mainframe-migration.json so its source libraries, extensions, encoding,
known external programs, and transport profiles match the workspace.
4. Verify the setup
mainframe-toolkit doctor --workspace .
mainframe-toolkit run migration_preflight -- . --jcl MYJOB --format json
mainframe-toolkit run migration_preflight -- . --jcl MYJOB --program MYPROG --scope program-only --format json
mainframe-toolkit run generate_program_capsule -- . --jcl MYJOB --program MYPROG --scope program-only --out-dir migration/MYJOB/capsules --format json
Preflight exits 0 when generation is unblocked and 2 when required source
or configuration is missing or ambiguous. Its JSON report includes detected
capabilities, a stable capabilityDigest, and capabilityResolutions that
show which user policy and adapter, if any, applies to each capability.
Unknown static COBOL CALL and JCL EXEC PGM targets are nonblocking
unknown-provenance EXTERNAL_PROGRAM_ADAPTER TODOs with explicit adapter
requirements; missing copybooks, missing PROCs, and duplicate/conflicting
sources remain BLOCK findings. Classify a verified target later through
knownExternalPrograms or
knownExternalUtilities.
Program scopes require both --program and --jcl; program-only keeps local
callees as boundary references, while program-with-dependencies includes the
transitive local static CALL closure. Supplying --program without --scope
selects the latter, so the packaged migration workflow always passes explicit
--scope program-only. It uses dependency closure only when the user requests
one cohesive downstream unit. For multi-program jobs, this writes one capsule
instead of sibling or downstream capsules, reducing generated artifact and
review size by an amount that depends on the job boundary.
Scoped JSON filters inventory, findings, and capabilities and emits a stable
scopeDigest; boundaryReferences preserve excluded local and unknown external
invocations for adapter review.
5. Use it in VS Code
The extension provides:
- F12 and hover for COBOL
CALL,COPY, data items, and JCLEXEC PGM=. - Context-aware copybook resolution.
- COBOL/JCL diagnostics, completion, and document symbols.
- Commands to reindex and inspect the dependency graph.
- Six deterministic Language Model Tools for Copilot agent mode.
Run Mainframe Migration: Reindex COBOL/JCL Workspace after changing source library configuration.
Invoke the packaged workflow with a workspace root, JCL boundary, and optional program selection:
/mainframe-jcl-migration /path/to/workspace MYJOB MYPROG migration/MYJOB
When no program is supplied, the workflow selects the sole local candidate or presents deterministic candidate/risk order and asks when multiple remain. Alternatively, select the Mainframe JCL Migrator custom agent.
How it works
The toolkit separates deterministic evidence collection from AI reasoning:
- The language server indexes COBOL programs, copybooks, JCL jobs, calls, includes, data declarations, and execution edges.
- Language Model Tools expose those indexed facts to Copilot.
- Python commands persist graphs, warnings, contracts, rules, SQL, readers, fixtures, scaffolds, and migration reports.
- The agent reasons over tool output instead of reconstructing dependencies from model memory.
Unresolved or unsafe constructs are never silently guessed. Tools emit
structured BLOCK, TODO, or informational findings for dynamic calls,
missing copybooks, ambiguous libraries, edited PIC clauses, ODO, REDEFINES,
transaction dialects, and unterminated SQL blocks.
Running packaged tools
Prefer the umbrella command:
mainframe-toolkit run dependency_graph -- . --format json
mainframe-toolkit run impact_analysis -- . --changed ACCTREC --format text
mainframe-toolkit run business_rule_extractor -- app/cbl/VALIDATE.cbl --format markdown
mainframe-toolkit run copybook_to_contract -- app/cpy/ACCTREC.cpy --config mainframe-migration.json --format json
mainframe-toolkit run generate_file_readers -- . --out-dir migration/data --format text
The separator -- ends arguments for mainframe-toolkit; everything after it
is passed to the selected tool.
The module form works when the console entry point is not on PATH:
python -m mainframe_modernization_toolkit run dependency_graph -- . --format json
Consumer workspaces do not need a scripts/ directory. Do not locate or run
Python files inside site-packages directly.
MCP server
Serve the seven deterministic tools over stdio with the official MCP Python SDK 2.x:
mainframe-toolkit mcp serve
The server exposes mainframe_preflight, mainframe_dependency_graph,
mainframe_get_callers, mainframe_resolve_copybook,
mainframe_impact_analysis, mainframe_run_migration_tool, and
mainframe_query_artifact. Full output is persisted under
.mainframe-toolkit/runs/<runId>/; responses stay bounded and carry artifact
paths and SHA-256 digests. The stdio process does not emit application logs on
stdout.
Supplying program to MCP preflight while leaving scope at its default uses
program-only for unit migration. Request program-with-dependencies
explicitly when the downstream local CALL closure is intended.
Shared workload runtime
Install provider extras in the migrated workload image:
python -m pip install "mainframe-modernization-toolkit[runtime]"
The shared runtime supplies Control-M process/exit-code handling, S3 object and
CSV access, PostgreSQL transactions, SQS explicit acknowledgement, strong
PySpark contracts, Glue Catalog reads, safe Iceberg writes, and stdout logging.
CSV uses ; by default and honors quoted delimiters; explicit sep or
delimiter options override the default. Provider imports are lazy, so the
base toolkit and bundled VSIX do not require these extras.
Typed Spark descriptors isolate transformations from S3, Glue Catalog, and Iceberg. Contracts enforce ordered fields, decimal precision/scale, and nullability; generated lineage and structured rejection columns prevent bad records from disappearing silently. Iceberg global overwrite is prohibited. AWS Glue imports and job lifecycle exist only in the generated AWS wrapper. Deployments must still configure connector JARs, IAM, Lake Formation, and any required Glue bookmark policy.
Incremental JCL campaigns
Avoid materializing very large job-level preflight JSON. Initialize one compact SQLite-backed campaign, then claim and complete program-only units:
mainframe-toolkit run migration_campaign -- init . --jcl MYJOB --max-workers 2 --parallel-agents --format json
mainframe-toolkit run migration_campaign -- next . --jcl MYJOB --worker agent-1 --format json
mainframe-toolkit run migration_campaign -- complete . --jcl MYJOB \
--program MYPROG --evidence migration/MYJOB/MYPROG/report.json --format json
The bounded manifest exposes counts and digests rather than full inventory.
The default is one visible worker; multiple workers require the explicit
--parallel-agents confirmation. SQLite claims coordinate approved parallel workers, blocked units release their slot,
and completion detects changed inventory or program scope. Content-addressed
artifact-register and artifacts commands let workers reuse shared contracts,
readers, and fixtures by subject identity and SHA-256 digest.
COBOL parse facts are cached separately under .mainframe-toolkit/cache/ using
path, source SHA-256, encoding, parse mode, and parser version. The cache stores
JSON rather than executable serialization, invalidates changed files, relocates
paths when a workspace is copied, and reparses malformed entries safely.
Tool catalog
| Tool | Purpose |
|---|---|
migration_preflight |
Validate configured inventory and migration blockers |
dependency_graph |
Build CALL, COPY, and EXEC dependency graphs |
impact_analysis |
Compute transitive upstream/downstream impact |
dead_code_finder |
Report unreferenced programs and copybooks with caveats |
sql_extractor |
Extract embedded SQL and host-variable evidence |
business_rule_extractor |
Extract reviewable IF/EVALUATE rules |
copybook_to_dataclass |
Generate Python models and optional DDL |
copybook_to_contract |
Build canonical physical and transport contracts |
generate_copybook_fixtures |
Generate deterministic ingestion-only fixtures |
generate_file_readers |
Generate binary-safe fixed-width readers |
cobol_to_python_skeleton |
Generate disposable traceability scaffolds |
jcl_flow_extractor |
Extract JCL steps, DDs, conditions, and flow |
migration_complexity_report |
Rank migration effort and risk |
characterization_test_scaffolder |
Scaffold golden-master harnesses |
generate_program_capsule |
Generate preflight-gated partial evidence capsules |
validate_relational_ir |
Validate reviewed relational IR |
ir_to_pyspark |
Compile executable relational IR to PySpark |
Each tool is also installed as an individual console entry point, but the umbrella command is the stable form used by the VS Code extension and agent.
Language Model Tools
The VS Code extension registers six deterministic tools:
| Tool | Example input |
|---|---|
mainframe_getDependencyGraph |
{"focus":"MYPROG","direction":"both"} |
mainframe_getCallers |
{"program":"MYPROG"} |
mainframe_resolveCopybook |
{"copybook":"CUSTOMER-RECORD"} |
mainframe_impactAnalysis |
{"names":["MYPROG","CUSTOMER-RECORD"]} |
mainframe_runMigrationScript |
{"script":"migration_preflight","args":[".","--jcl","MYJOB","--program","MYPROG","--scope","program-only","--format","json"],"responseMode":"summary"} |
mainframe_queryArtifact |
{"runId":"<runId>","operation":"filter","pointer":"/findings","field":"/classification","equals":"BLOCK","select":["/code","/message"]} |
mainframe_runMigrationScript invokes the pip-installed package. It does not
expect repository scripts in the user's project.
mainframe_queryArtifact performs deterministic, read-only JSON Pointer,
filter, page, keys, get, and summary queries over workspace or saved run
artifacts. Use a narrow query and pagination instead of grep, search, or reading
an entire JSON artifact into agent context.
Specify exactly one workspace-relative path or safe runId; runId defaults
to stdout. The pointer defaults to the document root, offset to 0, and limit
to 20 with a maximum of 100. Filters use exact primitive equality and allow
at most 20 projected select pointers. Inputs must be regular UTF-8 JSON files
no larger than 100 MiB; canonical real-path checks reject workspace and symlink
escapes. Follow nextOffset until it is null.
Agent responses, artifacts, and preflight cache
The runner defaults to responseMode: "summary", returning a compact envelope
with status, findings, counts, digests, continuationAllowed, and nextActions.
responseMode: "preview" adds bounded stdout/stderr excerpts. Complete stdout,
stderr, and result output is always spooled under
.mainframe-toolkit/runs/<runId> while the hard artifact limit is not exceeded.
Add .mainframe-toolkit/ to the consumer repository's .gitignore unless run
evidence is intentionally committed.
Identical migration_preflight arguments reuse a cached envelope until COBOL,
copybook, JCL/PROC, configuration, artifact fingerprints, or explicit reindex
invalidate it. Agents should obey continuationAllowed, perform the listed
nextActions, and consume the full artifact instead of rerunning because a
preview was truncated.
mainframeMigration.maxAgentResponseBytes limits the serialized response only.
mainframeMigration.maxArtifactBytes defaults to a hard 100 MiB combined
stdout/stderr cap. Deprecated mainframeMigration.maxOutputBytes remains a
preview compatibility setting and does not terminate the process.
Preflight returns environmentFacts from schema-backed configuration such as:
{
"environment": {
"cobolDialect": "Enterprise COBOL",
"compiler": {
"name": "IBM Enterprise COBOL",
"version": "6.4",
"options": ["RENT", "SSRANGE"]
},
"sourceFormat": "fixed",
"runtime": "z/OS batch",
"runtimeDependencies": {"DB2": "13"},
"testCommands": ["./run-characterization-tests.sh"]
}
}
Configured values are KNOWN. Missing values remain UNKNOWN; the toolkit
does not guess them or emit unrelated blockers. Packaged agent guidance allows
one targeted, capped search for each UNKNOWN, then requests user evidence.
Configuration
mainframe-migration.json controls:
- Ordered primary and fallback COBOL, copybook, and JCL libraries.
- Source extensions and encoding.
- Known external programs, utilities, and copybooks.
- Generated TODO syntax.
- Physical-to-transport record representations.
- Target strategies and adapters under
targetCapabilities.
Configuration is authoritative. The tools do not widen searches to guessed directories when configured resolution fails.
Capability discovery and target policy
Preflight uses two explicit stages. First, it deterministically discovers only
the capabilities evidenced by the selected JCL and its COBOL/files, then emits
the immutable capabilities inventory and capabilityDigest. Second, it applies
user-authored targetCapabilities policy; discovery never chooses a target
technology.
A concise policy can combine a class default, an exact discovered instance, and a selector:
{
"targetCapabilities": {
"schemaVersion": 1,
"defaults": {
"transform.sort_merge": {
"strategy": "pyspark",
"adapter": "builtin.pyspark_sort"
}
},
"instances": {
"storage.indexed_records:dataset:APP.ACCOUNTS": {
"strategy": "relational_table",
"adapter": "builtin.relational_keyed_store"
}
},
"selectors": [
{
"id": "daily-bulk-loads",
"capabilityClass": "load.bulk_records",
"priority": 20,
"match": {"job": "DAILY*"},
"policy": {
"strategy": "relational_bulk_load",
"adapter": "builtin.bulk_load"
}
}
]
}
}
Resolution precedence is exact instance, highest-priority matching selector,
capability-class default, then unresolved. Selector array order is irrelevant;
equal-priority selectors with different policies produce BLOCK instead of a
guess.
Discovery currently emits orchestration.batch, transform.sort_merge,
load.bulk_records, storage.indexed_records, storage.sequential_records,
storage.versioned_generation, database.relational, transaction.online,
messaging.queue, and operations.audit. For a selected JCL, COBOL SQL,
transaction, and queue evidence is limited to local programs in its transitive
CALL/literal dialect-link closure. Messaging requires an exact supported IBM MQ
CALL or CICS READQ/WRITEQ/DELETEQ; generic CALLs and generic CICS blocks do
not qualify. Audit discovery groups SYSOUT=*, SYSPRINT, and SYSOUT DDs per
job step. security.authorization is available in policy/schema menus but no
instance is emitted until explicit RACF/security command evidence is parsed.
Built-in adapter IDs are builtin.relational_keyed_store,
builtin.key_value_store, builtin.lakehouse_table,
builtin.fixed_width_storage, builtin.pyspark_sort, builtin.sql_sort,
builtin.bulk_load, and builtin.batch_orchestration. Preflight records their
declared guarantees in each resolution. Third-party adapters use the
mainframe_modernization_toolkit.adapters entry-point group and must pin
adapterVersion; unavailable, ambiguous, incompatible, or mismatched adapters
block.
Framework-only descriptors are also available as
builtin.relational_database_framework, builtin.online_transaction_framework,
builtin.messaging_queue_framework, builtin.audit_framework, and
builtin.authorization_framework. They validate strategy compatibility and
declare only configuration/source-traceability guarantees; they have no provider
implementation, so generationEligible remains false.
An unresolved policy, or a selected strategy without an adapter, emits a TODO
and leaves that capability ineligible for adapter-backed generation. Unaffected
analysis continues, and reviewed relational IR can preserve the missing target
decision as a deterministic code-local TODO using the configured prefix.
The extension invokes mainframe-toolkit by default. If VS Code cannot find the
entry point, set mainframeMigration.executablePath to its absolute path. Find
it with the Python environment where the package was installed:
python -c "import shutil; print(shutil.which('mainframe-toolkit'))"
Safety boundaries
- Generated skeletons and capsules are evidence, not complete translations.
- Synthetic fixtures validate ingestion and field boundaries only. They are not authoritative expected program outputs.
- Semantic equivalence requires outputs captured from the mainframe or another verified implementation.
- Physical binary layouts and text transport layouts are separate contracts.
- PySpark generation requires reviewed, executable relational IR; the compiler does not invent business semantics.
Troubleshooting
VSIX verification fails on Windows with version 0.1.8
Version 0.1.8 can report a false negative when Windows prevents the verifier from reopening its temporary VSIX file. Upgrade to 0.1.9 or newer and rerun the same verification command:
python -m pip install --upgrade mainframe-modernization-toolkit
mainframe-toolkit vsix verify
mainframe-toolkit: command not found
Use:
python -m mainframe_modernization_toolkit doctor --workspace .
Then configure the extension with the absolute entry-point path if needed.
The agent tries to run scripts/<tool>.py
Upgrade the package, reinstall its bundled VSIX, and replace packaged workspace customizations only after reviewing local changes:
python -m pip install --upgrade mainframe-modernization-toolkit
mainframe-toolkit vsix export --output mainframe-migration-toolkit.vsix
code --install-extension mainframe-migration-toolkit.vsix --force
mainframe-toolkit workspace init . --force
The VSIX and Python package versions differ
mainframe-toolkit --version
mainframe-toolkit vsix verify
This check applies only to the optional wheel-export route. Export the VSIX from the same Python environment where the package is installed.
License
Apache-2.0.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mainframe_modernization_toolkit-0.1.14.tar.gz.
File metadata
- Download URL: mainframe_modernization_toolkit-0.1.14.tar.gz
- Upload date:
- Size: 774.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
72d733eda0f44b3dc79adb08490ac71c5420012b21bdcc8fc5f8472cdddcbd7c
|
|
| MD5 |
b1683429898ebaed12ce76d12a532e8e
|
|
| BLAKE2b-256 |
ddadbf3217bee4432e323185e1fa316cdb4939d3ed5c00736d3e9458ab1d17eb
|
File details
Details for the file mainframe_modernization_toolkit-0.1.14-py3-none-any.whl.
File metadata
- Download URL: mainframe_modernization_toolkit-0.1.14-py3-none-any.whl
- Upload date:
- Size: 791.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
269683487b4a62e458fdcf9c271eaa460e56385deb9e6f769d87cc31aa596c04
|
|
| MD5 |
b51b4ca26fa0172fe9e7fec103d46717
|
|
| BLAKE2b-256 |
5cb6ab4c36c931b67d7a20cee35180ee6e440b1893583386168e0137ff86315c
|