<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Venkateshwar Rao Nagala</title>
    <description>The latest articles on DEV Community by Venkateshwar Rao Nagala (@venkateshwar_raonagala_4).</description>
    <link>https://dev.to/venkateshwar_raonagala_4</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3372699%2Fcae490c8-d62f-43b9-9b29-3bde9721e013.png</url>
      <title>DEV Community: Venkateshwar Rao Nagala</title>
      <link>https://dev.to/venkateshwar_raonagala_4</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/venkateshwar_raonagala_4"/>
    <language>en</language>
    <item>
      <title>How I Added Zero-Trust Guardrails to 4 MCP Servers Using AgentGateway — and Modernized Legacy COBOL Along the Way</title>
      <dc:creator>Venkateshwar Rao Nagala</dc:creator>
      <pubDate>Tue, 24 Feb 2026 23:56:17 +0000</pubDate>
      <link>https://dev.to/venkateshwar_raonagala_4/how-i-added-zero-trust-guardrails-to-4-mcp-servers-using-agentgateway-and-modernized-legacy-cobol-1fl8</link>
      <guid>https://dev.to/venkateshwar_raonagala_4/how-i-added-zero-trust-guardrails-to-4-mcp-servers-using-agentgateway-and-modernized-legacy-cobol-1fl8</guid>
      <description>&lt;h1&gt;
  
  
  How I Added Zero-Trust Guardrails to 4 MCP Servers Using AgentGateway — and Modernized Legacy COBOL Along the Way
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; Venkat Nagala&lt;br&gt;&lt;br&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/venkatnagala/Mainframe-Modernization" rel="noopener noreferrer"&gt;https://github.com/venkatnagala/Mainframe-Modernization&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Competition:&lt;/strong&gt; Solo.io SOLO AI Hackathon 2026&lt;/p&gt;


&lt;h2&gt;
  
  
  Demo Videos
&lt;/h2&gt;

&lt;p&gt;▶️ &lt;strong&gt;Watch the full pipeline in action:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quick Demo (2 min):&lt;/strong&gt; 
&lt;a href="https://www.youtube.com/watch?v=a7Yfz614d5Y" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=a7Yfz614d5Y&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detailed Walkthrough (9 min):&lt;/strong&gt; 
&lt;a href="https://www.youtube.com/watch?v=5s6MMIfxNf0" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=5s6MMIfxNf0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes Deployment Demo (3 min):&lt;/strong&gt; 
&lt;a href="https://youtu.be/05I-q2Ugw5Q" rel="noopener noreferrer"&gt;https://youtu.be/05I-q2Ugw5Q&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The demo shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All 7 services starting up on Kubernetes with a single command&lt;/li&gt;
&lt;li&gt;Full COBOL→Rust pipeline: &lt;code&gt;SUCCESS - Outputs match! ✅&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;AgentGateway RBAC security: &lt;code&gt;authorized: false&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Generated Rust code saved to AWS S3&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  My Story
&lt;/h2&gt;

&lt;p&gt;I started this project because I was frustrated.&lt;/p&gt;

&lt;p&gt;After 30 years working with enterprise systems — from Fortran to COBOL to Python — I watched organizations spend millions on mainframe modernization vendors only to get Java code so complex it needed another team to simplify it before humans could maintain it.&lt;/p&gt;

&lt;p&gt;I'm not a professional writer. I once spent $200/hour on GMAT verbal tutoring and still scored 640. But I know mainframes. I know Rust. And I know that 800 billion lines of COBOL aren't going to modernize themselves.&lt;/p&gt;

&lt;p&gt;I spent nights debugging Gemini API errors at 2 AM. I switched to Claude claude-opus-4-6 when the generated Rust kept failing to compile with errors like &lt;code&gt;.inv()&lt;/code&gt; method not found and invalid &lt;code&gt;RoundingStrategy&lt;/code&gt; variants. I fixed Docker port conflicts, Rust compiler errors, cargo permission issues, and AWS credential problems — one by one, night after night.&lt;/p&gt;

&lt;p&gt;And then the terminal finally showed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status : SUCCESS - Outputs match! ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That moment made every late night worth it.&lt;/p&gt;

&lt;p&gt;This is my story of building an open-source COBOL→Rust modernization pipeline — and accidentally building a zero-trust AI security layer along the way. And then deploying the whole thing on Kubernetes.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem That Started It All
&lt;/h2&gt;

&lt;p&gt;Anthropic just published a blog post titled "How AI helps break the cost barrier to COBOL modernization" — and IBM shares dropped 31% after Claude Code demonstrated COBOL modernization capability. This project was built in parallel, independently proving the same thesis with a complete open-source pipeline secured by AgentGateway.&lt;/p&gt;

&lt;p&gt;There are an estimated 800 billion lines of COBOL still running in production globally — powering banks, governments, and insurance companies. The workforce that understands this code is retiring at 50,000 developers per year.&lt;/p&gt;

&lt;p&gt;I have worked with these systems my entire career. I have seen the complexity firsthand. I have talked to people at modernization vendors and understood the real challenges. The world needs an open-source, automated, validated modernization pipeline — starting with COBOL, with Assembler (HLASM) support planned for Phase 4.&lt;/p&gt;

&lt;p&gt;What surprised me most was this: securing the AI agents doing the modernization turned out to be just as important as the modernization itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;A complete &lt;strong&gt;AI-powered COBOL→Rust modernization pipeline&lt;/strong&gt; with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;4 MCP servers&lt;/strong&gt; — S3, AI Translation (ai_mcp), COBOL, Rust&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AgentGateway&lt;/strong&gt; — zero-trust JWT authentication and RBAC for every MCP call&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2 agents&lt;/strong&gt; — Green Agent (Orchestrator) and Purple Agent (AI Modernizer)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude claude-opus-4-6&lt;/strong&gt; — for reliable, consistent Rust code generation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated validation&lt;/strong&gt; — outputs compared before any code is saved&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes deployment&lt;/strong&gt; — all 7 services orchestrated in production-grade infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The result that matters:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;task_id        : MODERN-DEMO-2026
status         : SUCCESS - Outputs match! ✅
match_confirmed: True
rust_code_url  : https://mainframe-refactor-lab-venkatnagala.s3.us-east-1.amazonaws.com/...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Security Problem I Didn't Expect
&lt;/h2&gt;

&lt;p&gt;When I started building, my agents talked directly to services. Green Agent called S3 directly. It worked — but one day I asked myself a simple question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;What happens if the Purple Agent (AI Modernizer) is compromised?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without guardrails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It could read all COBOL source from S3&lt;/li&gt;
&lt;li&gt;It could overwrite modernized Rust code with malicious output&lt;/li&gt;
&lt;li&gt;It could exfiltrate data to external services&lt;/li&gt;
&lt;li&gt;There would be no audit trail of what happened&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I had built a pipeline with no security boundaries. Any compromised agent had unlimited access to everything.&lt;/p&gt;

&lt;p&gt;That's when I discovered AgentGateway — and everything changed.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;AgentGateway&lt;/strong&gt; the Purple Agent is now physically blocked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"authorized"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Role Modernizer is not authorized to call fetch_source on s3_mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"audit_trail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"agent_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"purple_agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"authorized"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"request_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cf1d3191-4053-4b8e-b8a8-d4035023f92a"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Blast radius = limited to translation only.&lt;/strong&gt; The Purple Agent cannot read source, cannot write output, cannot execute code. This is the zero-trust principle applied to AI agents — and it works.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────────────────┐
│                     Kubernetes (mainframe-modernization)                    │
│                                                                             │
│  ┌──────────────┐     JWT/HTTPS      ┌─────────────────────────────────┐   │
│  │              │ ────────────────► │       Agent Gateway              │   │
│  │ Green Agent  │                   │   (AuthN + AuthZ + Audit)        │   │
│  │(Orchestrator)│ ◄──────────────── │   Port: 8090                    │   │
│  │  Port: 8080  │    Proxy Result   └──────────┬──────────────────────┘   │
│  └──────────────┘                              │ Authorized calls only      │
│                                                ▼                            │
│  ┌──────────────┐      ┌─────────────────────────────────────────────┐     │
│  │ Purple Agent │─────►│            MCP Servers                      │     │
│  │(AI Modernizer│      │  ┌──────┐  ┌──────────┐  ┌───────┐ ┌────┐  │     │
│  │  Port: 8081  │      │  │  S3  │  │AI Trans. │  │ COBOL │ │Rust│  │     │
│  └──────────────┘      │  │:8081 │  │  :8082   │  │ :8083 │ │:84 │  │     │
│                        └─────────────────────────────────────────────┘     │
│   NetworkPolicy: Default DENY ALL — whitelist only                          │
└─────────────────────────────────────────────────────────────────────────────┘
                                      │
                               ┌──────┴──────┐
                               │   AWS S3    │
                               │  programs/  │
                               │  data/      │
                               │  modernized/│
                               └─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every agent-to-MCP call flows through AgentGateway. No exceptions. The NetworkPolicy enforces this at the network level — agents literally cannot reach MCP servers without going through the gateway.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the 4 MCP Servers
&lt;/h2&gt;

&lt;p&gt;Each MCP server is a focused Rust microservice built with Actix-web. I chose Rust for everything — not just the modernization target but the entire infrastructure. The compiler catches security issues at build time. Memory safety prevents credential leaks in the gateway. It was the right call.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. S3 MCP (Port 8081)
&lt;/h3&gt;

&lt;p&gt;Handles all AWS S3 operations — fetch COBOL source, save validated Rust output, generate pre-signed download URLs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fetch_source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;web&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AppState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;web&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FetchRequest&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nf"&gt;get_s3_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="py"&gt;.s3_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="py"&gt;.bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="py"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FetchResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="nn"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;InternalServerError&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FetchResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="nn"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. AI Translation MCP — ai_mcp (Port 8082)
&lt;/h3&gt;

&lt;p&gt;This is the heart of the pipeline. I started with Gemini 2.5 Pro — and spent many nights debugging generated Rust code that used non-existent methods like &lt;code&gt;.inv()&lt;/code&gt;, &lt;code&gt;.quantize()&lt;/code&gt;, and invalid &lt;code&gt;RoundingStrategy&lt;/code&gt; variants. Every run produced different errors.&lt;/p&gt;

&lt;p&gt;I switched to Claude claude-opus-4-6. First attempt — compiled and ran correctly. That was the end of the debate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CLAUDE_API_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://api.anthropic.com/v1/messages"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CLAUDE_MODEL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"claude-opus-4-6"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;call_claude&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;AppState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ClaudeRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CLAUDE_MODEL&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32768&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ClaudeMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"user"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="py"&gt;.http_client&lt;/span&gt;
        &lt;span class="nf"&gt;.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CLAUDE_API_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="py"&gt;.claude_api_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"anthropic-version"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2023-06-01"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"content-type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;.await&lt;/span&gt;
        &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Claude API request failed: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;claude_response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ClaudeResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="nf"&gt;.json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;
        &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to parse Claude response: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;claude_response&lt;/span&gt;&lt;span class="py"&gt;.content&lt;/span&gt;
        &lt;span class="nf"&gt;.into_iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.find&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="py"&gt;.content_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.and_then&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="py"&gt;.text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.ok_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Empty response from Claude"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. COBOL MCP (Port 8083)
&lt;/h3&gt;

&lt;p&gt;Compiles and executes COBOL using GnuCOBOL. Captures stdout for comparison with the Rust output. This is the ground truth — whatever COBOL produces, Rust must match exactly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;compile_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cobc"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.args&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;"-x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;binary_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;source_path&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nf"&gt;.output&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Rust MCP (Port 8084)
&lt;/h3&gt;

&lt;p&gt;Compiles and executes the Claude-generated Rust code using Cargo. One challenge I solved here was dynamic dependency injection — Claude sometimes generates code using &lt;code&gt;rust_decimal&lt;/code&gt;, &lt;code&gt;num-format&lt;/code&gt;, or &lt;code&gt;num-traits&lt;/code&gt;. The Rust MCP detects which crates are needed and injects them into &lt;code&gt;Cargo.toml&lt;/code&gt; automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Dynamic dependency injection based on generated code&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;deps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"rust_decimal = &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;1.34&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
     rust_decimal_macros = &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;1.34&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
     num-format = { version = &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;0.4&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;, features = [&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;with-system-locale&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;] }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
     num-traits = &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;0.2&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="py"&gt;.source&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"chrono"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;deps&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"chrono = &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;0.4&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="py"&gt;.source&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"regex"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;deps&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"regex = &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Adding Guardrails with AgentGateway
&lt;/h2&gt;

&lt;p&gt;Here is the complete flow that makes every MCP call secure:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Agent Authentication
&lt;/h3&gt;

&lt;p&gt;Each agent authenticates with an API key and receives a JWT token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/auth/token&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"green_agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"api_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requested_role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"orchestrator"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;Response:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJ..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"orchestrator"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: RBAC Enforcement
&lt;/h3&gt;

&lt;p&gt;Every MCP call is checked against the role table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Agent&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;S3 MCP&lt;/th&gt;
&lt;th&gt;ai_mcp&lt;/th&gt;
&lt;th&gt;COBOL MCP&lt;/th&gt;
&lt;th&gt;Rust MCP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Green Agent&lt;/td&gt;
&lt;td&gt;Orchestrator&lt;/td&gt;
&lt;td&gt;✅ All&lt;/td&gt;
&lt;td&gt;✅ All&lt;/td&gt;
&lt;td&gt;✅ All&lt;/td&gt;
&lt;td&gt;✅ All&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Purple Agent&lt;/td&gt;
&lt;td&gt;Modernizer&lt;/td&gt;
&lt;td&gt;❌ Blocked&lt;/td&gt;
&lt;td&gt;✅ Translate&lt;/td&gt;
&lt;td&gt;❌ Blocked&lt;/td&gt;
&lt;td&gt;❌ Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 3: Proxied MCP Call
&lt;/h3&gt;

&lt;p&gt;Authorized calls are forwarded to the MCP server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/mcp/invoke&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Authorization:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Bearer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;eyJ...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target_mcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3_mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fetch_source"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"payload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bucket"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-bucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"programs/interest_calc.cbl"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Audit Trail
&lt;/h3&gt;

&lt;p&gt;Every call — authorized or denied — is logged with a unique request ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-23T21:00:24Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"green_agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target_mcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3_mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fetch_source"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"authorized"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"request_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"c067e63e-7cda-41dd-a9d0-1aededa00c65"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Validation Pipeline
&lt;/h2&gt;

&lt;p&gt;The automated validation is what makes this production-ready. Only verified, functionally-equivalent Rust code gets saved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Fetch COBOL source from S3          (via S3 MCP)
2. Compile + execute COBOL             (via COBOL MCP)
   → Output: "CALCULATED INTEREST:     550.00"
3. Translate COBOL → Rust via Claude   (via ai_mcp)
4. Compile + execute Rust              (via Rust MCP)
   → Output: "CALCULATED INTEREST: 550.00"
5. Normalize + compare outputs
   → "calculated interest: 550.00" == "calculated interest: 550.00"
6. MATCH → save Rust code to S3        (via S3 MCP)
7. Return pre-signed download URL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the outputs don't match — the code is not saved. No human needed to check. The pipeline catches it automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Rust is the Right Target Language
&lt;/h2&gt;

&lt;p&gt;Claude claude-opus-4-6 generates idiomatic Rust that is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Clean and readable&lt;/strong&gt; — functions are concise and directly traceable to the original COBOL business logic&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Immediately maintainable&lt;/strong&gt; — any Rust developer can understand and modify the generated code without specialized mainframe knowledge&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Memory safe&lt;/strong&gt; — no garbage collector, no null pointer exceptions, no buffer overflows&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;High performance&lt;/strong&gt; — zero runtime overhead, sub-millisecond execution&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Automatically validated&lt;/strong&gt; — outputs compared against original COBOL before saving&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Production ready&lt;/strong&gt; — compiles with standard Cargo, no custom toolchain needed&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Serverless ready&lt;/strong&gt; — native support on all major serverless platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The generated Rust preserves the original business logic clarity while bringing it into a modern, safe, and performant language that enterprises can confidently maintain for the next 30 years.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technical Challenges I Overcame
&lt;/h2&gt;

&lt;p&gt;I want to be honest about the struggles — this was not a smooth journey:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Rust Compiler Errors&lt;/strong&gt;: &lt;code&gt;RwLock&amp;lt;Option&amp;lt;String&amp;gt;&amp;gt;&lt;/code&gt; doesn't implement Clone — spent hours before finding the fix&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker Port Conflicts&lt;/strong&gt;: Two services both wanted port 8081 — separated external (8086) vs internal (8081) ports&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS SDK Requirements&lt;/strong&gt;: &lt;code&gt;aws-smithy-runtime&lt;/code&gt; requires Rust 1.91+ — learned this the hard way&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Model Selection&lt;/strong&gt;: Gemini generated different broken code every run — switched to Claude claude-opus-4-6 which generates correct, compilable Rust consistently&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cargo Permissions&lt;/strong&gt;: Non-root mcpuser couldn't write to &lt;code&gt;/tmp/cargo_home&lt;/code&gt; — needed explicit &lt;code&gt;.cargo&lt;/code&gt; directory ownership&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Dependencies&lt;/strong&gt;: Rust MCP needed to inject crate dependencies automatically based on what Claude generated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Naming&lt;/strong&gt;: Renamed from &lt;code&gt;gemini_mcp&lt;/code&gt; to &lt;code&gt;ai_mcp&lt;/code&gt; — reflects actual function (AI translation) not the model name&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rust compiler messages&lt;/strong&gt; are detailed and beginner-friendly — while experienced Assembler programmers read storage dumps and PSW registers with ease, Rust's compiler guides developers who are new to low-level systems programming&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each of these took hours to debug. I'm documenting them here so you don't have to go through the same pain.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 3: Kubernetes Deployment — Taking It to Production
&lt;/h2&gt;

&lt;p&gt;After validating the pipeline on Docker Compose, the next challenge was clear: can this run on Kubernetes? For a product targeting enterprise banks and insurance companies, Docker Compose is a prototype. Kubernetes is a product.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Goal
&lt;/h3&gt;

&lt;p&gt;Deploy all 7 microservices on Kubernetes with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero-trust network policies (default DENY ALL)&lt;/li&gt;
&lt;li&gt;Health endpoints on every service&lt;/li&gt;
&lt;li&gt;HorizontalPodAutoscaler on Purple Agent (scales 1→5 replicas automatically)&lt;/li&gt;
&lt;li&gt;Single command deployment via &lt;code&gt;.\deploy.ps1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Automated demo and RBAC security test after deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Kubernetes Manifests
&lt;/h3&gt;

&lt;p&gt;I organized all manifests under &lt;code&gt;k8s/base/&lt;/code&gt; applied in strict order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00-namespace-rbac.yaml   — namespace + service accounts
01-secrets-config.yaml   — ConfigMap + secret templates
02-agent-gateway.yaml    — AgentGateway deployment + ClusterIP service
03-agents.yaml           — Green Agent + Purple Agent + HPA
04-network-policy.yaml   — zero-trust NetworkPolicy
05-mcp-servers.yaml      — all 4 MCP servers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The order matters. Agent Gateway must be running before agents start — enforced by an &lt;code&gt;initContainer&lt;/code&gt; on both Green Agent and Purple Agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;initContainers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wait-for-gateway&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;busybox:1.36&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sh'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-c'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;until&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;wget&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-qO-&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;http://agent-gateway:8090/health;&lt;/span&gt; 
    &lt;span class="s"&gt;do&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;echo&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Waiting&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;agent-gateway...";&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;sleep&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;3;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;done'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This guarantees agents never start before the security gateway is ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  Three Hard Problems I Hit on Kubernetes
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. GLIBC Mismatch — cobol-mcp and s3-mcp CrashLoopBackOff
&lt;/h4&gt;

&lt;p&gt;Both pods crashed immediately on Kubernetes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.38 not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Rust binaries were compiled against a newer GLIBC than &lt;code&gt;debian:bookworm-slim&lt;/code&gt; provided. The fix was straightforward — change the runtime stage in both Dockerfiles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Before&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; debian:bookworm-slim&lt;/span&gt;

&lt;span class="c"&gt;# After&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; rust:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pushed to GitHub, GitHub Actions rebuilt the images automatically, rolled out the new pods — fixed.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. JWT Token Expiry — 401 Unauthorized After 3 Hours
&lt;/h4&gt;

&lt;p&gt;Green Agent authenticates with Agent Gateway at startup and caches the JWT token. Works perfectly — until 3 hours later when the token expires and every MCP call returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Token decode failed: ExpiredSignature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix was adding auto-refresh logic in &lt;code&gt;invoke_mcp()&lt;/code&gt;. On a 401 response, Green Agent re-authenticates and retries the request transparently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="nf"&gt;.as_u16&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"🔄 JWT expired — refreshing token..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AGENT_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"AGENT_API_KEY not set"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Retry with new token...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the kind of issue that only surfaces under production-like conditions — Docker Compose never runs long enough to hit it.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Missing Health Endpoint — Purple Agent 34 Restarts
&lt;/h4&gt;

&lt;p&gt;Purple Agent was stuck in CrashLoopBackOff with 34 restarts. The logs showed it started successfully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🟣 Purple Agent (AI Modernizer) Online | Listening on 0.0.0.0:8081
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But Kubernetes kept killing it. The reason? The liveness probe was hitting &lt;code&gt;/health&lt;/code&gt; — a route that didn't exist in the code. Kubernetes saw timeouts, assumed the pod was unhealthy, and restarted it every 30 seconds.&lt;/p&gt;

&lt;p&gt;The fix was a single addition to the Axum router:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/solve"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handle_modernization&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/health"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;health&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;  &lt;span class="c1"&gt;// ← This one line stopped 34 restarts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lesson learned: every service needs a health endpoint — not optional on Kubernetes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Single Command Deployment
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;deploy.ps1&lt;/code&gt; handles everything end-to-end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Step 1:  Verify kubectl and cluster connection
Step 2:  Load environment variables from .env
Step 3:  Create namespace and RBAC
Step 4:  Create all Kubernetes secrets from .env
Step 5:  Apply ConfigMap
Step 6:  Deploy Agent Gateway — wait for readiness
Step 7:  Deploy MCP servers
Step 8:  Deploy Green Agent + Purple Agent
Step 9:  Apply zero-trust NetworkPolicy
Step 10: Wait for all 7 pods ready
Step 11: Show pod and service status
Step 12: Port-forward Green Agent (background)
Step 13: Run demo pipeline automatically
Step 14: Run RBAC security test
Step 15: Cleanup port-forwards
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No manual steps. No separate terminal windows. One command tells the complete story.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Final Result
&lt;/h3&gt;

&lt;p&gt;All 7 pods running with 0 restarts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                          READY   STATUS    RESTARTS
agent-gateway-xxx             1/1     Running   0
ai-mcp-xxx                    1/1     Running   0
cobol-mcp-xxx                 1/1     Running   0
green-agent-xxx               1/1     Running   0
purple-agent-xxx              1/1     Running   0
rust-mcp-xxx                  1/1     Running   0
s3-mcp-xxx                    1/1     Running   0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pipeline result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status=SUCCESS - Outputs match! ✅
match_confirmed=True
rust_code_url= [S3 presigned URL]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RBAC security test — automated in deploy.ps1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✅ RBAC ENFORCED — Purple Agent blocked from S3 as expected!
🛡️  Role 'Modernizer' is NOT authorized to call fetch_source on s3_mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Kubernetes Adds to the Story
&lt;/h3&gt;

&lt;p&gt;For a product targeting enterprise banks, Kubernetes is not optional:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HPA on Purple Agent&lt;/strong&gt; — scales 1→5 replicas under load, handles bursts of COBOL files automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-trust NetworkPolicy&lt;/strong&gt; — pods cannot talk to each other unless explicitly whitelisted, even inside the cluster&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rolling updates&lt;/strong&gt; — GitHub Actions rebuilds images on every push, Kubernetes rolls them out with zero downtime&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health probes&lt;/strong&gt; — Kubernetes self-heals pods that stop responding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Namespace isolation&lt;/strong&gt; — all 7 services live in &lt;code&gt;mainframe-modernization&lt;/code&gt;, isolated from other workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is what separates a hackathon project from a product. 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 — Clone Repository
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/venkatnagala/Mainframe-Modernization.git
&lt;span class="nb"&gt;cd &lt;/span&gt;Mainframe-Modernization
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2 — Configure Environment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# Edit .env and add:&lt;/span&gt;
&lt;span class="c"&gt;# CLAUDE_API_KEY        → get from console.anthropic.com ($5 minimum)&lt;/span&gt;
&lt;span class="c"&gt;# AWS_ACCESS_KEY_ID     → get from console.aws.amazon.com&lt;/span&gt;
&lt;span class="c"&gt;# AWS_SECRET_ACCESS_KEY&lt;/span&gt;
&lt;span class="c"&gt;# S3_BUCKET_NAME&lt;/span&gt;
&lt;span class="c"&gt;# JWT_SECRET            → minimum 32 characters&lt;/span&gt;
&lt;span class="c"&gt;# AGENT_API_KEY         → your agent API key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3 — Upload Sample COBOL to S3
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;legacy_source/interest_calc.cbl s3://YOUR_BUCKET/programs/interest_calc.cbl
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;data/loan_data.json s3://YOUR_BUCKET/data/loan_data.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4 — Enable Kubernetes in Docker Desktop
&lt;/h3&gt;

&lt;p&gt;Settings → Kubernetes → Enable Kubernetes → Apply &amp;amp; Restart&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 — Run Everything (One Command!)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\deploy.ps1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Deploys all 7 services on Kubernetes&lt;/li&gt;
&lt;li&gt;✅ Waits for all pods to be ready&lt;/li&gt;
&lt;li&gt;✅ Triggers the modernization pipeline&lt;/li&gt;
&lt;li&gt;✅ Runs the RBAC security test&lt;/li&gt;
&lt;li&gt;✅ Cleans up port-forwards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🚀 Mainframe Modernization Pipeline - Kubernetes Deployment
============================================================
...
🎉 DEPLOYMENT COMPLETE!

Sending Modernization Task to Green Agent...
Task accepted!
@{task_id=MODERN-DEMO-2026; status=SUCCESS - Outputs match! ✅; match_confirmed=True}

✅ RBAC ENFORCED — Purple Agent blocked from S3 as expected!
🛡️  Role 'Modernizer' is NOT authorized to call fetch_source on s3_mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The IBM Connection
&lt;/h2&gt;

&lt;p&gt;IBM shares dropped 31% recently — triggered by Claude Code's ability to modernize COBOL. This single market event confirms what this project demonstrates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;COBOL modernization is no longer a distant future — it is happening now&lt;/li&gt;
&lt;li&gt;Open-source, automated, validated pipelines are the answer&lt;/li&gt;
&lt;li&gt;Security of AI agents doing the modernization is critical&lt;/li&gt;
&lt;li&gt;Rust is the right target language — memory safe, performant, modern, serverless-ready&lt;/li&gt;
&lt;li&gt;Assembler (HLASM) modernization is the natural next step — planned for Phase 4&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This project is a working proof that the entire pipeline — fetch, compile, translate, validate, save — can be fully automated with zero-trust security, deployed on Kubernetes, and triggered with a single command.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Lessons Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;On MCP Security:&lt;/strong&gt;&lt;br&gt;
Don't let agents talk directly to MCP servers. I learned this the hard way. AgentGateway makes it straightforward to add JWT auth and RBAC without modifying your MCP servers at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On AI Model Selection:&lt;/strong&gt;&lt;br&gt;
I evaluated Gemini 2.5 Pro extensively. It generated different broken Rust code on every run. Claude claude-opus-4-6 generates correct, compilable Rust consistently — that consistency is critical for an automated pipeline with no human review step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On Rust for AI Infrastructure:&lt;/strong&gt;&lt;br&gt;
I chose Rust for the entire stack — not just the modernization target. The compiler catches security issues at build time. Memory safety prevents credential leaks in the gateway. Eight worker threads per service with sub-millisecond JWT validation. It was the right decision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On Kubernetes vs Docker Compose:&lt;/strong&gt;&lt;br&gt;
Docker Compose is for development. Kubernetes is for products. Real production issues — GLIBC mismatches, JWT token expiry, missing health endpoints — only surface when you deploy to Kubernetes. Every enterprise customer will ask: "Does it run on Kubernetes?" Now the answer is yes, with a single command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On MCP vs Agent Skills:&lt;/strong&gt;&lt;br&gt;
Lin Sun asked a great question about this on LinkedIn. My practical answer from building this pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP&lt;/strong&gt;: When the operation crosses a trust boundary (S3, compilers, external APIs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent Skills&lt;/strong&gt;: When the operation is deterministic and stateless within a trust boundary&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What's Next — Phase 4: Enterprise Features
&lt;/h2&gt;

&lt;p&gt;Kubernetes deployment is now complete ✅ — the pipeline runs in production-grade container orchestration with zero-trust security, HPA, and single-command deployment.&lt;/p&gt;

&lt;p&gt;The roadmap ahead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IBM HLASM Assembler → Rust&lt;/strong&gt; — architecture designed, implementation planned&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IBM z/OS COBOL compiler&lt;/strong&gt; integration (IBM Developer for z/OS Enterprise Edition)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;COBOL-CICS&lt;/strong&gt; — modernize transaction processing to modern web services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VSAM to modern databases&lt;/strong&gt; — ESDS→S3, KSDS→DynamoDB/RDS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch processing&lt;/strong&gt; — modernize entire COBOL codebases in one run&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes production hardening&lt;/strong&gt; — multi-node cluster, Ingress controller, TLS termination, persistent volumes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  About Me
&lt;/h2&gt;

&lt;p&gt;I am Venkat Nagala — 30+ years from Fortran to Rust.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GATE 1994 AIR 444&lt;/strong&gt; — Top 0.4% of India's engineering graduates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CMU-trained&lt;/strong&gt; in Big Data Analytics (INSOFE)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fortune 500&lt;/strong&gt; experience (AIG, RBC, Thrivent Financial)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Languages lived&lt;/strong&gt;: Fortran → COBOL → Python → Rust&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I started my career writing Fortran. I have written COBOL. I have worked alongside experienced Assembler programmers who read storage dumps and PSW registers as naturally as reading English. I have seen what happens to systems that last 60 years. I built this project because I believe the next 60 years of enterprise computing should be built on Rust — safe, fast, and maintainable.&lt;/p&gt;

&lt;p&gt;The late nights were worth it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with Rust. Secured with AgentGateway. Deployed on Kubernetes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Modernizing mainframes, one COBOL line at a time. Assembler support coming in Phase 4!&lt;/em&gt; 🚀&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/venkatnagala/Mainframe-Modernization" rel="noopener noreferrer"&gt;https://github.com/venkatnagala/Mainframe-Modernization&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo Videos:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quick Demo (2 min):&lt;/strong&gt; 
&lt;a href="https://www.youtube.com/watch?v=a7Yfz614d5Y" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=a7Yfz614d5Y&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detailed Walkthrough (9 min):&lt;/strong&gt; 
&lt;a href="https://www.youtube.com/watch?v=5s6MMIfxNf0" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=5s6MMIfxNf0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes Deployment (3 min):&lt;/strong&gt; 
&lt;a href="https://youtu.be/05I-q2Ugw5Q" rel="noopener noreferrer"&gt;https://youtu.be/05I-q2Ugw5Q&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rust</category>
      <category>mcp</category>
      <category>agentgateway</category>
      <category>kubernetes</category>
    </item>
  </channel>
</rss>
