Alterlab Cbioportal logo

Alterlab Cbioportal

Community
AlterLab-IEU
alterlab-cbioportal

Query cBioPortal via its keyless REST API for cancer genomics across TCGA, GENIE, MSK-IMPACT and hundreds of studies — somatic mutations, copy-number alterations (GISTIC), mRNA/protein expression, structural variants, and patient-level clinical/survival data. Use when asked how often a gene is mutated/amplified/deleted in a tumor type, to profile oncogenes or tumor suppressors across cancers (pan-cancer alteration frequency), to pull patient-level mutations joined to OS/clinical outcomes, or to validate a cancer target from cohort genomics. For germline variant pathogenicity use alterlab-clinvar; for mutational-signature (SBS) decomposition use alterlab-cosmic; for CRISPR/RNAi gene-dependency use alterlab-depmap; for aggregated target-disease evidence use alterlab-opentargets. Part of the AlterLab Academic Skills suite.

Overview

PublisherAlterLab-IEU
RepositoryAlterLab-Academic-Skills
Skill namealterlab-cbioportal
Stars
84
Forks
16
Bundled files
3
LicenseLGPL-3.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 3 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by AlterLab-IEU on GitHub. Read the source before you install it.

Installation

Install the Alterlab Cbioportal AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/AlterLab-IEU/AlterLab-Academic-Skills/tree/main/skills/databases/alterlab-cbioportal
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/AlterLab-IEU/AlterLab-Academic-Skills.git /tmp/AlterLab-Academic-Skills
mkdir -p .claude/skills
cp -r /tmp/AlterLab-Academic-Skills/skills/databases/alterlab-cbioportal .claude/skills/alterlab-cbioportal
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Alterlab Cbioportal in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Alterlab Cbioportal on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Alterlab Cbioportal is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

cBioPortal Database

Overview

cBioPortal for Cancer Genomics (https://www.cbioportal.org/) is an open-access resource for exploring, visualizing, and analyzing multidimensional cancer genomics data. It hosts data from The Cancer Genome Atlas (TCGA), AACR Project GENIE, MSK-IMPACT, and hundreds of other cancer studies — covering mutations, copy number alterations (CNA), structural variants, mRNA/protein expression, methylation, and clinical data for thousands of cancer samples.

Key resources:

When to Use This Skill

Use cBioPortal when:

  • Mutation landscape: What fraction of a cancer type has mutations in a specific gene?
  • Oncogene/TSG validation: Is a gene frequently mutated, amplified, or deleted in cancer?
  • Co-mutation patterns: Are mutations in gene A and gene B mutually exclusive or co-occurring?
  • Survival analysis: Do mutations in a gene associate with better or worse patient outcomes?
  • Alteration profiles: What types of alterations (missense, truncating, amplification, deletion) affect a gene?
  • Pan-cancer analysis: Compare alteration frequencies across cancer types
  • Clinical associations: Link genomic alterations to clinical variables (stage, grade, treatment response)
  • TCGA/GENIE exploration: Systematic access to TCGA and clinical sequencing datasets

Does NOT Trigger

ScenarioUse Instead
Germline variant pathogenicity / clinical significancealterlab-clinvar
COSMIC Cancer Gene Census, mutational signatures (SBS), curated somatic catalogalterlab-cosmic
CRISPR/RNAi gene-dependency (essentiality) in cancer cell linesalterlab-depmap
Aggregated target–disease association evidence and tractabilityalterlab-opentargets

Core Capabilities

1. cBioPortal REST API

Base URL: https://www.cbioportal.org/api

The API is RESTful, returns JSON, and requires no API key for public data.

python
import requests

BASE_URL = "https://www.cbioportal.org/api"
HEADERS = {"Accept": "application/json", "Content-Type": "application/json"}

def cbioportal_get(endpoint, params=None):
    url = f"{BASE_URL}/{endpoint}"
    response = requests.get(url, params=params, headers=HEADERS)
    response.raise_for_status()
    return response.json()

def cbioportal_post(endpoint, body):
    url = f"{BASE_URL}/{endpoint}"
    response = requests.post(url, json=body, headers=HEADERS)
    response.raise_for_status()
    return response.json()

2. Browse Studies

python
def get_all_studies():
    """List all available cancer studies.

    The public portal hosts 540+ studies (2026-09), so a pageSize of 500 would
    silently truncate the list; ask for more than you expect.
    """
    return cbioportal_get("studies", {"pageSize": 100000})

# Each study has:
# studyId: unique identifier (e.g., "brca_tcga")
# name: human-readable name
# description: dataset description
# cancerTypeId: cancer type abbreviation
# referenceGenome: hg19 or hg38
# allSampleCount: samples in the study
# (pass projection=DETAILED to also get pmid, citation, sequencedSampleCount, ...)

studies = get_all_studies()
print(f"Total studies: {len(studies)}")

# Common TCGA study IDs — each cancer has several versions:
#   *_tcga                     original TCGA Firehose Legacy (e.g. brca_tcga)
#   *_tcga_pan_can_atlas_2018  harmonized PanCancer Atlas (preferred for pan-cancer work)
#   *_tcga_gdc                 GDC re-processed data
# e.g. brca_tcga, luad_tcga, coadread_tcga, gbm_tcga, prad_tcga, skcm_tcga

# Filter for TCGA studies
tcga_studies = [s for s in studies if "tcga" in s["studyId"]]
print([s["studyId"] for s in tcga_studies[:10]])

3. Molecular Profiles

Each study has multiple molecular profiles (mutation, CNA, expression, etc.):

python
def get_molecular_profiles(study_id):
    """Get all molecular profiles for a study."""
    return cbioportal_get(f"studies/{study_id}/molecular-profiles")

profiles = get_molecular_profiles("brca_tcga")
for p in profiles:
    print(f"  {p['molecularProfileId']}: {p['name']} ({p['molecularAlterationType']})")

# Alteration types:
# MUTATION_EXTENDED — somatic mutations
# COPY_NUMBER_ALTERATION — CNA (GISTIC)
# MRNA_EXPRESSION — mRNA expression
# PROTEIN_LEVEL — RPPA protein expression
# STRUCTURAL_VARIANT — fusions/rearrangements

4. Mutation Data

python
def get_mutations(molecular_profile_id, entrez_gene_ids, sample_list_id=None):
    """Get mutations for specified genes in a molecular profile."""
    body = {
        "entrezGeneIds": entrez_gene_ids,
        "sampleListId": sample_list_id or molecular_profile_id.replace("_mutations", "_all")
    }
    return cbioportal_post(
        f"molecular-profiles/{molecular_profile_id}/mutations/fetch",
        body
    )

# BRCA1 Entrez ID is 672, TP53 is 7157, PTEN is 5728
mutations = get_mutations("brca_tcga_mutations", entrez_gene_ids=[7157])  # TP53

# Each mutation record contains:
# patientId, sampleId, entrezGeneId (the nested gene.hugoGeneSymbol only
#   appears with ?projection=DETAILED on the fetch URL)
# mutationType (Missense_Mutation, Nonsense_Mutation, Frame_Shift_Del, etc.)
# proteinChange (e.g., "R175H"), variantType
# ncbiBuild, chr, startPosition, endPosition, referenceAllele, variantAllele
# mutationStatus (Somatic/Germline)
# tumorAltCount, tumorRefCount (read counts — there is no VAF field; derive it)

import pandas as pd
df = pd.DataFrame(mutations)
df["vaf"] = df["tumorAltCount"] / (df["tumorAltCount"] + df["tumorRefCount"])
print(df[["patientId", "mutationType", "proteinChange", "vaf"]].head())
print(f"\nMutation types:\n{df['mutationType'].value_counts()}")

5. Copy Number Alteration Data

python
def get_cna(molecular_profile_id, entrez_gene_ids):
    """Get discrete CNA data (GISTIC: -2, -1, 0, 1, 2)."""
    body = {
        "entrezGeneIds": entrez_gene_ids,
        "sampleListId": molecular_profile_id.replace("_gistic", "_all").replace("_cna", "_all")
    }
    return cbioportal_post(
        f"molecular-profiles/{molecular_profile_id}/discrete-copy-number/fetch",
        body
    )

# GISTIC values:
# -2 = Deep deletion (homozygous loss)
# -1 = Shallow deletion (heterozygous loss)
#  0 = Diploid (neutral)
#  1 = Low-level gain
#  2 = High-level amplification

cna_data = get_cna("brca_tcga_gistic", entrez_gene_ids=[1956])  # EGFR
df_cna = pd.DataFrame(cna_data)
print(df_cna["value"].value_counts())

6. Alteration Frequency (OncoPrint-style)

python
def get_alteration_frequency(study_id, gene_symbols, alteration_types=None):
    """Compute alteration frequencies for genes across a cancer study."""
    import requests, pandas as pd

    # Denominator = samples profiled for mutations (cBioPortal's own convention).
    # 'all_cases_in_study' also counts unsequenced samples and deflates the
    # frequency (brca_tcga: 1,108 samples in study vs 982 sequenced).
    samples = requests.get(
        f"{BASE_URL}/studies/{study_id}/sample-lists",
        headers=HEADERS
    ).json()
    by_category = {s["category"]: s["sampleListId"] for s in samples}
    sample_list_id = (by_category.get("all_cases_with_mutation_data")
                      or by_category.get("all_cases_in_study"))
    total_samples = len(requests.get(
        f"{BASE_URL}/sample-lists/{sample_list_id}/sample-ids",
        headers=HEADERS
    ).json())

    # Get gene Entrez IDs. /genes/fetch takes a plain JSON array of identifiers
    # plus a geneIdType query param; symbols WITHOUT the param resolve to [].
    gene_data = requests.post(
        f"{BASE_URL}/genes/fetch",
        params={"geneIdType": "HUGO_GENE_SYMBOL"},
        json=gene_symbols,
        headers=HEADERS
    ).json()
    # Response order is not guaranteed; map by symbol.
    entrez_by_symbol = {g["hugoGeneSymbol"]: g["entrezGeneId"] for g in gene_data}
    entrez_ids = [entrez_by_symbol[g] for g in gene_symbols if g in entrez_by_symbol]

    # Get mutations
    mutation_profile = f"{study_id}_mutations"
    mutations = get_mutations(mutation_profile, entrez_ids, sample_list_id)

    freq = {}
    for g_symbol, e_id in entrez_by_symbol.items():
        # Count samples (not patients) so numerator and denominator match.
        mutated = len(set(m["sampleId"] for m in mutations if m["entrezGeneId"] == e_id))
        freq[g_symbol] = mutated / total_samples * 100

    return freq

# Example
freq = get_alteration_frequency("brca_tcga", ["TP53", "PIK3CA", "BRCA1", "BRCA2"])
for gene, pct in sorted(freq.items(), key=lambda x: -x[1]):
    print(f"  {gene}: {pct:.1f}%")

7. Clinical Data

The global /clinical-data/fetch endpoint is POST-only (a GET returns HTTP 405). The simplest path for one study is the per-study GET endpoint, which returns a list of {patientId, studyId, clinicalAttributeId, value} records:

python
def get_patient_clinical_data(study_id, attribute_ids):
    """Patient-level clinical data via the per-study GET endpoint.

    GET /studies/{studyId}/clinical-data?clinicalDataType=PATIENT&attributeId=...
    accepts a single attributeId, so we query each and concatenate.
    """
    records = []
    for attr in attribute_ids:
        records += cbioportal_get(
            f"studies/{study_id}/clinical-data",
            {"clinicalDataType": "PATIENT", "attributeId": attr, "pageSize": 100000},
        )
    return records

# Clinical attributes include:
# OS_STATUS, OS_MONTHS, DFS_STATUS, DFS_MONTHS (survival)
# AJCC_PATHOLOGIC_TUMOR_STAGE, GRADE, AGE, SEX, RACE
# Study-specific attributes vary — list them with get_clinical_attributes().
# GOTCHA: OS_STATUS / DFS_STATUS are encoded "1:DECEASED" / "0:LIVING"
# (event:label), not bare 0/1 — split on ":" before survival analysis.

def get_clinical_attributes(study_id):
    """List all available clinical attributes for a study."""
    return cbioportal_get(f"studies/{study_id}/clinical-attributes")

Query Workflows

Workflow 1: Gene Alteration Profile in a Cancer Type

python
import requests, pandas as pd

def alteration_profile(study_id, gene_symbol):
    """Full alteration profile for a gene in a cancer study."""

    # 1. Get gene Entrez ID (plain array body + geneIdType param)
    gene_info = requests.post(
        f"{BASE_URL}/genes/fetch",
        params={"geneIdType": "HUGO_GENE_SYMBOL"},
        json=[gene_symbol],
        headers=HEADERS
    ).json()[0]
    entrez_id = gene_info["entrezGeneId"]

    # 2. Get mutations
    mutations = get_mutations(f"{study_id}_mutations", [entrez_id])
    mut_df = pd.DataFrame(mutations) if mutations else pd.DataFrame()

    # 3. Get CNAs
    cna = get_cna(f"{study_id}_gistic", [entrez_id])
    cna_df = pd.DataFrame(cna) if cna else pd.DataFrame()

    # 4. Summary
    n_mut = len(set(mut_df["patientId"])) if not mut_df.empty else 0
    n_amp = len(cna_df[cna_df["value"] == 2]) if not cna_df.empty else 0
    n_del = len(cna_df[cna_df["value"] == -2]) if not cna_df.empty else 0

    return {"mutations": n_mut, "amplifications": n_amp, "deep_deletions": n_del}

result = alteration_profile("brca_tcga", "PIK3CA")
print(result)

Workflow 2: Pan-Cancer Gene Mutation Frequency

python
import requests, pandas as pd

def pan_cancer_mutation_freq(gene_symbol, cancer_study_ids=None):
    """Mutation frequency of a gene across multiple cancer types."""
    studies = get_all_studies()
    if cancer_study_ids:
        studies = [s for s in studies if s["studyId"] in cancer_study_ids]

    results = []
    for study in studies[:20]:  # Limit for demo
        try:
            freq = get_alteration_frequency(study["studyId"], [gene_symbol])
            results.append({
                "study": study["studyId"],
                "cancer": study.get("cancerTypeId", ""),
                "mutation_pct": freq.get(gene_symbol, 0)
            })
        except Exception:
            pass

    df = pd.DataFrame(results).sort_values("mutation_pct", ascending=False)
    return df

Workflow 3: Survival Analysis by Mutation Status

python
import requests, pandas as pd

def survival_by_mutation(study_id, gene_symbol):
    """Get survival data split by mutation status."""
    # This workflow fetches clinical and mutation data for downstream analysis

    gene_info = requests.post(
        f"{BASE_URL}/genes/fetch",
        params={"geneIdType": "HUGO_GENE_SYMBOL"},
        json=[gene_symbol],
        headers=HEADERS
    ).json()[0]
    entrez_id = gene_info["entrezGeneId"]

    mutations = get_mutations(f"{study_id}_mutations", [entrez_id])
    mutated_patients = set(m["patientId"] for m in mutations)

    # Patient-level survival via the per-study GET endpoint (clinical-data/fetch
    # is POST-only — a GET there returns HTTP 405).
    clinical = get_patient_clinical_data(study_id, ["OS_MONTHS", "OS_STATUS"])
    clinical_df = pd.DataFrame(clinical)

    os_wide = clinical_df.pivot(index="patientId", columns="clinicalAttributeId", values="value")
    # OS_STATUS is encoded as "1:DECEASED" / "0:LIVING"; split off the 0/1 event flag.
    if "OS_STATUS" in os_wide:
        os_wide["OS_EVENT"] = os_wide["OS_STATUS"].str.startswith("1").astype("Int64")
    os_wide["OS_MONTHS"] = pd.to_numeric(os_wide.get("OS_MONTHS"), errors="coerce")
    os_wide["mutated"] = os_wide.index.isin(mutated_patients)

    return os_wide

Key API Endpoints Summary

EndpointDescription
GET /studiesList all studies
GET /studies/{studyId}/molecular-profilesMolecular profiles for a study
POST /molecular-profiles/{profileId}/mutations/fetchGet mutation data
POST /molecular-profiles/{profileId}/discrete-copy-number/fetchGet CNA data
POST /molecular-profiles/{profileId}/molecular-data/fetchGet expression data
GET /studies/{studyId}/clinical-attributesAvailable clinical variables
GET /studies/{studyId}/clinical-dataClinical data for one study (one attributeId per call)
POST /clinical-data/fetch?clinicalDataType=PATIENTClinical data across studies (POST-only; GET → 405)
POST /genes/fetch?geneIdType=HUGO_GENE_SYMBOLResolve symbols → Entrez IDs (body is a plain JSON array, e.g. ["TP53"])
GET /studies/{studyId}/sample-listsSample lists

Best Practices

  • Know your study IDs: Use the Swagger UI or GET /studies to find the correct study ID
  • Use sample lists: Each study has an all sample list and subsets; always specify the appropriate one
  • TCGA vs. GENIE: TCGA data is comprehensive but older; GENIE has more recent clinical sequencing data, but its consortium releases live on the separate https://genie.cbioportal.org portal (login required), not on the keyless public API
  • Entrez gene IDs: The API uses Entrez IDs — convert from symbols with POST /genes/fetch?geneIdType=HUGO_GENE_SYMBOL. The body must be a plain JSON array (["TP53","KRAS"]); the object form [{"hugoGeneSymbol":...}] returns HTTP 400, and omitting geneIdType silently returns [] for symbols. Response order is not guaranteed — map results back by hugoGeneSymbol.
  • Handle 404s: Some molecular profiles may not exist for all studies
  • Rate limiting: Add delays for bulk queries; consider downloading data files for large-scale analyses

Data Downloads

For large-scale analyses, download study data directly (the older cbioportal-datahub.s3.amazonaws.com links now return 403):

bash
# Download TCGA BRCA (PanCancer Atlas) data
wget https://datahub.assets.cbioportal.org/brca_tcga_pan_can_atlas_2018.tar.gz

Additional Resources

Scripts

scripts/query_cbioportal.py — runnable helper for the cBioPortal REST API (public, no key):

bash
python scripts/query_cbioportal.py studies --filter tcga
python scripts/query_cbioportal.py profiles brca_tcga
python scripts/query_cbioportal.py mutations brca_tcga_mutations --genes 7157,672

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Alterlab Cbioportal AI skill do?

Query cBioPortal via its keyless REST API for cancer genomics across TCGA, GENIE, MSK-IMPACT and hundreds of studies — somatic mutations, copy-number alterations (GISTIC), mRNA/protein expression, structural variants, and patient-level clinical/survival data. Use when asked how often a gene is mutated/amplified/deleted in a tumor type, to profile oncogenes or tumor suppressors across cancers (pan-cancer alteration frequency), to pull patient-level mutations joined to OS/clinical outcomes, or to validate a cancer target from cohort genomics. For germline variant pathogenicity use alterlab-cl...

Why use Alterlab Cbioportal on TypingMind?

Because you install it once and use it with any model. Alterlab Cbioportal is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Alterlab Cbioportal in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/AlterLab-IEU/AlterLab-Academic-Skills/tree/main/skills/databases/alterlab-cbioportal. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Alterlab Cbioportal?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Alterlab Cbioportal?

As many as you like. As long as a model supports skills, you can use Alterlab Cbioportal with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Alterlab Cbioportal AI skill free?

Yes. It is published on GitHub by AlterLab-IEU under the LGPL-3.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇