
String Database
- 1.3k installs
- 2.6k repo stars
- Updated July 7, 2026
- google-deepmind/science-skills
string-database is a Claude Code skill that runs Gene Ontology, KEGG, Pfam, and protein-protein interaction statistical enrichment through STRING CLI commands for developers who analyze protein sets in terminal or agent
About
string-database is a google-deepmind/science-skills guide for functional and PPI enrichment using scripts/string_cli.py invoked with uv run. The enrichment command accepts protein identifiers and a species NCBI taxon ID, then writes TSV output with category, term, p_value, fdr, and description columns covering GO, KEGG, Pfam, InterPro, and SMART terms. A separate ppi-enrichment command tests whether a network has significantly more interactions than a background proteome. Developers reach for string-database when they need reproducible enrichment statistics from STRING inside automated research pipelines rather than manual web searches. Commands are designed for copy-paste terminal execution or agent orchestration with explicit output paths such as /tmp/enrichment.tsv.
- Four specialized commands: enrichment, ppi-enrichment, functional-terms, functional-annotation
- Outputs enriched terms with p_value, fdr, description for GO/KEGG/Pfam/InterPro/SMART
- PPI network significance testing against background proteome distribution
- Retrieves all functional annotations or proteins linked to a term like "Melanoma" or a GO ID
- Species-aware analysis supporting 511145 (E. coli), 10090 (mouse), 9606 (human) and others
String Database by the numbers
- 1,259 all-time installs (skills.sh)
- +165 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #263 of 2,064 Data Science & ML skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/google-deepmind/science-skills --skill string-databaseAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.3k |
|---|---|
| repo stars | ★ 2.6k |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 7, 2026 |
| Repository | google-deepmind/science-skills ↗ |
How do you run GO and KEGG enrichment from STRING?
Run Gene Ontology, KEGG, Pfam, and PPI statistical enrichment analyses directly from the terminal or via an agent.
Who is it for?
Developers or bioinformatics engineers running STRING-based functional enrichment and PPI statistics from terminal or agent-driven pipelines.
Skip if: Teams that only need Human Protein Atlas expression lookups or who lack a uv/Python environment for STRING CLI scripts.
When should I use this skill?
The user asks for STRING enrichment, GO or KEGG pathway analysis, Pfam term enrichment, or PPI network significance testing.
What you get
Enrichment TSV files with category, term, p_value, fdr, and description plus PPI significance results.
- enrichment.tsv
- PPI enrichment statistics
By the numbers
- Enrichment TSV includes 5 output fields: category, term, p_value, fdr, description
- Covers 6 annotation sources: GO, KEGG, Pfam, InterPro, and SMART
Files
STRING Database Skill
This skill allows you to query the STRING database programmatically using a bundled Python CLI wrapper.
Prerequisites
1. `uv`: Read the uv skill and follow its Setup instructions to ensure uv is installed and on PATH. 2. User Notification: If LICENSE_NOTIFICATION.txt does not already exist in this skill directory then (1) prominently notify the user to check the terms at https://string-db.org/cgi/access, then (2) create the file recording the notification text and timestamp.
Core Rules
1. MANDATORY: Ask for Species First: The STRING API requires NCBI Taxon IDs. You MUST NOT guess or assume a species. If the user does not explicitly state a species or Taxon ID, you MUST stop and ask: "Which species are you interested in? I need the NCBI Taxon ID to proceed." Even for well-known proteins like TP53, BRCA1, or MDM2 that are commonly associated with human studies, you MUST still ask — do not default to Human. 2. Never print output to stdout: The --output <file.tsv> is required. Never read large outputs into context. Instead use jq, python or file operations (grep, head) to process large output. 3. Map Identifiers first: If you only have common gene names (e.g., 'TP53'), map them to STRING IDs first as this guarantees much faster server responses. Use the map command for this. 4. Notification: If this skill is used, ensure this is mentioned in the output.
Tool Execution
The CLI is at scripts/string_cli.py and should be run using uv run:
uv run scripts/string_cli.py <command> [options] --output /tmp/out.tsvFeature Domains (Progressive Disclosure)
Read the following reference files based on the user's request:
- [Mapping Identifiers](references/mapping.md) - Map common protein names
to STRING IDs.
- [Interactions & Network](references/interactions.md) - Find interacting
proteins, network topologies, mediators, homology, and visual network images.
- [Enrichment & Functional Annotations](references/enrichment.md) -
Analyze pathway enrichment (GO, KEGG, Pfam), PPI significance, or find all proteins associated with a specific term (e.g. Melanoma).
- [Values/Ranks Enrichment](references/valuesranks.md) - Submit full
experimental datasets (e.g., logFC, p-values) for rank-based enrichment analysis using the async background API.
To begin, read the reference file most appropriate to the current task to discover the correct CLI command.
Functional & PPI Enrichment
Use these commands for determining Gene Ontology, KEGG pathway enrichment, and general Protein-Protein Interaction (PPI) statistical enrichment.
Command: enrichment
Identifies enriched functional terms (GO, KEGG, Pfam, InterPro, SMART) for a set of proteins.
uv run scripts/string_cli.py enrichment \
--identifiers trpA trpB trpC trpE \
--species 511145 \
--output /tmp/enrichment.tsvOutput fields: category, term, p_value, fdr (False Discovery Rate), description.
Command: ppi-enrichment
Determines if a network has significantly more interactions than expected by chance, comparing it to the background proteome-wide distribution.
uv run scripts/string_cli.py ppi-enrichment \
--identifiers Trp53 Mdm2 Cdkn1a Cdk2 Cdk4 Ccnd1 Rb1 E2f1 \
--species 10090 \
--output /tmp/ppi_enrichment.tsvOutput fields: number_of_nodes, number_of_edges, expected_number_of_edges, p_value.
Command: functional-terms
Searches for all proteins associated with a specific functional term or disease (e.g., "Melanoma" or "GO:0008543"). Note: This API takes `--term_text` instead of `--identifiers`.
uv run scripts/string_cli.py functional-terms \
--term_text "Melanoma" \
--species 9606 \
--output /tmp/melanoma_proteins.tsvCommand: functional-annotation
Retrieves all functional annotations (not just enriched ones) for the given proteins.
uv run scripts/string_cli.py functional-annotation \
--identifiers CDC28 CLB1 CLB2 CLB3 CKS1 \
--species 4932 \
--output /tmp/annotations.tsvInteractions & Networks
Use these commands to retrieve protein interaction networks, topologies, mediators, and homology scores.
Command: network
Retrieves interactions between the provided input proteins. If --add_nodes is provided, it extends the neighborhood.
uv run scripts/string_cli.py network \
--identifiers Trp53 Mdm2 \
--species 10090 \
--add_nodes 10 \
--network_type physical \
--output /tmp/p53_neighborhood.tsv- Options:
-
--required_score(0-1000 threshold, e.g. 400 for medium confidence) -
--network_type(functionalorphysical) -
--add_nodes(number of closely interacting proteins to add to the
network).
- Output columns:
score(combined confidence),escore(experimental
evidence), dscore (database), nscore (neighborhood), fscore (fusion), pscore (phylogenetic), tscore (textmining), ascore (coexpression).
Command: partners
Gets the top interaction partners against the entire database for the provided proteins.
uv run scripts/string_cli.py partners \
--identifiers BRCA1 \
--species 9606 \
--limit 10 \
--output /tmp/partners.tsvCommand: image
Generates a visual map of the network. Output can be a PNG or SVG.
uv run scripts/string_cli.py image \
--identifiers Trp53 Mdm2 Atm Atr Chek2 Brca1 Cdkn1a \
--species 10090 \
--format highres_image \
--output /tmp/p53_pathway_network.pngCommand: homology
Gets Smith-Waterman homology (similarity) scores between the input proteins.
uv run scripts/string_cli.py homology \
--identifiers CDK1 CDK2 \
--species 9606 \
--output /tmp/homology.tsvCommand: homology-best
Gets best homology similarity hits between the input proteins and proteins in other specified species. Note: Target species must be exact comma-separated taxon IDs with no spaces.
uv run scripts/string_cli.py homology-best \
--identifiers CDK1 \
--species 9606 \
--species_b 10090,7227 \
--output /tmp/best_homology.tsvMapping Identifiers
Before querying for networks or enrichments, it is highly recommended to map common protein names (e.g., "TP53", "CDK2") to STRING's internal identifiers. Using mapped identifiers guarantees much faster server responses.
Command: map
uv run scripts/string_cli.py map \
--identifiers sna twi dl \
--species 7227 \
--output /tmp/mapped_ids.tsvParameters:
-
--identifiers: Space-separated list of protein names or UniProt IDs. -
--species: NCBI Taxon ID (e.g.,9606for Human). -
--output: File to save the TSV results.
Output Fields:
The resulting TSV contains columns like queryItem, stringId, ncbiTaxonId, taxonName, preferredName, and annotation.
Values/Ranks Enrichment API
This API performs GSEA-like enrichment on full datasets (e.g., complete differential expression analysis or ranking results) to find pathways enriched at the top, bottom, or both ends of your value distribution. This is an asynchronous API process.
Step 1: Get an API Key (Once per session)
Generate a free, anonymous key required to submit full datasets.
uv run scripts/string_cli.py valuesranks-key --output /tmp/api_key.jsonRead the JSON output to get the "api_key" value.
Step 2: Submit the Job
Prepare a tab-separated text file. The script will automatically strip headers and skip lines that don't have at least two tab-separated columns or lack a valid numeric value in the second column.
- Column 1: Protein identifier (STRING IDs are fastest).
- Column 2: Associated value (e.g., p-value, fold-change, rank).
uv run scripts/string_cli.py valuesranks-submit \
--api_key YOUR_EXTRACTED_KEY \
--input_file /tmp/my_data.tsv \
--species 10090 \
--output /tmp/submit_response.jsonRead the response JSON to extract the "job_id".
Step 3: Check Job Status & Download
You can either poll the status occasionally, or use the --wait flag to automatically poll the job and download the final TSV result once successful.
uv run scripts/string_cli.py valuesranks-status \
--api_key YOUR_EXTRACTED_KEY \
--job_id EXTRACTED_JOB_ID \
--wait \
--output /tmp/job_results.tsvWithout --wait, wait until "status": "success" is returned. When completed, the JSON will contain a "download_url" where you can fetch the final TSV enrichment results.
# 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.
"""STRING Database CLI Wrapper.
Provides access to STRING API v12.0 endpoints.
"""
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "scienceskillscommon",
# ]
# [tool.uv.sources]
# scienceskillscommon = { path = "../../scienceskillscommon" }
# ///
import argparse
import json
import os
import sys
import time
from typing import Any
import urllib.parse
import urllib.request
from science_skills.skills.scienceskillscommon import http_client
CALLER_IDENTITY = 'google-science-skills'
URL_TEMPLATE = (
'https://version-{api_version}-0.string-db.org/api/{format_type}/{endpoint}'
)
_CLIENT = None
class _DummyResponse:
def __init__(self, content):
self.content = content
def json(self):
return json.loads(self.content.decode('utf-8'))
def call_api(url: str, params: dict[str, Any], output_file: str):
"""Calls the STRING API and writes the response to a file.
Args:
url: The API URL to call.
params: A dictionary of parameters to send with the request.
output_file: The file path where the API response will be written.
Returns:
A dummy response object with a .json() method if the call is successful.
Exits the program on API errors.
"""
params['caller_identity'] = CALLER_IDENTITY
data = urllib.parse.urlencode(params).encode('utf-8')
assert _CLIENT is not None
content = _CLIENT.fetch_bytes(url, method='POST', data=data)
os.makedirs(os.path.dirname(os.path.abspath(output_file)), exist_ok=True)
with open(output_file, 'wb') as f:
f.write(content)
print(f'Success: Output written to {output_file}')
return _DummyResponse(content)
def main():
parser = argparse.ArgumentParser(description='STRING Database CLI wrapper')
parser.add_argument(
'--api_version', default='12', help='STRING API version (default: 12)'
)
subparsers = parser.add_subparsers(dest='command', required=True)
# Common parent parser for output
parent_out = argparse.ArgumentParser(add_help=False)
parent_out.add_argument(
'--output', required=True, help='File to write output to'
)
# Common parent for identifiers and species
parent_id = argparse.ArgumentParser(add_help=False)
parent_id.add_argument(
'--identifiers',
nargs='+',
required=True,
help='List of protein names/IDs',
)
parent_id.add_argument(
'--species', type=int, help='NCBI Taxon ID (e.g. 9606 for human)'
)
# map
p_map = subparsers.add_parser(
'map',
parents=[parent_out, parent_id],
help='Map identifiers to STRING IDs',
)
p_map.add_argument('--echo_query', type=int, choices=[0, 1], default=1)
# network
p_net = subparsers.add_parser(
'network', parents=[parent_out, parent_id], help='Get interaction network'
)
p_net.add_argument('--required_score', type=int, help='0-1000 threshold')
p_net.add_argument(
'--network_type', choices=['functional', 'physical'], default='functional'
)
p_net.add_argument('--add_nodes', type=int, help='Number of nodes to add')
# image
p_img = subparsers.add_parser(
'image', parents=[parent_out, parent_id], help='Get network image'
)
p_img.add_argument(
'--format', choices=['image', 'highres_image', 'svg'], default='image'
)
p_img.add_argument(
'--network_flavor',
choices=['evidence', 'confidence', 'actions'],
default='evidence',
)
p_img.add_argument('--add_color_nodes', type=int)
# partners
p_part = subparsers.add_parser(
'partners',
parents=[parent_out, parent_id],
help='Get interaction partners',
)
p_part.add_argument('--limit', type=int, help='Max partners per protein')
# homology
subparsers.add_parser(
'homology', parents=[parent_out, parent_id], help='Get homology scores'
)
# homology_best
p_homb = subparsers.add_parser(
'homology-best', parents=[parent_out, parent_id], help='Best homology hit'
)
p_homb.add_argument(
'--species_b', help='Comma-separated target species (e.g., 10090,7227)'
)
# enrichment
subparsers.add_parser(
'enrichment',
parents=[parent_out, parent_id],
help='Functional enrichment',
)
# functional_annotation
p_fa = subparsers.add_parser(
'functional-annotation',
parents=[parent_out, parent_id],
help='Functional annotations',
)
p_fa.add_argument('--allow_pubmed', type=int, choices=[0, 1], default=0)
# functional_terms (uses term_text instead of identifiers)
p_ft = subparsers.add_parser(
'functional-terms', parents=[parent_out], help='Search proteins by term'
)
p_ft.add_argument(
'--term_text', required=True, help='e.g. Melanoma, GO:0008543'
)
p_ft.add_argument('--species', type=int, default=9606)
# ppi_enrichment
subparsers.add_parser(
'ppi-enrichment', parents=[parent_out, parent_id], help='PPI enrichment'
)
# version
subparsers.add_parser(
'version', parents=[parent_out], help='Get STRING version'
)
# valuesranks API key
subparsers.add_parser(
'valuesranks-key',
parents=[parent_out],
help='Get API key for Values/Ranks',
)
# valuesranks submit
p_vr_sub = subparsers.add_parser(
'valuesranks-submit', parents=[parent_out], help='Submit Values/Ranks job'
)
p_vr_sub.add_argument('--api_key', required=True)
p_vr_sub.add_argument(
'--input_file', required=True, help='Tab-separated file of ID and value'
)
p_vr_sub.add_argument('--species', type=int, required=True)
p_vr_sub.add_argument('--ge_fdr', type=float, default=0.01)
# valuesranks status
p_vr_stat = subparsers.add_parser(
'valuesranks-status', parents=[parent_out], help='Check job status'
)
p_vr_stat.add_argument('--api_key', required=True)
p_vr_stat.add_argument('--job_id', help='Omit to list all jobs')
p_vr_stat.add_argument(
'--wait',
action='store_true',
help='Wait for job to complete and download final TSV',
)
args = parser.parse_args()
global _CLIENT
base_url = f'https://version-{args.api_version}-0.string-db.org/'
_CLIENT = http_client.HttpClient(base_url, qps=1)
def _url(endpoint, *, format_type='tsv'):
return URL_TEMPLATE.format(
api_version=args.api_version, format_type=format_type, endpoint=endpoint
)
requires_species = [
'map',
'network',
'image',
'partners',
'homology',
'homology-best',
'enrichment',
'functional-annotation',
'ppi-enrichment',
]
if args.command in requires_species and (
not hasattr(args, 'species') or args.species is None
):
print(
f"Error: Command '{args.command}' requires --species", file=sys.stderr
)
sys.exit(1)
params = {}
if hasattr(args, 'identifiers') and args.identifiers:
params['identifiers'] = '\r'.join(args.identifiers)
if hasattr(args, 'species') and args.species is not None:
params['species'] = args.species
if args.command == 'map':
params['echo_query'] = args.echo_query
call_api(_url('get_string_ids'), params, args.output)
elif args.command == 'network':
if args.required_score:
params['required_score'] = args.required_score
params['network_type'] = args.network_type
if args.add_nodes:
params['add_nodes'] = args.add_nodes
call_api(_url('network'), params, args.output)
elif args.command == 'image':
params['network_flavor'] = args.network_flavor
if args.add_color_nodes:
params['add_color_nodes'] = args.add_color_nodes
call_api(_url('network', format_type=args.format), params, args.output)
elif args.command == 'partners':
if args.limit:
params['limit'] = args.limit
call_api(_url('interaction_partners'), params, args.output)
elif args.command == 'homology-best':
if args.species_b:
params['species_b'] = args.species_b.replace(',', '\r')
call_api(_url('homology_best'), params, args.output)
elif args.command == 'functional-annotation':
params['allow_pubmed'] = args.allow_pubmed
call_api(_url('functional_annotation'), params, args.output)
elif args.command == 'functional-terms':
params['term_text'] = args.term_text
call_api(_url('functional_terms'), params, args.output)
elif args.command == 'valuesranks-key':
call_api(_url('get_api_key', format_type='json'), {}, args.output)
elif args.command in ['enrichment', 'homology', 'ppi-enrichment', 'version']:
call_api(_url(args.command.replace('-', '_')), params, args.output)
elif args.command == 'valuesranks-submit':
params['api_key'] = args.api_key
params['species'] = args.species
params['ge_fdr'] = args.ge_fdr
try:
with open(args.input_file, 'r') as f:
lines = f.readlines()
valid_lines = []
for i, line in enumerate(lines):
parts = line.strip().split('\t')
if len(parts) >= 2:
try:
float(parts[1])
valid_lines.append(f'{parts[0]}\t{parts[1]}')
except ValueError:
print(
f"Skipping line {i+1} as it doesn't contain a valid numeric"
f' value: {line.strip()}',
file=sys.stderr,
)
elif line.strip():
print(
f"Skipping line {i+1} as it doesn't contain at least 2"
f' tab-separated columns: {line.strip()}',
file=sys.stderr,
)
params['identifiers'] = '\r'.join(valid_lines)
if not params['identifiers']:
print('Error: No valid data found in input file.', file=sys.stderr)
sys.exit(1)
except OSError as e:
print(f'Error reading {args.input_file}: {e}', file=sys.stderr)
sys.exit(1)
url = _url('valuesranks_enrichment_submit', format_type='json')
call_api(url, params, args.output)
elif args.command == 'valuesranks-status':
url = _url('valuesranks_enrichment_status', format_type='json')
params['api_key'] = args.api_key
if args.job_id:
params['job_id'] = args.job_id
if not args.wait or not args.job_id:
call_api(url, params, args.output)
return
# Loop until the call completes.
while True:
response = call_api(url, params, args.output)
try:
data = response.json()
except ValueError:
print('Error parsing status response.', file=sys.stderr)
sys.exit(1)
if not isinstance(data, list) or len(data) == 0:
print(f'Unexpected response format: {data}', file=sys.stderr)
sys.exit(1)
job_info = data[0]
status = job_info.get('status')
if status == 'success':
if download_url := job_info.get('download_url'):
print(
f'Job success. Downloading from {download_url} to {args.output}'
)
req = urllib.request.Request(download_url)
with urllib.request.urlopen(req) as dl_resp:
content = dl_resp.read()
with open(args.output, 'wb') as f:
f.write(content)
break
else:
print('Success but no download URL found.', file=sys.stderr)
break
elif status == 'running' or status == 'queued':
print(f'Job still {status}... waiting 5 seconds.')
time.sleep(5)
else:
print(f'Job failed or unknown status: {status}', file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()
Related skills
How it compares
Choose string-database for GO, KEGG, and PPI statistical enrichment via STRING CLI rather than HPA expression query construction.
FAQ
What does the STRING enrichment command output?
The string-database skill's enrichment command produces a TSV with category, term, p_value, fdr, and description fields covering GO, KEGG, Pfam, InterPro, and SMART functional terms for supplied protein identifiers.
How do you invoke STRING enrichment from the terminal?
Run uv run scripts/string_cli.py enrichment with --identifiers, --species, and --output flags. The string-database skill example uses identifiers trpA trpB trpC trpE and species 511145 writing to /tmp/enrichment.tsv.
Is String Database safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.