Generated banner for Gtex Database
Generated logo for Gtex Database

Gtex Database

Google DeepMindOfficialPublished by Google DeepMind

  • Skill
  • Tool integration
  • Science and research
  • Apache-2.0
  • Low risk
  • Has scripts

From the author

Use when you want to retrieve quantitative RNA expression data and variant eQTL information from the GTEx (Genotype-Tissue Expression) Project across 54 non-diseased tissue sites.
— the description by Google DeepMind

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

The GTEx Database skill provides an interface to the GTEx Portal API V2, enabling researchers to retrieve baseline transcriptomics data and expression Quantitative Trait Loci (eQTLs). It facilitates the mapping of gene symbols to GENCODE IDs and allows for the extraction of median TPM expression values and significant eQTL associations across 54 non-diseased adult human tissues.

What it does

  • Maps standard gene symbols to Versioned GENCODE IDs.
  • Retrieves median TPM expression levels for genes across specified tissues.
  • Identifies tissues with the highest expression levels for a given gene.
  • Fetches significant eQTLs associated with specific genes.
  • Queries significant single-tissue eQTLs within defined chromosomal regions.

When to use it

  • When you need to analyze baseline gene expression in healthy adult human tissues.
  • When investigating the relationship between genetic variants and gene expression levels.
  • When contextualizing variants within GWAS loci using eQTL data.
  • When you require programmatic access to GTEx Portal data via a CLI wrapper.

Advantages

  • Provides direct access to standardized GTEx transcriptomics and eQTL datasets.
  • Includes a CLI wrapper that manages API rate limits automatically.
  • Supports targeted queries by tissue, gene, or chromosomal region.
  • Facilitates reproducible research workflows by outputting results to JSON files.

Usage examples

“Find the top 5 tissues where the GATA4 gene is most highly expressed.”
The agent resolves the GATA4 gene symbol to its GENCODE ID and then queries the GTEx database to return the top 5 tissues by median TPM.
“Get all significant eQTLs for the TNF gene in Whole Blood.”
The agent retrieves the relevant GENCODE ID for TNF and fetches the associated eQTL data specifically for the Whole Blood tissue site.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/google-deepmind/gtex-database/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip gtex-database.zip -d .agents/skills

Documentation by Google DeepMind

GTEx Database Integration

This skill retrieves transcriptomics data (RNA expression baselines) and expression Quantitative Trait Loci (eQTLs) from the GTEx Portal API V2. It provides access to median TPM (Transcripts Per Million) values for genes and significant eQTLs for variants across 54 human tissue sites.

Prerequisites

  1. uv: Read the uv skill and follow its Setup instructions to ensure uv is installed and on PATH.

  2. User Notification: If .licenses/gtex_database_LICENSE.txt does not already exist in the workspace root directory then (1) prominently notify the user to check the terms at https://gtexportal.org/home/license and https://gtexportal.org/home/documentationPage#gtexApi, then (2) create the file recording the notification text and timestamp.

When to Use

Use this skill when you need to:

  • Map a gene symbol to its Versioned GENCODE ID.

  • Retrieve the baseline median expression level (in TPM) of a gene across various tissues.

  • Find the top tissues where a particular gene is most highly expressed.

  • Fetch significant single-tissue eQTLs for a variant or within a chromosomal window.

  • Get all significant eQTLs associated with a specific gene.

  • Contextualise a variant within GWAS loci using eQTL data.

Do NOT use when you need to:

  • Query for protein-level expression or post-translational modifications (PTMs). GTEx only measures mRNA abundance.

  • Query gene expression in diseased tissues (e.g., tumor samples, cirrhosis). GTEx is a baseline atlas of normal, non-diseased tissues.

  • Query embryonic or fetal gene expression. GTEx donors are adults only.

Core Rules

CRITICAL: You MUST respect GTEx Portal API Terms of Use.

  • Use the Wrapper: ALWAYS execute the provided helper scripts to query the database rather than accessing the database directly. The scripts automatically enforce the required rate limit gracefully.

  • Limit requests to maximum 250 items per page where applicable.

  • Notification: If this skill is used, ensure this is mentioned in the output.

Command Selection Guide

Pick the right command on the first try. Match the user's input to the correct subcommand below.

  • Map a gene symbol to GENCODE ID: resolve-gencode-id

  • Get median expression (TPM) for a gene: get-median-expression

  • Find tissues with highest expression for a gene: get-top-expressed-tissues

  • Get all eQTLs for a specific gene: get-gene-eqtls

  • Find eQTLs within a chromosomal region: get-eqtls-in-region

Quick Start

# Map the TNF gene symbol to its GENCODE ID
uv run scripts/gtex_cli.py resolve-gencode-id TNF --output /tmp/tnf_id.json

# Get median expression of a gene by GENCODE ID
uv run scripts/gtex_cli.py get-median-expression ENSG00000232810.2 --output /tmp/tnf_expr.json

All subcommands write JSON to disk. Always save output in the /tmp/ directory. The default output file is /tmp/gtex_output.json if --output is not specified.

Commands

1. resolve-gencode-id — Gene Symbol → GENCODE ID

Maps a standard gene symbol (e.g., "JUN", "TNF") to its Versioned GENCODE ID. This ID is required for all other expression and eQTL calls.

uv run scripts/gtex_cli.py resolve-gencode-id TNF --output /tmp/tnf_id.json

Arguments:

  • gene_symbol (positional): The standard gene symbol (e.g., "TNF").

  • --output: Output file path (default: /tmp/gtex_output.json).

2. get-median-expression — Get Median Expression (TPM)

Retrieves the median TPM for a gene across all 54 GTEx tissue sites or specified tissues.

uv run scripts/gtex_cli.py get-median-expression ENSG00000232810.2 \
  --tissues "Whole Blood,Spleen" --output /tmp/expr.json

Arguments:

  • gencode_id (positional): The Versioned GENCODE ID.

  • --tissues: Comma-separated list of tissue IDs (optional, defaults to all 54 tissues).

  • --output: Output file path (default: /tmp/gtex_output.json).

3. get-top-expressed-tissues — Get Top Expressed Tissues

Returns the n tissues with the highest median expression for the target gene.

uv run scripts/gtex_cli.py get-top-expressed-tissues ENSG00000232810.2 \
  --n 5 --output /tmp/top_tissues.json

Arguments:

  • gencode_id (positional): The Versioned GENCODE ID.

  • --n: Number of top tissues to return (default: 5).

  • --output: Output file path.

4. get-gene-eqtls — Get All eQTLs for a Gene

Returns every significant eQTL associated with the gene across specified tissues.

uv run scripts/gtex_cli.py get-gene-eqtls ENSG00000232810.2 \
  --tissues "Whole Blood" --output /tmp/eqtls.json

Arguments:

  • gencode_id (positional): The Versioned GENCODE ID.

  • --tissues: Comma-separated list of tissue IDs (optional, defaults to all).

  • --output: Output file path.

5. get-eqtls-in-region — Get eQTLs in Chromosomal Region

Returns all significant single-tissue eQTLs within a chromosomal window (up to 8Mb).

uv run scripts/gtex_cli.py get-eqtls-in-region chr17 7000000 7100000 "Esophagus - Muscularis" \
  --output /tmp/region_eqtls.json

Arguments:

  • chromosome (positional): Chromosome name (e.g., chr17).

  • start (positional): Start position.

  • end (positional): End position (max 8Mb from start).

  • tissue_id (positional): The target tissue ID.

  • --output: Output file path.

Typical Workflows

Identify highest expressing tissues for a gene
# Step 1: Map symbol to GENCODE ID
uv run scripts/gtex_cli.py resolve-gencode-id GATA4 --output /tmp/gata4_id.json

# Step 2: Query for top tissues using the resolved ID
uv run scripts/gtex_cli.py get-top-expressed-tissues <gencode_id> --n 5 \
  --output /tmp/gata4_top.json

Files

scripts/gtex_cli.py

# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""CLI wrapper for GTEx API V2.

Follows GTEx Portal Terms of Use by fetching sequentially and handling
pagination.
"""

# /// script
# requires-python = ">=3.10"
# dependencies = [
#   "polite-http",
# ]
# ///

import argparse
import json
import sys
import urllib.parse

from polite_http import http_client

BASE_URL = 'https://gtexportal.org/api/v2'
DATASET_ID = 'gtex_v10'
GENCODE_VERSION = 'v39'
CLIENT = http_client.HttpClient(BASE_URL, qps=1.0)

# Optional cache for tissues to avoid fetching repeatedly
TISSUE_CACHE = None


def _fetch(url, params=None):
  """Fetches URL, with optional query parameters, using HttpClient."""
  if params:
    # Filter out None values
    params = {k: v for k, v in params.items() if v is not None}
    query_string = urllib.parse.urlencode(params, doseq=True)
    full_url = f'{url}?{query_string}'
  else:
    full_url = url

  return CLIENT.fetch_json(full_url)


def _fetch_paginated(url, params=None):
  """Fetches all pages from a paginated endpoint."""
  if params is None:
    params = {}

  all_data = []
  page = 0
  while True:
    params['page'] = page
    response = _fetch(url, params)

    # Some endpoints don't use 'data' wrapping or 'paging_info'
    if 'paging_info' not in response:
      return response

    data = response.get('data', [])
    all_data.extend(data)

    paging = response.get('paging_info', {})
    total_pages = paging.get('numberOfPages', 0)

    if page >= total_pages - 1:
      break
    page += 1

  return all_data


def get_tissue_mapping():
  """Fetches the list of tissues and maps names to tissueSiteDetailId."""
  global TISSUE_CACHE
  if TISSUE_CACHE is not None:
    return TISSUE_CACHE

  url = f'{BASE_URL}/dataset/tissueSiteDetail'
  data = _fetch_paginated(url, {'datasetId': DATASET_ID})

  mapping = {}
  for t in data:
    id_ = t.get('tissueSiteDetailId')
    name = t.get('tissueSiteDetail')
    if id_ and name:
      mapping[id_.lower()] = id_
      mapping[name.lower()] = id_
      # Allow "Esophagus - Muscularis" instead of "Esophagus_Muscularis", etc.
      mapping[id_.replace('_', ' ').lower()] = id_
      mapping[name.replace('-', ' ').lower()] = id_

  TISSUE_CACHE = mapping
  return mapping


def resolve_tissue(tissue_str):
  mapping = get_tissue_mapping()
  cleaned = tissue_str.strip().lower()
  if cleaned in mapping:
    return mapping[cleaned]
  # Try more fuzzy matching if needed
  cleaned_no_hyphen = cleaned.replace('-', ' ')
  if cleaned_no_hyphen in mapping:
    return mapping[cleaned_no_hyphen]
  sys.stderr.write(f"Error: Unknown tissue '{tissue_str}'.\n")
  sys.exit(1)


def resolve_gencode_id(gene_symbol, output_file):
  """Maps a standard gene symbol to its Versioned GENCODE ID."""
  url = f'{BASE_URL}/reference/gene'
  data = _fetch_paginated(
      url, {'geneId': gene_symbol, 'gencodeVersion': GENCODE_VERSION}
  )
  if not data:
    sys.stderr.write(f"Error: Could not find GENCODE ID for '{gene_symbol}'.\n")
    sys.exit(1)

  # Return the first matching exact symbol if possible, else just the first one
  best_match = data[0]
  for d in data:
    if d.get('geneSymbol', '').lower() == gene_symbol.lower():
      best_match = d
      break

  result = {
      'gene_symbol': best_match.get('geneSymbol'),
      'gencode_id': best_match.get('gencodeId'),
      'chromosome': best_match.get('chromosome'),
      'start': best_match.get('start'),
      'end': best_match.get('end'),
      'gene_type': best_match.get('geneType'),
  }

  with open(output_file, 'w') as f:
    json.dump(result, f, indent=2)


def get_median_expression(gencode_id, tissues, output_file):
  """Returns the median expression (TPM) for a gene across tissues."""
  url = f'{BASE_URL}/expression/medianGeneExpression'
  params = {'gencodeId': gencode_id, 'datasetId': DATASET_ID}

  if tissues:
    tissue_list = [t.strip() for t in tissues.split(',')]
    resolved_tissues = [resolve_tissue(t) for t in tissue_list]
    # The API accepts multiple tissueSiteDetailId parameters.
    # urllib.urlencode with doseq=True handles list correctly.
    params['tissueSiteDetailId'] = resolved_tissues

  data = _fetch_paginated(url, params)

  with open(output_file, 'w') as f:
    json.dump(data, f, indent=2)


def get_top_expressed_tissues(gencode_id, n, output_file):
  url = f'{BASE_URL}/expression/medianGeneExpression'
  params = {'gencodeId': gencode_id, 'datasetId': DATASET_ID}

  data = _fetch_paginated(url, params)
  # Sort by median expression, descending
  sorted_data = sorted(data, key=lambda x: x.get('median', 0), reverse=True)

  top_n = sorted_data[:n]
  with open(output_file, 'w') as f:
    json.dump(top_n, f, indent=2)


def get_gene_eqtls(gencode_id, tissues, output_file):
  """Returns all significant eQTLs for a gene across tissues."""
  url = f'{BASE_URL}/association/singleTissueEqtl'
  params = {'gencodeId': gencode_id, 'datasetId': DATASET_ID}

  if tissues:
    tissue_list = [t.strip() for t in tissues.split(',')]
    resolved_tissues = [resolve_tissue(t) for t in tissue_list]
    params['tissueSiteDetailId'] = resolved_tissues

  data = _fetch_paginated(url, params)

  with open(output_file, 'w') as f:
    json.dump(data, f, indent=2)


def get_eqtls_in_region(chromosome, start, end, tissue, output_file):
  """Returns all significant eQTLs for a region in a tissue."""
  url = f'{BASE_URL}/association/singleTissueEqtlByLocation'
  resolved_tissue = resolve_tissue(tissue)

  params = {
      'chromosome': chromosome,
      'start': start,
      'end': end,
      'tissueSiteDetailId': resolved_tissue,
      'datasetId': DATASET_ID,
  }

  # This endpoint is NOT paginated per API docs and does not return paging_info.
  data = _fetch(url, params)

  with open(output_file, 'w') as f:
    json.dump(data, f, indent=2)


def main():
  parser = argparse.ArgumentParser(description='GTEx Portal API V2 CLI')
  subparsers = parser.add_subparsers(dest='command', required=True)

  p_resolve = subparsers.add_parser(
      'resolve-gencode-id',
      help='Map a standard gene symbol to its Versioned GENCODE ID',
  )
  p_resolve.add_argument('gene_symbol', help='Gene symbol (e.g. TNF)')
  p_resolve.add_argument('--output', default='/tmp/gtex_output.json')

  p_median = subparsers.add_parser(
      'get-median-expression', help='Get median expression (TPM)'
  )
  p_median.add_argument('gencode_id', help='Versioned GENCODE ID')
  p_median.add_argument('--tissues', help='Comma-separated list of tissue IDs')
  p_median.add_argument('--output', default='/tmp/gtex_output.json')

  p_top = subparsers.add_parser(
      'get-top-expressed-tissues', help='Get top expressed tissues for a gene'
  )
  p_top.add_argument('gencode_id', help='Versioned GENCODE ID')
  p_top.add_argument(
      '--n', type=int, default=5, help='Number of tissues to return'
  )
  p_top.add_argument('--output', default='/tmp/gtex_output.json')

  p_eqtls = subparsers.add_parser(
      'get-gene-eqtls', help='Get all significant eQTLs for a gene'
  )
  p_eqtls.add_argument('gencode_id', help='Versioned GENCODE ID')
  p_eqtls.add_argument('--tissues', help='Comma-separated list of tissue IDs')
  p_eqtls.add_argument('--output', default='/tmp/gtex_output.json')

  p_region = subparsers.add_parser(
      'get-eqtls-in-region', help='Get significant eQTLs in a region'
  )
  p_region.add_argument('chromosome', help='Chromosome (e.g. chr17)')
  p_region.add_argument('start', type=int, help='Start position')
  p_region.add_argument('end', type=int, help='End position')
  p_region.add_argument('tissue_id', help='Target tissue ID')
  p_region.add_argument('--output', default='/tmp/gtex_output.json')

  args = parser.parse_args()

  if args.command == 'resolve-gencode-id':
    resolve_gencode_id(args.gene_symbol, args.output)
  elif args.command == 'get-median-expression':
    get_median_expression(args.gencode_id, args.tissues, args.output)
  elif args.command == 'get-top-expressed-tissues':
    get_top_expressed_tissues(args.gencode_id, args.n, args.output)
  elif args.command == 'get-gene-eqtls':
    get_gene_eqtls(args.gencode_id, args.tissues, args.output)
  elif args.command == 'get-eqtls-in-region':
    get_eqtls_in_region(
        args.chromosome, args.start, args.end, args.tissue_id, args.output
    )
  else:
    parser.print_help()


if __name__ == '__main__':
  main()

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Python

Security

What our scanner noticed· Low risk

  • Scripts that your agent may run

This skill includes scripts that your agent may run. Read them before you use it.

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
Google DeepMind
Publisher
Google DeepMind (Official)
Source
google-deepmind/science-skills · skills/gtex_database · commit 4de6786
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Google DeepMind.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download gtex-database.zip