Skip to content

Ligand binding site

Detect a bound ligand and report the protein residues that surround it, using the bundled trypsin-benzamidine complex (3PTB).

import molscope as ms

mol = ms.read("examples/data/3ptb.pdb")

# Water and ions are filtered out; only the real ligand remains.
print(mol.ligands())                 # [LigandResidue(A:BEN1, 9 atoms)]

site = mol.binding_site(cutoff=4.5)  # single ligand auto-detected
print(site)                          # BindingSite(A:BEN1: 13 residues < 4.5 A)

for res, dist in zip(site.residues, site.min_distances):
    print(f"{res!s:<10} {dist:.2f} A")
# A:GLY219   2.82
# A:ASP189   2.87   <- benzamidine specificity residue
# A:SER190   3.04
# A:GLY226   3.37
# A:SER195   3.65   <- catalytic serine

For quick figures or reports, convert the site to table-friendly residue records and extract descriptors for only the site residues:

site.to_records()[0]
# {'residue_id': 'A:GLY219', 'chain': 'A', 'resid': 219,
#  'insertion_code': '', 'resname': 'GLY',
#  'min_distance': 2.815..., 'n_atom_contacts': 5}

site.descriptors(mol, preset="pocket-basic")
site.plot(mol, show=False)          # pocket residues plus ligand

Describe the pocket for an LLM

LLMs and RAG pipelines struggle with raw 3D coordinates. describe_environment turns the pocket into a chemistry-aware paragraph you can drop straight into a prompt: it reports the hydrophobic wall, aromatic residues, hydrogen bonds, and salt-bridge / electrostatic contacts from pure-geometry heuristics (no force field).

pocket = mol.select_pocket(ligand="BEN", cutoff=4.5)
print(pocket.describe_environment())
# The binding pocket around ligand BEN (chain A) is lined by 13 residues within
# 4.5 A of the ligand. A hydrophobic pocket wall appears to be formed by VAL213,
# CYS191 and TRP215. Aromatic ring from TRP215 may engage in pi-stacking or
# cation-pi interactions with the ligand. Likely hydrogen bonds are suggested
# between the ligand N1 and O of GLY219 (2.8 A) and the ligand N2 and OG of
# SER190 (3.0 A). A possible salt bridge / electrostatic contact is suggested
# between the ligand N2 and the carboxylate of ASP189 (2.9 A). (Contacts are
# inferred from heavy-atom distances only ... confirm with a profiler such as
# PLIP or ProLIF.)

The interactions are distance-only heuristics: there is no donor/acceptor typing, hydrogen-bond angle criterion, or protonation-state model, so the prose is deliberately phrased as candidates ("likely", "possible"). Treat the output as a first-pass scaffold and confirm with a dedicated interaction profiler such as PLIP or ProLIF for rigorous analysis.

For programmatic use, pocket.environment() returns a PocketEnvironment whose to_dict() gives the structured findings (hydrophobic residues, aromatic residues, hydrogen bonds, salt bridges) with atom names and distances. The same output is available over the MCP server as the describe_environment tool, which returns both the prompt text and the structured features.

The same residue table is available from the command line:

molscope binding-site examples/data/3ptb.pdb --out site.csv --cutoff 4.5

Add --descriptors-out pocket.csv to also write the one-row pocket-basic descriptor table.

When a structure has several ligands, select one by residue name or location:

mol.binding_site(ligand="BEN")
mol.binding_site(ligand=("A", 1))
mol.binding_site(ligand=("A", 100, "A"))  # with insertion code

The CLI accepts the same choices as --ligand BEN, --ligand A:1, or --ligand A:100:A.

A runnable version lives in examples/binding_site.py. See the full guide: Protein analysis.