DEV Community

Cover image for AI, Hope, and Healing: Can We Build Our Own Personalized mRNA Cancer Vaccine Pipeline?
David Au Yeung
David Au Yeung

Posted on

AI, Hope, and Healing: Can We Build Our Own Personalized mRNA Cancer Vaccine Pipeline?

Introduction

When people talk about Artificial Intelligence today, the conversation often revolves around chatbots, image generators, or productivity tools. But a recent story caught my attention and made me rethink what AI might mean for the future of medicine.

An Australian tech entrepreneur, Paul Conyngham, used AI tools, including ChatGPT and Google DeepMind's AlphaFold, to explore a personalized mRNA cancer vaccine for his dog Rosie, who had been diagnosed with terminal cancer. Rosie's condition improved, and the story quickly spread across global media.

This case is not a scientific breakthrough, nor is it a reproducible medical treatment. But it symbolizes something important:

AI is lowering the barrier for ordinary people to understand complex biology.

As someone who has lost both my father and sister to cancer, this story hit close to home. It made me wonder - not whether AI can cure cancer today, but whether AI can help us learn, innovate, and accelerate the path toward better treatments tomorrow.

This article explores:

  • ✅ How tumor DNA sequencing leads to mutation discovery
  • ✅ How DNA mutations become protein mutations
  • ✅ Where AlphaFold fits (and where it does not)
  • ✅ How a simplified C# demo can mirror that real biological flow
  • ✅ Why safety, rigor, and validation always matter

Important: This project is for learning and inspiration only. It is not a medical tool and must not be used for diagnosis, treatment, or experimentation.

From Tumor DNA to Vaccine: The Real Scientific Workflow

To understand Rosie’s story, we need to understand how personalized cancer vaccines are actually developed.

Step 1 - Tumor DNA Sequencing

Cancer begins with DNA mutations.

In real oncology workflows:

  1. A tumor biopsy is taken.
  2. Tumor DNA is sequenced.
  3. The patient’s normal DNA is sequenced.
  4. Bioinformatics pipelines compare the two.
  5. Somatic mutations unique to the tumor are identified.

These mutations are changes in the DNA letters:

A, T, C, G
Enter fullscreen mode Exit fullscreen mode

Example DNA change:

Normal:  CGA
Tumor:   CAT
Enter fullscreen mode Exit fullscreen mode

That may change the encoded amino acid.

Step 2 - DNA Mutation → Protein Mutation

DNA encodes proteins using triplet codons.

Each 3‑base codon → 1 amino acid.

Example:

DNA: CGA → Arginine (R)
DNA: CAT → Histidine (H)
Enter fullscreen mode Exit fullscreen mode

Mutation result:

R175H
Enter fullscreen mode Exit fullscreen mode

Meaning:

  • Original amino acid: R
  • Position: 175
  • New amino acid: H

This is called a missense mutation.

These altered proteins may generate neoantigens - small peptides the immune system has never seen before.

That is the foundation of personalized cancer vaccines.

Step 3 - From Mutated Protein to Neoantigen

The immune system does NOT see full proteins.

It sees short fragments:

  • Usually 8-11 amino acids
  • Presented by HLA molecules

So the real pipeline becomes:

Tumor DNA → Protein mutation → Mutant peptide → HLA binding prediction → Vaccine candidate
Enter fullscreen mode Exit fullscreen mode

This requires:

  • Variant calling pipelines
  • Peptide extraction algorithms
  • HLA binding prediction models
  • Immunogenicity scoring
  • Wet‑lab validation
  • Clinical trials

AI assists parts of this process.

It does not replace biological validation.

Where AlphaFold Fits, and Where It Does Not

AlphaFold provides precomputed 3D structure predictions for wild‑type proteins.

These models are available through the official AlphaFold Protein Structure Database, jointly operated by Google DeepMind and EMBL‑EBI.

AlphaFold does not:

  • Identify mutations
  • Predict immune response
  • Predict neoantigen binding
  • Design vaccines
  • Predict mutated protein structures
  • Replace clinical validation

However, AlphaFold can help provide structural context:

  • Is the mutated residue surface‑exposed?
  • Is it inside a functional domain?
  • Is it near an active site?

This is context, not immunogenic proof.

How Our Educational C# Demo Mirrors the Real Pipeline

We simulate the workflow safely:

Real World Demo Equivalent
Tumor sequencing Synthetic mutation injection
Variant calling Protein comparison
Missense mutation Amino acid change string
Neoantigen extraction 9‑mer peptide builder
mRNA construct Codon assembly

This preserves the shape of the real scientific pipeline without claiming medical validity.

Full Educational C# Example

DNA → Protein → Mutation Detection → Neoantigen → mRNA Construct

This demo:

  • Fetches a wild-type protein sequence from the AlphaFold API (with a safe fallback sequence if the API is unavailable).
  • Reverse-translates the protein into a synthetic coding DNA sequence.
  • Introduces a synthetic tumor-like point mutation in DNA.
  • Translates mutated DNA back into protein.
  • Detects amino acid changes (missense-style mutation strings, e.g., A11T).
  • Extracts 9-mer mutant peptide windows as neoantigen candidates.
  • Assembles those peptide candidates into a simplified educational mRNA sequence.

The AlphaFold API retrieves precomputed wild‑type models from the official database.

It does not run new structure predictions.

✅Program.cs

using MyPlaygroundApp.Utils;

class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine("DNA -> Protein Mutation Detection Demo");
        Console.WriteLine("----------------------------------------");
        Console.WriteLine();

        string accession = "Q5VSL9";

        using var client = new AlphaFoldClient();
        var alphaEntry = await client.GetFirstPredictionAsync(accession);

        if (alphaEntry == null || string.IsNullOrWhiteSpace(alphaEntry.Sequence))
        {
            Console.WriteLine("AlphaFold fetch failed. Using synthetic fallback sequence.");
            alphaEntry = new AlphaFoldPrediction
            {
                Gene = accession,
                Sequence = "MKWVTFISLLFLFSSAYSRGVFRRDAHKSEVAHRFKDLGE"
            };
        }

        Console.WriteLine($"Gene: {alphaEntry.Gene}");
        Console.WriteLine($"Protein Length: {alphaEntry.Sequence.Length}");
        Console.WriteLine();

        string referenceProtein = alphaEntry.Sequence;
        string referenceDna = DnaUtils.ReverseTranslateProtein(referenceProtein);

        Console.WriteLine("Generated dummy coding DNA.");
        Console.WriteLine($"DNA length: {referenceDna.Length}");
        Console.WriteLine();

        string mutatedDna = DnaUtils.IntroducePointMutation(referenceDna, codonIndex: 10);

        Console.WriteLine("Introduced synthetic somatic mutation.");
        Console.WriteLine();

        string mutatedProtein = DnaUtils.TranslateDna(mutatedDna);
        var mutations = MutationDetector.FindMutations(referenceProtein, mutatedProtein);

        var neoantigens = mutations
            .Select((m, index) =>
            {
                var positionDigits = new string(m.Where(char.IsDigit).ToArray());
                var position = int.TryParse(positionDigits, out var parsed) ? parsed : 1;
                var mutationIndex = Math.Max(0, Math.Min(mutatedProtein.Length - 1, position - 1));
                var start = Math.Max(0, mutationIndex - 4);
                var length = Math.Min(9, mutatedProtein.Length - start);
                var peptide = mutatedProtein.Substring(start, length).PadRight(9, 'A');

                return new NeoantigenSummary
                {
                    Rank = index + 1,
                    Gene = alphaEntry.Gene,
                    SourceMutation = m,
                    MutantPeptideSequence = peptide,
                    CompositeScore = 0.9 - (index * 0.05)
                };
            })
            .ToList();

        var codons = new Dictionary<char, string>
        {
            ['A'] = "GCC",
            ['C'] = "UGC",
            ['D'] = "GAC",
            ['E'] = "GAG",
            ['F'] = "UUC",
            ['G'] = "GGC",
            ['H'] = "CAC",
            ['I'] = "AUC",
            ['K'] = "AAG",
            ['L'] = "CUG",
            ['M'] = "AUG",
            ['N'] = "AAC",
            ['P'] = "CCC",
            ['Q'] = "CAG",
            ['R'] = "CGG",
            ['S'] = "AGC",
            ['T'] = "ACC",
            ['V'] = "GUG",
            ['W'] = "UGG",
            ['Y'] = "UAC"
        };

        var mrnaBuilder = new System.Text.StringBuilder();
        foreach (var peptide in neoantigens.Select(n => n.MutantPeptideSequence))
        {
            foreach (var aa in peptide)
            {
                mrnaBuilder.Append(codons.GetValueOrDefault(aa, "NNN"));
            }
        }

        var mrnaSequence = mrnaBuilder.Length > 0 ? mrnaBuilder.ToString() : "AUG";

        Console.WriteLine("Detected Amino Acid Mutations:");
        foreach (var m in mutations)
        {
            Console.WriteLine($" - {m}");
        }

        Console.WriteLine();
        Console.WriteLine("Neoantigens:");
        foreach (var neo in neoantigens)
        {
            Console.WriteLine($" - #{neo.Rank} {neo.MutantPeptideSequence} ({neo.SourceMutation})");
        }

        Console.WriteLine();
        Console.WriteLine($"mRNA Sequence: {mrnaSequence}");

        Console.WriteLine();
        Console.WriteLine("Demo complete.");
    }
}
Enter fullscreen mode Exit fullscreen mode

✅ AlphaFold Client

public sealed class AlphaFoldClient : IDisposable
{
    private readonly HttpClient _http;
    private static readonly Uri PrimaryBaseAddress = new("https://alphafold.com/api/");
    private static readonly Uri SecondaryBaseAddress = new("https://alphafold.ebi.ac.uk/api/");

    public AlphaFoldClient(HttpClient? httpClient = null)
    {
        _http = httpClient ?? new HttpClient
        {
            BaseAddress = PrimaryBaseAddress
        };

        _http.Timeout = TimeSpan.FromSeconds(30);
        _http.DefaultRequestHeaders.Accept.ParseAdd("application/json");
        _http.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) MyPlaygroundApp/1.0");
    }

    public async Task<AlphaFoldPrediction?> GetFirstPredictionAsync(string accession, CancellationToken cancellationToken = default)
    {
        try
        {
            var encodedAccession = Uri.EscapeDataString(accession);
            var list = await TryGetPredictionListAsync(PrimaryBaseAddress, encodedAccession, cancellationToken)
                ?? await TryGetPredictionListAsync(SecondaryBaseAddress, encodedAccession, cancellationToken);

            if (list is null)
            {
                return null;
            }

            return list?.FirstOrDefault();
        }
        catch
        {
            return null;
        }
    }

    private async Task<List<AlphaFoldPrediction>?> TryGetPredictionListAsync(Uri baseAddress, string encodedAccession, CancellationToken cancellationToken)
    {
        try
        {
            _http.BaseAddress = baseAddress;
            using var response = await _http.GetAsync($"prediction/{encodedAccession}", cancellationToken);
            if (!response.IsSuccessStatusCode)
            {
                return null;
            }

            var json = await response.Content.ReadAsStringAsync(cancellationToken);
            return JsonSerializer.Deserialize<List<AlphaFoldPrediction>>(json,
                new JsonSerializerOptions
                {
                    PropertyNameCaseInsensitive = true
                });
        }
        catch
        {
            return null;
        }
    }

    public void Dispose()
    {
        _http.Dispose();
    }
}

public class AlphaFoldPrediction
{
    [JsonPropertyName("sequence")]
    public string Sequence { get; set; } = "";

    [JsonPropertyName("gene")]
    public string Gene { get; set; } = "";
}
Enter fullscreen mode Exit fullscreen mode

✅ DNA Utility (Translation + Mutation)

public static class DnaUtils
{
    // Non-technical explanation:
    // DNA is written with four letters (A, T, C, G). Cells read DNA in groups of 3 letters called "codons".
    // Each codon maps to one protein building block (an amino acid), represented here by a single letter.
    // Example: TTT -> F (Phenylalanine).
    // This table uses the standard genetic code and includes all codons (including stop codons marked as '*').
    private static readonly Dictionary<string, char> CodonTable = new()
    {
        ["TTT"] = 'F', ["TTC"] = 'F', ["TTA"] = 'L', ["TTG"] = 'L',
        ["TCT"] = 'S', ["TCC"] = 'S', ["TCA"] = 'S', ["TCG"] = 'S',
        ["TAT"] = 'Y', ["TAC"] = 'Y', ["TAA"] = '*', ["TAG"] = '*',
        ["TGT"] = 'C', ["TGC"] = 'C', ["TGA"] = '*', ["TGG"] = 'W',

        ["CTT"] = 'L', ["CTC"] = 'L', ["CTA"] = 'L', ["CTG"] = 'L',
        ["CCT"] = 'P', ["CCC"] = 'P', ["CCA"] = 'P', ["CCG"] = 'P',
        ["CAT"] = 'H', ["CAC"] = 'H', ["CAA"] = 'Q', ["CAG"] = 'Q',
        ["CGT"] = 'R', ["CGC"] = 'R', ["CGA"] = 'R', ["CGG"] = 'R',

        ["ATT"] = 'I', ["ATC"] = 'I', ["ATA"] = 'I', ["ATG"] = 'M',
        ["ACT"] = 'T', ["ACC"] = 'T', ["ACA"] = 'T', ["ACG"] = 'T',
        ["AAT"] = 'N', ["AAC"] = 'N', ["AAA"] = 'K', ["AAG"] = 'K',
        ["AGT"] = 'S', ["AGC"] = 'S', ["AGA"] = 'R', ["AGG"] = 'R',

        ["GTT"] = 'V', ["GTC"] = 'V', ["GTA"] = 'V', ["GTG"] = 'V',
        ["GCT"] = 'A', ["GCC"] = 'A', ["GCA"] = 'A', ["GCG"] = 'A',
        ["GAT"] = 'D', ["GAC"] = 'D', ["GAA"] = 'E', ["GAG"] = 'E',
        ["GGT"] = 'G', ["GGC"] = 'G', ["GGA"] = 'G', ["GGG"] = 'G'
    };

    private static readonly Dictionary<char, string> ReverseTable =
        CodonTable.GroupBy(x => x.Value)
                  .ToDictionary(g => g.Key, g => g.First().Key);

    // Reads DNA in 3-base codons and converts each recognized codon into one amino acid.
    public static string TranslateDna(string dna)
    {
        var protein = new List<char>();
        for (int i = 0; i + 2 < dna.Length; i += 3)
        {
            var codon = dna.Substring(i, 3);
            if (CodonTable.TryGetValue(codon, out char aa))
                protein.Add(aa);
        }
        return new string(protein.ToArray());
    }

    // Converts an amino-acid sequence into a synthetic coding DNA string using one representative codon per amino acid.
    public static string ReverseTranslateProtein(string protein)
    {
        return string.Concat(protein.Select(aa =>
            ReverseTable.ContainsKey(aa) ? ReverseTable[aa] : "GCT"));
    }

    // Simulates a single point mutation and prefers a non-synonymous change (amino acid actually changes).
    public static string IntroducePointMutation(string dna, int codonIndex)
    {
        var pos = codonIndex * 3;
        if (pos + 2 >= dna.Length)
        {
            return dna;
        }

        var originalCodon = dna.Substring(pos, 3);
        if (!CodonTable.TryGetValue(originalCodon, out var originalAminoAcid))
        {
            return dna;
        }

        var arr = dna.ToCharArray();
        var bases = new[] { 'A', 'T', 'C', 'G' };

        for (var offset = 0; offset < 3; offset++)
        {
            var index = pos + offset;
            var originalBase = arr[index];

            foreach (var candidateBase in bases)
            {
                if (candidateBase == originalBase)
                {
                    continue;
                }

                arr[index] = candidateBase;
                var mutatedCodon = new string(arr, pos, 3);

                if (!CodonTable.TryGetValue(mutatedCodon, out var mutatedAminoAcid))
                {
                    continue;
                }

                if (mutatedAminoAcid != originalAminoAcid && mutatedAminoAcid != '*')
                {
                    return new string(arr);
                }
            }

            arr[index] = originalBase;
        }

        return dna;
    }
}
Enter fullscreen mode Exit fullscreen mode

✅ Mutation Detector

public static class MutationDetector
{
    public static List<string> FindMutations(string reference, string mutated)
    {
        var mutations = new List<string>();
        int len = Math.Min(reference.Length, mutated.Length);

        for (int i = 0; i < len; i++)
        {
            if (reference[i] != mutated[i])
            {
                mutations.Add($"{reference[i]}{i + 1}{mutated[i]}");
            }
        }

        return mutations;
    }
}
Enter fullscreen mode Exit fullscreen mode

Example Output

This mirrors:

Tumor DNA mutation → missense change → neoantigen candidate → mRNA construct
Enter fullscreen mode Exit fullscreen mode

Safely and conceptually.

Why This Matters

Without explaining the mutation pipeline clearly, people might think:

"AI designs cancer vaccines."

It does not.

The real power lies in combining:

  • Sequencing
  • Variant calling
  • Structural biology
  • Immunology
  • AI-assisted analysis
  • Clinical validation

AI amplifies understanding.

It does not replace medicine.

Conclusion

AI is transforming how we think about biology, not by giving us instant cures, but by giving us tools to understand, explore, and innovate. Stories like Rosie’s remind us that hope often begins with curiosity, and that technology can empower people in ways we never imagined.

But technology alone is not enough. For AI‑enabled healthcare to truly benefit patients, we need thoughtful collaboration between researchers, clinicians, policymakers, and regulatory bodies. That means updating outdated frameworks, supporting responsible innovation, and building pathways that allow safe, evidence‑based personalized medicine to reach the people who need it.

Let's use AI not just to make life easier, but to make life better.

Reference:

Love C# & AI & Life!

Top comments (0)