Generated banner for Foldseek Structural Search
Generated logo for Foldseek Structural Search

Foldseek Structural Search

Google DeepMindOfficialPublished by Google DeepMind

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

From the author

Performs 3D structural searches of proteins against various databases (PDB, AlphaFold, CATH, MGnify, etc.) using the Foldseek API. Use ONLY when the user provides a physical 3D coordinate file (.cif, .mmcif, or .pdb) and wants to find structurally similar proteins. Do NOT use if the user only provides a protein sequence, gene name, or UniProt ID.
— the description by Google DeepMind

AutoGineer summary

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

This skill enables users to perform structural similarity searches for proteins by submitting a 3D coordinate file (such as .pdb, .cif, or .mmcif) to the Foldseek API. It compares the query structure against established databases like PDB, AlphaFold, and CATH to identify structural homologues. The skill processes the search, interprets key alignment metrics such as probability and query coverage, and provides a functional analysis of the top matches, saving results in both human-readable Markdown and machine-readable JSON formats.

What it does

  • Submits 3D protein structure files to the Foldseek API for structural comparison.
  • Supports searches against databases including PDB, AlphaFold, CATH, and MGnify.
  • Generates a Markdown table summarizing top structural hits and alignment metrics.
  • Saves detailed search results into a local JSON file for further analysis.
  • Interprets alignment metrics like probability, query coverage, and E-value.
  • Synthesizes functional information from structural matches to suggest potential protein roles.

When to use it

  • When you have a physical 3D protein structure file and need to find structurally similar proteins.
  • When you need to identify potential protein functions based on structural homology.
  • When you want to compare a protein structure against specific databases like AlphaFold or PDB.
  • When you require a structured report of structural matches for further research.

Advantages

  • Automates the submission and retrieval process for structural protein searches.
  • Provides clear interpretation of structural alignment metrics.
  • Enables functional analysis by identifying homologous protein families.
  • Produces both human-readable summaries and data-rich files for downstream workflows.

Usage examples

“Find structural matches for my protein structure file located at ./data/protein_model.pdb using the AlphaFold database.”
The agent validates the file, queries the AlphaFold database, and returns a summary of the top structural matches with their functional annotations.
“Search for structural homologues of structure.cif in the PDB and CATH databases.”
The agent performs the search against the specified databases and provides a Markdown table detailing the structural similarity and functional context of the results.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/google-deepmind/foldseek-structural-search/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip foldseek-structural-search.zip -d .agents/skills

Documentation by Google DeepMind

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/foldseek_structural_search_LICENSE.txt does not already exist in the workspace root directory then (1) prominently notify the user to check the terms at https://search.foldseek.com/search and https://github.com/steineggerlab/foldseek, then (2) create the file recording the notification text and timestamp.

Goal

Submit a user-provided 3D protein structure file (.cif, .mmcif, or .pdb) to the Foldseek web server API to find structurally similar proteins. Report the top structural hits, interpret key alignment metrics, summarize the inferred protein functions, save the Markdown-formatted table to a .md file, and save the full detailed results to a local JSON file.

Core Rules

  • File Requirement: This tool absolutely cannot search by sequence, name, or accession ID. It strictly requires a .pdb, .cif, or .mmcif file path.

  • Strict Validation: Never bypass the input validation or the database allowlist check.

  • Do Not Parse the JSON: Rely entirely on the generated .md file for your immediate summary. The JSON is saved purely for subsequent, specialized tool use.

  • No Raw Parsing: Do not attempt to parse or read the raw 3D coordinates yourself; always pass the file to the script.

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

Instructions

  1. Strict Input Validation: Verify that the user has explicitly provided a valid path to a .cif, .mmcif, or .pdb file in their workspace.

    • If the user provided a protein name, an amino acid sequence, or an accession ID (e.g., a UniProt ID) but NO downloaded structure file, halt immediately. Do not run the script.

    • Inform the user that Foldseek requires a physical 3D coordinate file, and suggest downloading the structure first (e.g., using the AlphaFold fetch tool).

  2. Database Validation: Check if the user requested specific databases to search.

    • Allowed List: afdb50, afdb-swissprot, pdb100, BFVD, mgnify_esm30, cath50, gmgcl_id, bfmd, afdb-proteome.

    • If the user requests a database NOT on this list, halt immediately. Do not run the script. Inform the user that the database is unsupported and provide them with the allowed list.

  3. Generate File Names: Generate descriptive output file names for both the JSON data and the Markdown table based on the input file (e.g., proteinA_foldseek_results.json and proteinA_foldseek_results.md).

  4. Execute the python script based on the user's request, redirecting the standard output into your generated .md file:

    • Default (No databases specified): uv run scripts/search.py <path-to-file> -o <generated-filename.json> > <generated-filename.md>

    • Custom (Valid databases specified): uv run scripts/search.py <path-to-file> -o <generated-filename.json> --databases <db1,db2,db3> > <generated-filename.md>

  5. The script will query the databases, save the full JSON payload, and write a Markdown-formatted table to your specified .md file.

  6. Read the Results: Open and read the newly generated .md file carefully to view the Markdown table.

  7. Interpret the Metrics: Summarize the top 3 to 5 structural matches that have meaningfull annotations for the user. When reporting, assess the match quality using these specific fields:

    • Prob (Probability): Values approaching 1.0 (100%) indicate extreme confidence that the fold is a true structural homologue.

    • Q-Cov (Query Coverage): High percentages mean the match covers the majority of the query protein's overall shape, rather than just a small local motif.

    • E-value & Seq Identity: Use these to provide additional evolutionary context.

  8. Perform Functional Analysis: Analyze the text descriptions embedded within the Target ID column for the reported matches.

    • Explicitly report the specific protein names/functions of the top structural homologues.

    • Provide a synthesized overview summarizing the entire variety of different functions, domains, or protein families found across the whole list of homologues (e.g., "Most hits are portal proteins, but there is also a distinct cluster of viral capsid matches...").

  9. Explicitly inform the user of both newly created files (.json and .md) and their locations so they can be seamlessly used in subsequent analysis steps.

* If the API returns an error or the file is missing, inform the user clearly

and ask them to verify the file path.

Files

scripts/search.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.

"""Runs Foldseek search for a PDB/mmCIF file against different databases."""

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

import argparse
import json
import os
import sys
import time
import uuid

from polite_http import http_client

ALLOWED_DATABASES = [
    "afdb50",
    "afdb-swissprot",
    "pdb100",
    "BFVD",
    "mgnify_esm30",
    "cath50",
    "gmgcl_id",
    "bfmd",
    "afdb-proteome",
]

MAX_ALIGNMENT_HITS = 300
DEFAULT_EVALUE = 1000

# Respect 0.1 queries per second requirement.
CLIENT = http_client.HttpClient("https://search.foldseek.com", qps=1)


def build_multipart_payload(fields, files):
  """Manually constructs a multipart/form-data byte payload."""
  boundary = uuid.uuid4().hex
  body = bytearray()

  # Add standard form fields (handling lists for multiple databases)
  for key, values in fields.items():
    if not isinstance(values, list):
      values = [values]
    for value in values:
      body.extend(f"--{boundary}\r\n".encode("utf-8"))
      body.extend(
          f'Content-Disposition: form-data; name="{key}"\r\n\r\n'.encode(
              "utf-8"
          )
      )
      body.extend(f"{value}\r\n".encode("utf-8"))

  # Add file data
  for key, filepath in files.items():
    filename = os.path.basename(filepath)
    with open(filepath, "rb") as f:
      content = f.read()
    body.extend(f"--{boundary}\r\n".encode("utf-8"))
    body.extend(
        f'Content-Disposition: form-data; name="{key}";'
        f' filename="{filename}"\r\n'.encode("utf-8")
    )
    body.extend(b"Content-Type: application/octet-stream\r\n\r\n")
    body.extend(content)
    body.extend(b"\r\n")

  body.extend(f"--{boundary}--\r\n".encode("utf-8"))
  return boundary, body


# ---------------------------------------------------


def main():
  # Set up command line argument parsing
  parser = argparse.ArgumentParser(
      description="Query Foldseek with a PDB/mmCIF file and save the results."
  )
  parser.add_argument("input_file", help="Path to the mmCIF or PDB file")
  parser.add_argument(
      "-o",
      "--output",
      help="Path to save the output JSON file",
      default="foldseek_results.json",
  )
  parser.add_argument(
      "--databases",
      help="Comma-separated list of databases to search",
      default="pdb100,afdb50",
  )
  args = parser.parse_args()

  file_path = args.input_file
  output_path = args.output
  ticket_url = "https://search.foldseek.com/api/ticket"

  # Process and validate the databases
  selected_dbs = [db.strip() for db in args.databases.split(",")]
  invalid_dbs = [db for db in selected_dbs if db not in ALLOWED_DATABASES]

  if invalid_dbs:
    print(f"[!] Error: Invalid database(s) provided: {', '.join(invalid_dbs)}")
    print(f"[*] Allowed databases are: {', '.join(ALLOWED_DATABASES)}")
    sys.exit(1)

  # Standard headers for all requests
  headers = {
      "User-Agent": "",
      "Accept": "application/json",
  }

  print(f"[*] Submitting {file_path} to Foldseek API...")
  print(f"[*] Searching databases: {', '.join(selected_dbs)}")

  # 1. Submit Ticket
  try:
    if not os.path.exists(file_path):
      raise FileNotFoundError(f"No such file: '{file_path}'")

    boundary, body = build_multipart_payload(
        fields={"mode": "3diaa", "database[]": selected_dbs},
        files={"q": file_path},
    )

    headers["Content-Type"] = f"multipart/form-data; boundary={boundary}"
    response = CLIENT.fetch_json(
        ticket_url, method="POST", data=body, headers=headers, timeout=30
    )
    ticket_id = response["id"]
    print(f"[*] Ticket ID generated: {ticket_id}")

  except FileNotFoundError as e:
    print(f"[!] Error: {e}")
    sys.exit(1)
  except http_client.HttpError as e:
    print(f"[!] API Submission Failed: {e}")
    sys.exit(1)

  # 2. Poll the server until the job finishes
  print("[*] Polling server for completion...")
  while True:
    status_res = CLIENT.fetch_json(
        f"{ticket_url}/{ticket_id}", headers=headers, timeout=20
    )
    status = status_res.get("status")

    if status == "COMPLETE":
      print("\n[*] Job marked as COMPLETE.")
      break
    elif status == "ERROR":
      print("\n[!] Foldseek job failed on the server.")
      sys.exit(1)

    sys.stdout.write(".")
    sys.stdout.flush()
    time.sleep(10)

  time.sleep(3)  # Brief pause to allow server-side result finalization.

  # 3. Fetch and format the results
  print(
      "[*] Fetching results (this might take a moment for large databases)...\n"
  )
  result_url = f"https://search.foldseek.com/api/result/{ticket_id}/0"
  res = CLIENT.fetch_json(result_url, headers=headers, timeout=120)

  # Save the JSON response to the specified local file
  with open(output_path, "w") as json_file:
    json.dump(res, json_file, indent=4)
  print(f"[*] Raw JSON results successfully saved to: {output_path}\n")

  # Output as a Markdown Table for LLM Agent parsing
  print("### All Structural Matches")
  print("| Target ID | Q-Cov | Prob | E-value | Seq Identity | Aln Length |")
  print("|---|---|---|---|---|---|")

  alignments_list = []
  if isinstance(res, dict):
    if "results" in res:
      for result_group in res.get("results", []):
        for db_alignments in result_group.get("alignments", []):
          if isinstance(db_alignments, list):
            alignments_list.extend(db_alignments)
          elif isinstance(db_alignments, dict):
            alignments_list.append(db_alignments)
    elif "alignments" in res:
      alignments_list = res["alignments"]
  elif isinstance(res, list):
    alignments_list = res

  def get_evalue(hit):
    # This field must not be empty as it is used for sorting the results
    # If the field is missing or empty, we assign a default value that is
    # larger than any reasonable E-value (puts it at the end of the sorted
    # list).
    try:
      return float(
          hit.get("eval", hit.get("eValue", hit.get("evalue", DEFAULT_EVALUE)))
      )
    except ValueError:
      return float(DEFAULT_EVALUE)

  # Sort all hits first, then take the top MAX_ALIGNMENT_HITS
  alignments_list.sort(key=get_evalue)
  alignments_list = alignments_list[:MAX_ALIGNMENT_HITS]

  for hit in alignments_list:
    target = hit.get("target", "N/A")

    # --- Corrected Query Coverage Logic ---
    q_len = hit.get("qLen", hit.get("qlen", 0))
    q_start = hit.get("qStartPos", 0)
    q_end = hit.get("qEndPos", 0)

    if q_len > 0 and q_end > q_start:
      # Calculate the actual number of query residues involved, ignoring gaps
      aligned_q_residues = (q_end - q_start) + 1

      # Cap at 100.0% to handle any minor index shifting in the API
      cov_percentage = min((aligned_q_residues / q_len) * 100, 100.0)
      q_cov = f"{cov_percentage:.1f}%"
    else:
      q_cov = "N/A"

    # Extract Probability
    prob = hit.get("prob", hit.get("probability", "N/A"))
    if isinstance(prob, (float, int)):
      prob = f"{prob:.3f}"
    else:
      prob = str(prob)

    evalue = str(hit.get("eval", hit.get("eValue", hit.get("evalue", "N/A"))))
    seq_id = str(
        hit.get("seqId", hit.get("seqIdentity", hit.get("fident", "N/A")))
    )
    aln_len = str(
        hit.get("alnLength", hit.get("alnLen", hit.get("alnlen", "N/A")))
    )

    # Print Markdown row without any truncation
    print(f"| {target} | {q_cov} | {prob} | {evalue} | {seq_id} | {aln_len} |")


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/foldseek_structural_search · 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 or Google DeepMind.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download foldseek-structural-search.zip