Skill: hollyya/uniprot-query (version 1.0.0)

--- name: uniprot-query description: > Query UniProt database for protein sequences, metadata, and search by criteria. Use this skill when: (1) Looking up protein information by UniProt accession ID, (2) Searching proteins by gene name, organism, function, or disease, (3) Retrieving comprehensive protein metadata including domains, PTMs, and annotations. license: MIT category: knowledge-retrieval tags: [uniprot, protein, database, metadata, sequence] --- # UniProt Query Query the UniProt knowledgebase for comprehensive protein information. ## When to Use - Look up protein by UniProt accession (e.g., P00533 for EGFR) - Search proteins by gene name, organism, or keywords - Retrieve protein metadata: function, domains, diseases, PTMs - Get protein sequences and structural annotations ## Workflow ### Use Case 1: Protein Lookup by ID Fetch complete protein information including metadata. ```python from open_biomed.tools.tool_registry import TOOLS import requests import json # Get protein sequence (existing tool) tool = TOOLS["protein_uniprot_request"] proteins, _ = tool.run(accession="P0DTC2") # SARS-CoV-2 Spike protein = proteins[0] # Fetch full metadata from UniProt API url = f"https://rest.uniprot.org/uniprotkb/P0DTC2?format=json" response = requests.get(url) metadata = parse_uniprot_entry(response.json()) ``` See `examples/lookup_by_id.py` for complete implementation. ### Use Case 2: Search by Criteria Search UniProt by gene name, organism, keywords, or disease. ```python import requests base_url = "https://rest.uniprot.org/uniprotkb/search" # Example queries: queries = { "gene_exact:EGFR AND organism_id:9606": "Human EGFR", "gene_exact:S AND organism_id:2697049": "SARS-CoV-2 Spike", "keyword:Kinase AND organism_id:9606": "Human kinases", "diabetes AND organism_id:9606": "Diabetes-related proteins", } params = { "query": "gene_exact:EGFR AND organism_id:9606 AND reviewed:true", "fields": "accession,gene_primary,protein_name,organism_name,length", "format": "json", "size": 10 } response = requests.get(base_url, params=params) ``` See `examples/search_by_criteria.py` for complete implementation. ## Query Syntax Reference | Field | Example | Description | |-------|---------|-------------| | `gene_exact` | `gene_exact:EGFR` | Exact gene name match | | `gene` | `gene:BRCA` | Gene name (partial match) | | `organism_id` | `organism_id:9606` | Organism by TaxID | | `organism` | `organism:"Homo sapiens"` | Organism by name | | `protein_name` | `protein_name:kinase` | Protein name search | | `keyword` | `keyword:Kinase` | UniProt keyword | | `cc_disease` | `cc_disease:diabetes` | Disease association | | `reviewed` | `reviewed:true` | Swiss-Prot only (curated) | **Common Organism IDs**: Human (9606), Mouse (10090), SARS-CoV-2 (2697049), E. coli (83333) **Combine queries**: Use `AND`, `OR` to combine criteria: - `gene_exact:EGFR AND organism_id:9606 AND reviewed:true` ## Expected Outputs ### Metadata JSON (lookup_by_id) ```json { "accession": "P0DTC2", "uniProtId": "SPIKE_SARS2", "protein": {"name": "Spike glycoprotein"}, "gene": {"primary": "S", "synonyms": []}, "organism": {"scientific_name": "...", "taxon_id": 2697049}, "sequence": {"length": 1273, "mass": 141178}, "function": ["Attaches the virion to host receptor..."], "domains": [{"type": "Domain", "description": "RBD", "location": "319-541"}], "keywords": ["Glycoprotein", "Transmembrane", "Viral attachment"], "subcellular_location": ["Virion membrane"] } ``` ### Text Report ``` ====================================================================== UNIPROT PROTEIN REPORT ====================================================================== Accession: P0DTC2 Protein: Spike glycoprotein Gene: S Organism: Severe acute respiratory syndrome coronavirus 2 Length: 1273 aa FUNCTION Attaches the virion to the cell membrane by interacting with host receptor ACE2... DOMAINS • Domain: BetaCoV S1-NTD (14-303) • Region: Receptor-binding domain (319-541) ``` ### Search Results JSON ```json { "query": "gene_exact:S AND organism_id:2697049", "total_results": 10, "results": [ {"accession": "P0DTC2", "gene": "S", "protein_name": "Spike glycoprotein", ...} ] } ``` ## Error Handling | Error | Solution | |-------|----------| | Accession not found | Verify UniProt ID format (e.g., P00533, not EGFR) | | No search results | Broaden query, remove `reviewed:true`, check organism ID | | Timeout | Reduce `size` parameter, simplify query | | Rate limited | Wait and retry; UniProt allows 10 requests/second | ## Available Tools | Tool | Purpose | |------|---------| | `protein_uniprot_request` | Fetch protein sequence by accession (existing) | The workflows in this skill extend the basic tool with full metadata retrieval via UniProt REST API. ## References - `references/query_fields.md` - Complete query field reference - `references/metadata_fields.md` - Available metadata fields - UniProt API Docs: https://www.uniprot.org/api-documentation

path: examples/lookup_by_id.py
content: """
UniProt Query - Use Case 1: Protein Lookup by ID

Fetch comprehensive protein information including metadata and generate reports.

Usage:
    python lookup_by_id.py P0DTC2
    python lookup_by_id.py P00533 --output ./output
"""

import argparse
import json
import os
import requests
from datetime import datetime
from typing import Dict, Any, List, Tuple

# OpenBioMed imports
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
from open_biomed.tools.tool_registry import TOOLS
from open_biomed.data import Protein


def parse_uniprot_entry(data: Dict) -> Dict[str, Any]:
    """Parse UniProt JSON entry into structured metadata."""
    metadata = {
        'accession': data.get('primaryAccession'),
        'uniProtId': data.get('uniProtkbId'),
        'entryType': data.get('entryType'),
        'protein': {},
        'gene': {},
        'organism': {},
        'sequence': {},
        'function': [],
        'diseases': [],
        'keywords': [],
        'domains': [],
        'subcellular_location': [],
        'ptms': [],
        'cross_references': {},
        'query_date': datetime.now().isoformat()
    }

    # Protein name
    prot_desc = data.get('proteinDescription', {})
    rec_name = prot_desc.get('recommendedName', {})
    if rec_name:
        metadata['protein']['name'] = rec_name.get('fullName', {}).get('value')
        ec_nums = rec_name.get('ecNumbers', [])
        metadata['protein']['ec_numbers'] = [ec.get('value') for ec in ec_nums]

    alt_names = prot_desc.get('alternativeNames', [])
    metadata['protein']['alternative_names'] = [
        alt.get('fullName', {}).get('value') for alt in alt_names if alt.get('fullName')
    ]

    # Gene name
    genes = data.get('genes', [])
    if genes:
        metadata['gene']['primary'] = genes[0].get('geneName', {}).get('value')
        metadata['gene']['synonyms'] = [s.get('value') for s in genes[0].get('synonyms', [])]

    # Organism
    org = data.get('organism', {})
    metadata['organism']['scientific_name'] = org.get('scientificName')
    metadata['organism']['common_name'] = org.get('commonName')
    metadata['organism']['taxon_id'] = org.get('taxonId')

    # Sequence
    seq_info = data.get('sequence', {})
    metadata['sequence']['length'] = seq_info.get('length')
    metadata['sequence']['mass'] = seq_info.get('molWeight')
    metadata['sequence']['value'] = seq_info.get('value')

    # Comments (function, disease, location)
    comments = data.get('comments', [])
    for comment in comments:
        comment_type = comment.get('commentType')

        if comment_type == 'FUNCTION':
            for text in comment.get('texts', []):
                metadata['function'].append(text.get('value'))

        elif comment_type == 'DISEASE':
            disease = comment.get('disease', {})
            metadata['diseases'].append({
                'id': disease.get('diseaseId'),
                'description': disease.get('description'),
                'acronym': disease.get('acronym'),
                'cross_reference': disease.get('diseaseCrossReference', {})
            })

        elif comment_type == 'SUBCELLULAR LOCATION':
            for loc in comment.get('subcellularLocations', []):
                loc_value = loc.get('location', {}).get('value')
                if loc_value:
                    metadata['subcellular_location'].append(loc_value)

        elif comment_type == 'PTM':
            for text in comment.get('texts', []):
                metadata['ptms'].append(text.get('value'))

    # Keywords
    keywords = data.get('keywords', [])
    metadata['keywords'] = [kw.get('name') for kw in keywords]

    # Features (domains, regions, motifs)
    features = data.get('features', [])
    for feat in features:
        feat_type = feat.get('type')
        if feat_type in ['Domain', 'Region', 'Repeat', 'Motif', 'Active site', 'Binding site']:
            loc = feat.get('location', {})
            metadata['domains'].append({
                'type': feat_type,
                'description': feat.get('description'),
                'start': loc.get('start', {}).get('value'),
                'end': loc.get('end', {}).get('value')
            })

    # Cross-references
    xrefs = data.get('uniProtKBCrossReferences', [])
    for xref in xrefs[:20]:  # Limit to first 20
        db = xref.get('database')
        if db not in metadata['cross_references']:
            metadata['cross_references'][db] = []
        metadata['cross_references'][db].append(xref.get('id'))

    return metadata


def generate_report(metadata: Dict) -> str:
    """Generate human-readable text report from metadata."""
    lines = []
    lines.append('=' * 70)
    lines.append('UNIPROT PROTEIN REPORT')
    lines.append('=' * 70)
    lines.append(f'Generated: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}')
    lines.append('')

    # Identification
    lines.append('─' * 70)
    lines.append('IDENTIFICATION')
    lines.append('─' * 70)
    lines.append(f'Accession:     {metadata.get("accession", "N/A")}')
    lines.append(f'UniProt ID:    {metadata.get("uniProtId", "N/A")}')
    lines.append(f'Entry Type:    {metadata.get("entryType", "N/A")}')
    lines.append('')

    # Protein
    lines.append('─' * 70)
    lines.append('PROTEIN')
    lines.append('─' * 70)
    prot = metadata.get('protein', {})
    lines.append(f'Name:          {prot.get("name", "N/A")}')
    if prot.get('ec_numbers'):
        lines.append(f'EC Numbers:    {", ".join(prot["ec_numbers"])}')
    if prot.get('alternative_names'):
        lines.append(f'Alt Names:     {", ".join(prot["alternative_names"][:3])}')
    lines.append('')

    # Gene
    lines.append('─' * 70)
    lines.append('GENE')
    lines.append('─' * 70)
    gene = metadata.get('gene', {})
    lines.append(f'Primary:       {gene.get("primary", "N/A")}')
    if gene.get('synonyms'):
        lines.append(f'Synonyms:      {", ".join(gene["synonyms"])}')
    lines.append('')

    # Organism
    lines.append('─' * 70)
    lines.append('ORGANISM')
    lines.append('─' * 70)
    org = metadata.get('organism', {})
    lines.append(f'Scientific:    {org.get("scientific_name", "N/A")}')
    lines.append(f'Common:        {org.get("common_name", "N/A")}')
    lines.append(f'Taxon ID:      {org.get("taxon_id", "N/A")}')
    lines.append('')

    # Sequence
    lines.append('─' * 70)
    lines.append('SEQUENCE')
    lines.append('─' * 70)
    seq = metadata.get('sequence', {})
    lines.append(f'Length:        {seq.get("length", "N/A")} amino acids')
    lines.append(f'Mass:          {seq.get("mass", "N/A")} Da')
    lines.append('')

    # Function
    lines.append('─' * 70)
    lines.append('FUNCTION')
    lines.append('─' * 70)
    for i, func in enumerate(metadata.get('function', [])[:3], 1):
        func_text = func[:300] + '...' if len(func) > 300 else func
        lines.append(f'{i}. {func_text}')
        lines.append('')

    # Diseases
    lines.append('─' * 70)
    lines.append('DISEASES')
    lines.append('─' * 70)
    diseases = metadata.get('diseases', [])
    if diseases:
        for disease in diseases:
            lines.append(f'• {disease.get("id", "N/A")} ({disease.get("acronym", "N/A")})')
            desc = disease.get('description', 'No description')
            lines.append(f'  {desc[:150]}...' if len(desc) > 150 else f'  {desc}')
    else:
        lines.append('No disease annotations')
    lines.append('')

    # Subcellular Location
    lines.append('─' * 70)
    lines.append('SUBCELLULAR LOCATION')
    lines.append('─' * 70)
    locations = metadata.get('subcellular_location', [])
    if locations:
        for loc in locations:
            lines.append(f'• {loc}')
    else:
        lines.append('No location annotations')
    lines.append('')

    # Keywords
    lines.append('─' * 70)
    lines.append('KEYWORDS')
    lines.append('─' * 70)
    keywords = metadata.get('keywords', [])
    lines.append(', '.join(keywords[:15]))
    lines.append('')

    # Domains
    lines.append('─' * 70)
    lines.append('DOMAINS/FEATURES')
    lines.append('─' * 70)
    domains = metadata.get('domains', [])
    for domain in domains[:10]:
        start = domain.get('start', '?')
        end = domain.get('end', '?')
        lines.append(f'• {domain.get("type")}: {domain.get("description")} ({start}-{end})')
    lines.append('')

    # Cross-references
    lines.append('─' * 70)
    lines.append('CROSS-REFERENCES')
    lines.append('─' * 70)
    xrefs = metadata.get('cross_references', {})
    for db, ids in list(xrefs.items())[:10]:
        lines.append(f'{db}: {", ".join(ids[:5])}')
    lines.append('')

    lines.append('=' * 70)
    lines.append('END OF REPORT')
    lines.append('=' * 70)

    return '\n'.join(lines)


def lookup_protein(accession: str, output_dir: str = './tmp/uniprot') -> Tuple[Dict, str, Protein]:
    """
    Look up protein by UniProt accession.

    Args:
        accession: UniProt accession (e.g., P00533, P0DTC2)
        output_dir: Directory for output files

    Returns:
        Tuple of (metadata dict, report text, Protein object)
    """
    os.makedirs(output_dir, exist_ok=True)

    # Step 1: Get protein sequence using OpenBioMed tool
    print(f"Fetching protein sequence for {accession}...")
    tool = TOOLS["protein_uniprot_request"]
    proteins, message = tool.run(accession=accession)
    protein = proteins[0]
    print(f"  Sequence length: {len(protein.sequence)} aa")

    # Step 2: Fetch full metadata from UniProt REST API
    print(f"Fetching metadata from UniProt API...")
    url = f"https://rest.uniprot.org/uniprotkb/{accession}?format=json"
    response = requests.get(url)
    response.raise_for_status()

    # Step 3: Parse metadata
    metadata = parse_uniprot_entry(response.json())
    print(f"  Protein: {metadata['protein'].get('name', 'N/A')}")
    print(f"  Gene: {metadata['gene'].get('primary', 'N/A')}")

    # Step 4: Generate report
    report = generate_report(metadata)

    # Step 5: Save outputs
    metadata_file = os.path.join(output_dir, f"{accession}_metadata.json")
    with open(metadata_file, 'w') as f:
        json.dump(metadata, f, indent=2, default=str)
    print(f"  Metadata saved: {metadata_file}")

    report_file = os.path.join(output_dir, f"{accession}_report.txt")
    with open(report_file, 'w') as f:
        f.write(report)
    print(f"  Report saved: {report_file}")

    return metadata, report, protein


def main():
    parser = argparse.ArgumentParser(description='Lookup protein by UniProt accession')
    parser.add_argument('accession', help='UniProt accession (e.g., P00533, P0DTC2)')
    parser.add_argument('--output', '-o', default='./tmp/uniprot', help='Output directory')
    args = parser.parse_args()

    metadata, report, protein = lookup_protein(args.accession, args.output)

    print("\n" + report)


if __name__ == "__main__":
    main()


path: examples/search_by_criteria.py
content: """
UniProt Query - Use Case 2: Search by Criteria

Search UniProt database by gene name, organism, keywords, or disease.

Usage:
    python search_by_criteria.py "gene_exact:EGFR AND organism_id:9606"
    python search_by_criteria.py "keyword:Kinase" --organism 9606 --limit 20
    python search_by_criteria.py "diabetes" --organism 9606 --reviewed
"""

import argparse
import json
import os
import requests
from datetime import datetime
from typing import Dict, Any, List, Tuple, Optional


# Common organism IDs
ORGANISM_IDS = {
    'human': 9606,
    'mouse': 10090,
    'rat': 10116,
    'ecoli': 83333,
    'yeast': 559292,
    'sars-cov-2': 2697049,
    'sars-cov': 694009,
}


def build_query(
    query: str,
    organism: Optional[int] = None,
    reviewed: bool = False,
) -> str:
    """Build UniProt query string from components."""
    parts = [query]

    if organism:
        parts.append(f"organism_id:{organism}")

    if reviewed:
        parts.append("reviewed:true")

    return " AND ".join(parts)


def search_uniprot(
    query: str,
    fields: str = "accession,gene_primary,gene_synonym,protein_name,organism_name,length,mass,cc_function,keyword",
    size: int = 10,
    format: str = "json"
) -> Tuple[List[Dict], Dict]:
    """
    Search UniProt by query string.

    Args:
        query: UniProt query string
        fields: Comma-separated list of fields to return
        size: Maximum number of results
        format: Response format (json, tsv, etc.)

    Returns:
        Tuple of (results list, response headers dict)
    """
    base_url = "https://rest.uniprot.org/uniprotkb/search"

    params = {
        'query': query,
        'fields': fields,
        'format': format,
        'size': size
    }

    response = requests.get(base_url, params=params)
    response.raise_for_status()

    data = response.json()
    results = data.get('results', [])

    # Get total count from headers
    headers = {
        'total_results': response.headers.get('x-total-results', 'unknown'),
        'link': response.headers.get('link', '')
    }

    return results, headers


def parse_search_results(results: List[Dict]) -> List[Dict[str, Any]]:
    """Parse search results into structured format."""
    parsed = []

    for entry in results:
        entry_data = {
            'accession': entry.get('primaryAccession'),
            'uniProtId': entry.get('uniProtkbId'),
            'reviewed': 'reviewed' in entry.get('entryType', '').lower(),
        }

        # Gene
        genes = entry.get('genes', [])
        if genes:
            entry_data['gene'] = {
                'primary': genes[0].get('geneName', {}).get('value'),
                'synonyms': [s.get('value') for s in genes[0].get('synonyms', [])]
            }
        else:
            entry_data['gene'] = {'primary': None, 'synonyms': []}

        # Protein
        prot_desc = entry.get('proteinDescription', {})
        rec_name = prot_desc.get('recommendedName', {})
        entry_data['protein_name'] = rec_name.get('fullName', {}).get('value') if rec_name else None

        # Organism
        org = entry.get('organism', {})
        entry_data['organism'] = {
            'scientific_name': org.get('scientificName'),
            'taxon_id': org.get('taxonId')
        }

        # Sequence
        seq = entry.get('sequence', {})
        entry_data['sequence'] = {
            'length': seq.get('length'),
            'mass': seq.get('molWeight')
        }

        # Function
        comments = entry.get('comments', [])
        functions = []
        for comment in comments:
            if comment.get('commentType') == 'FUNCTION':
                for text in comment.get('texts', []):
                    functions.append(text.get('value'))
        entry_data['function'] = functions

        # Keywords
        keywords = entry.get('keywords', [])
        entry_data['keywords'] = [kw.get('name') for kw in keywords]

        parsed.append(entry_data)

    return parsed


def generate_search_report(query: str, results: List[Dict], total: str) -> str:
    """Generate human-readable search results report."""
    lines = []
    lines.append('=' * 70)
    lines.append('UNIPROT SEARCH RESULTS REPORT')
    lines.append('=' * 70)
    lines.append(f'Generated: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}')
    lines.append(f'Query: {query}')
    lines.append(f'Total Results: {total}')
    lines.append(f'Returned: {len(results)}')
    lines.append('')

    for i, entry in enumerate(results, 1):
        lines.append('─' * 70)
        lines.append(f'ENTRY {i}: {entry.get("accession", "N/A")}')
        lines.append('─' * 70)

        gene = entry.get('gene', {})
        lines.append(f'Gene:          {gene.get("primary", "N/A")}')
        if gene.get('synonyms'):
            lines.append(f'Synonyms:      {", ".join(gene["synonyms"])}')

        lines.append(f'Protein:       {entry.get("protein_name", "N/A")}')

        org = entry.get('organism', {})
        lines.append(f'Organism:      {org.get("scientific_name", "N/A")}')

        seq = entry.get('sequence', {})
        lines.append(f'Length:        {seq.get("length", "N/A")} aa')
        if seq.get('mass'):
            lines.append(f'Mass:          {seq.get("mass")} Da')

        # Function summary
        if entry.get('function'):
            func = entry['function'][0]
            func_short = func[:150] + '...' if len(func) > 150 else func
            lines.append(f'Function:      {func_short}')

        # Keywords
        if entry.get('keywords'):
            lines.append(f'Keywords:      {", ".join(entry["keywords"][:5])}')

        lines.append('')

    lines.append('=' * 70)
    lines.append('END OF REPORT')
    lines.append('=' * 70)

    return '\n'.join(lines)


def search_proteins(
    query: str,
    organism: Optional[int] = None,
    reviewed: bool = False,
    limit: int = 10,
    output_dir: str = './tmp/uniprot'
) -> Tuple[List[Dict], str]:
    """
    Search proteins by criteria.

    Args:
        query: Search query (gene name, keyword, etc.)
        organism: Organism taxon ID (optional)
        reviewed: Only reviewed (Swiss-Prot) entries
        limit: Maximum results to return
        output_dir: Directory for output files

    Returns:
        Tuple of (parsed results list, report text)
    """
    os.makedirs(output_dir, exist_ok=True)

    # Build query
    full_query = build_query(query, organism, reviewed)
    print(f"Query: {full_query}")

    # Execute search
    print(f"Searching UniProt...")
    raw_results, headers = search_uniprot(full_query, size=limit)
    total = headers.get('total_results', str(len(raw_results)))
    print(f"  Found: {total} results")

    # Parse results
    parsed_results = parse_search_results(raw_results)

    # Generate report
    report = generate_search_report(full_query, parsed_results, total)

    # Save outputs
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")

    metadata = {
        'query': full_query,
        'query_date': datetime.now().isoformat(),
        'total_results': total,
        'returned_results': len(parsed_results),
        'results': parsed_results
    }

    metadata_file = os.path.join(output_dir, f"search_results_{timestamp}.json")
    with open(metadata_file, 'w') as f:
        json.dump(metadata, f, indent=2, default=str)
    print(f"  Metadata saved: {metadata_file}")

    report_file = os.path.join(output_dir, f"search_results_{timestamp}.txt")
    with open(report_file, 'w') as f:
        f.write(report)
    print(f"  Report saved: {report_file}")

    return parsed_results, report


def main():
    parser = argparse.ArgumentParser(description='Search UniProt by criteria')
    parser.add_argument('query', help='Search query (gene name, keyword, etc.)')
    parser.add_argument('--organism', '-o', type=int, help='Organism taxon ID (e.g., 9606 for human)')
    parser.add_argument('--organism-name', choices=list(ORGANISM_IDS.keys()),
                        help='Organism name shortcut (human, mouse, etc.)')
    parser.add_argument('--reviewed', '-r', action='store_true',
                        help='Only reviewed (Swiss-Prot) entries')
    parser.add_argument('--limit', '-l', type=int, default=10,
                        help='Maximum results to return (default: 10)')
    parser.add_argument('--output', dest='output_dir', default='./tmp/uniprot',
                        help='Output directory')
    args = parser.parse_args()

    # Resolve organism
    organism = args.organism
    if args.organism_name:
        organism = ORGANISM_IDS[args.organism_name]

    results, report = search_proteins(
        query=args.query,
        organism=organism,
        reviewed=args.reviewed,
        limit=args.limit,
        output_dir=args.output_dir
    )

    print("\n" + report)


if __name__ == "__main__":
    main()


path: references/metadata_fields.md
content: # UniProt Metadata Fields Reference

Fields available in UniProt protein entries.

## Entry Identification

| Field | JSON Path | Description |
|-------|-----------|-------------|
| Accession | `primaryAccession` | Primary UniProt accession (e.g., P00533) |
| Entry Name | `uniProtkbId` | Entry name (e.g., EGFR_HUMAN) |
| Entry Type | `entryType` | Swiss-Prot or TrEMBL |
| Secondary Accessions | `secondaryAccessions` | Historical accessions |

## Protein Description

| Field | JSON Path | Description |
|-------|-----------|-------------|
| Recommended Name | `proteinDescription.recommendedName.fullName.value` | Primary protein name |
| Short Name | `proteinDescription.recommendedName.shortName` | Abbreviated name |
| EC Numbers | `proteinDescription.recommendedName.ecNumbers` | Enzyme Commission numbers |
| Alternative Names | `proteinDescription.alternativeNames` | Alternative protein names |
| Flags | `proteinDescription.flag` | e.g., "Precursor" |

## Gene Information

| Field | JSON Path | Description |
|-------|-----------|-------------|
| Primary Gene Name | `genes[0].geneName.value` | Primary gene symbol |
| Gene Synonyms | `genes[0].synonyms` | Alternative gene names |
| ORF Names | `genes[0].orfNames` | Open reading frame names |

## Organism

| Field | JSON Path | Description |
|-------|-----------|-------------|
| Scientific Name | `organism.scientificName` | Scientific name |
| Common Name | `organism.commonName` | Common name |
| Taxon ID | `organism.taxonId` | NCBI Taxonomy ID |
| Lineage | `organism.lineage` | Taxonomic lineage |

## Sequence

| Field | JSON Path | Description |
|-------|-----------|-------------|
| Sequence | `sequence.value` | Amino acid sequence |
| Length | `sequence.length` | Sequence length (aa) |
| Mass | `sequence.molWeight` | Molecular weight (Da) |
| CRC64 Checksum | `sequence.crc64Checksum` | Sequence checksum |
| Modified | `sequence.modified` | Last modification date |

## Comments (Annotations)

### Function
```json
{
  "commentType": "FUNCTION",
  "texts": [{"value": "Protein function description..."}]
}
```

### Disease
```json
{
  "commentType": "DISEASE",
  "disease": {
    "diseaseId": "Lung cancer",
    "acronym": "LNCR",
    "description": "Disease description...",
    "diseaseCrossReference": {"database": "MIM", "id": "211980"}
  }
}
```

### Subcellular Location
```json
{
  "commentType": "SUBCELLULAR LOCATION",
  "subcellularLocations": [
    {"location": {"value": "Cell membrane"}}
  ]
}
```

### Other Comment Types

| Type | Description |
|------|-------------|
| CATALYTIC ACTIVITY | Enzyme reactions |
| COFACTOR | Required cofactors |
| ACTIVITY REGULATION | Regulation of activity |
| SUBUNIT | Subunit structure |
| INTERACTION | Protein interactions |
| TISSUE SPECIFICITY | Tissue expression |
| DEVELOPMENTAL STAGE | Developmental expression |
| INDUCTION | Induction conditions |
| PTM | Post-translational modifications |
| ALTERNATIVE PRODUCTS | Isoforms |
| PHARMACOLOGICAL | Drug target info |
| MISCELLANEOUS | Other annotations |
| SIMILARITY | Sequence similarities |
| CAUTION | Important notes |

## Features (Sequence Annotations)

| Type | Description |
|------|-------------|
| Domain | Protein domain |
| Repeat | Repeated sequence |
| Region | Functional region |
| Motif | Short motif |
| Active site | Catalytic residue |
| Binding site | Ligand binding |
| Site | Other site |
| Transmembrane | Transmembrane region |
| Intramembrane | Intramembrane region |
| Signal | Signal peptide |
| Peptide | Processed peptide |
| Propeptide | Propeptide region |
| Chain | Mature chain |
| Cross-link | Cross-link |
| Disulfide bond | Disulfide bond |
| Glycosylation | Glycosylation site |
| Lipidation | Lipid attachment |
| Modified residue | Other modification |
| Natural variant | Natural variant |
| Mutagenesis | Mutagenesis site |
| Sequence conflict | Sequence conflict |
| Sequence uncertainty | Uncertain region |
| Helix | Secondary structure |
| Strand | Secondary structure |
| Turn | Secondary structure |

### Feature JSON Structure
```json
{
  "type": "Domain",
  "description": "Receptor-binding domain",
  "location": {
    "start": {"value": 319, "modifier": "EXACT"},
    "end": {"value": 541, "modifier": "EXACT"}
  }
}
```

## Keywords

Keywords categorize proteins by function, PTM, subcellular location, etc.

```json
{
  "keywords": [
    {"name": "3D-structure", "id": "KW-0001"},
    {"name": "Glycoprotein", "id": "KW-0325"}
  ]
}
```

### Common Keyword Categories

| Category | Examples |
|----------|----------|
| Molecular function | Kinase, Receptor, Transferase |
| Biological process | Apoptosis, Cell cycle, DNA repair |
| Cellular component | Membrane, Nucleus, Cytoplasm |
| PTM | Phosphoprotein, Glycoprotein, Acetylation |
| Technical term | 3D-structure, Reference proteome |

## Cross-References

External database links in `uniProtKBCrossReferences`:

| Database | Description |
|----------|-------------|
| PDB | 3D structures |
| AlphaFoldDB | Predicted structures |
| HGNC | Human gene nomenclature |
| Ensembl | Genome database |
| RefSeq | NCBI reference sequences |
| GeneID | NCBI Gene |
| KEGG | Pathway database |
| GO | Gene Ontology |
| Pfam | Protein families |
| InterPro | Integrated protein signatures |
| SMART | Protein domains |
| PROSITE | Protein patterns |
| DrugBank | Drug database |
| ChEMBL | Bioactive molecules |
| ClinVar | Clinical variants |
| dbSNP | SNP database |

### Cross-Reference JSON Structure
```json
{
  "database": "PDB",
  "id": "1IVO",
  "properties": [
    {"key": "Method", "value": "X-ray"},
    {"key": "Resolution", "value": "2.80 Å"}
  ]
}
```

## References

Literature citations in `references`:
- Citation details (authors, title, journal)
- PubMed ID
- DOI
- Evidence mapping to annotations

## Extra Attributes

| Field | Description |
|-------|-------------|
| `annotationScore` | Overall annotation quality (1-5) |
| `uniParcId` | UniParc cross-reference |
| `proteinExistence` | Evidence for protein existence |

## JSON API Response Structure

```
{
  "entryType": "UniProtKB reviewed (Swiss-Prot)",
  "primaryAccession": "P00533",
  "uniProtkbId": "EGFR_HUMAN",
  "organism": {...},
  "proteinDescription": {...},
  "genes": [...],
  "comments": [...],
  "features": [...],
  "keywords": [...],
  "references": [...],
  "uniProtKBCrossReferences": [...],
  "sequence": {...},
  "extraAttributes": {...}
}
```


path: references/query_fields.md
content: # UniProt Query Fields Reference

Complete list of query fields for searching UniProtKB.

## Basic Query Syntax

```
field:value
field:value1 OR field:value2
field1:value1 AND field2:value2
```

## Gene and Protein

| Field | Example | Description |
|-------|---------|-------------|
| `gene` | `gene:BRCA` | Gene name (partial match) |
| `gene_exact` | `gene_exact:EGFR` | Exact gene name match |
| `gene_synonym` | `gene_synonym:HER1` | Gene synonym |
| `protein_name` | `protein_name:kinase` | Protein name |
| `protein_full_name` | `protein_full_name:"Epidermal growth factor receptor"` | Full protein name |
| `ec` | `ec:2.7.10.1` | Enzyme Commission number |

## Organism

| Field | Example | Description |
|-------|---------|-------------|
| `organism_id` | `organism_id:9606` | NCBI Taxonomy ID |
| `organism` | `organism:"Homo sapiens"` | Organism name |
| `host_id` | `host_id:9606` | Host organism (for viral proteins) |
| `host_name` | `host_name:"Homo sapiens"` | Host organism name |

### Common Organism IDs

| Organism | Taxon ID |
|----------|----------|
| Human (Homo sapiens) | 9606 |
| Mouse (Mus musculus) | 10090 |
| Rat (Rattus norvegicus) | 10116 |
| Zebrafish (Danio rerio) | 7955 |
| Fruit fly (Drosophila melanogaster) | 7227 |
| Nematode (C. elegans) | 6239 |
| Yeast (S. cerevisiae) | 559292 |
| E. coli K-12 | 83333 |
| Arabidopsis thaliana | 3702 |
| SARS-CoV-2 | 2697049 |
| SARS-CoV | 694009 |
| HIV-1 | 11676 |

## Sequence Properties

| Field | Example | Description |
|-------|---------|-------------|
| `length` | `length:[100 TO 500]` | Sequence length range |
| `mass` | `mass:[10000 TO 50000]` | Molecular mass (Da) |
| `sequence` | `sequence:MKFLILLFNI` | Sequence search |
| `checksum` | `checksum:A836172E9D` | Sequence CRC64 checksum |

## Annotation

| Field | Example | Description |
|-------|---------|-------------|
| `keyword` | `keyword:Kinase` | UniProt keyword |
| `keyword_id` | `keyword_id:KW-0418` | Keyword ID |
| `cc_function` | `cc_function:apoptosis` | Function comment |
| `cc_disease` | `cc_disease:diabetes` | Disease annotation |
| `cc_subcellular_location` | `cc_subcellular_location:membrane` | Subcellular location |
| `cc_tissue_specificity` | `cc_tissue_specificity:liver` | Tissue specificity |
| `domain` | `domain:kinase` | Domain annotation |
| `family` | `family:"protein kinase"` | Protein family |

## Post-Translational Modifications

| Field | Example | Description |
|-------|---------|-------------|
| `ft_mod_res` | `ft_mod_res:"Phosphoserine"` | Modified residue |
| `ft_carbohyd` | `ft_carbohyd:true` | Glycosylation site |
| `ft_lipid` | `ft_lipid:true` | Lipidation site |
| `ft_disulfid` | `ft_disulfid:true` | Disulfide bond |

## Structural Features

| Field | Example | Description |
|-------|---------|-------------|
| `ft_transmem` | `ft_transmem:true` | Transmembrane region |
| `ft_signal` | `ft_signal:true` | Signal peptide |
| `ft_helix` | `ft_helix:true` | Helical region |
| `ft_strand` | `ft_strand:true` | Beta strand |
| `ft_turn` | `ft_turn:true` | Turn region |

## Entry Properties

| Field | Example | Description |
|-------|---------|-------------|
| `reviewed` | `reviewed:true` | Swiss-Prot (reviewed) only |
| `created` | `created:[2020-01-01 TO *]` | Entry creation date |
| `modified` | `modified:[2023-01-01 TO *]` | Last modified date |
| `version` | `version:123` | Entry version number |
| `annotation_score` | `annotation_score:5` | Annotation quality (1-5) |

## Cross-References

| Field | Example | Description |
|-------|---------|-------------|
| `database:(type:PDB)` | `database:(type:PDB)` | Has PDB entry |
| `database:(type:PDB AND 1ABC)` | Specific PDB ID |
| `xref_pdb` | `xref_pdb:1ABC` | PDB cross-reference |
| `xref_hgnc` | `xref_hgnc:HGNC:3236` | HGNC ID |
| `xref_ensembl` | `xref_ensembl:ENSP00000275493` | Ensembl ID |
| `xref_refseq` | `xref_refseq:NP_005219` | RefSeq ID |

## Query Examples

### Find all human kinases
```
keyword:Kinase AND organism_id:9606 AND reviewed:true
```

### Find SARS-CoV-2 proteins
```
organism_id:2697049
```

### Find proteins with transmembrane domains
```
ft_transmem:true AND organism_id:9606
```

### Find proteins involved in diabetes
```
cc_disease:diabetes AND organism_id:9606
```

### Find secreted proteins in human
```
ft_signal:true AND cc_subcellular_location:secreted AND organism_id:9606
```

### Find membrane proteins between 300-500 aa
```
ft_transmem:true AND length:[300 TO 500] AND organism_id:9606
```

## Operators

| Operator | Example | Description |
|----------|---------|-------------|
| AND | `gene:BRCA AND organism_id:9606` | Both conditions |
| OR | `gene:EGFR OR gene:HER2` | Either condition |
| NOT | `kinase NOT receptor` | Exclude |
| * | `gene:BRCA*` | Wildcard |
| [x TO y] | `length:[100 TO 500]` | Range |

## References

- UniProt Query Syntax: https://www.uniprot.org/help/text-search
- Query Fields: https://www.uniprot.org/help/query-fields


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)