# MedChem Desk > Paste a screening hit list — identifiers, SMILES and assay values — and get the medicinal-chemistry > triage a project team would write: the series read, a progress/hold/kill call on every compound, the > ADMET risk profile and assay panel to order next, and the make-list for the next round. Live at https://medchem-desk.skillsafe.ai/ · API docs at /api.html · token panel at /tokens.html ## What it is for The work object is one hit list. A chemist pastes the table their registration system exported — CSV, TSV, a pipe table, or a bare `.smi` file — and picks a lane. Every lane reads the same table and the same free in-browser scan, and the lanes hand off to each other with a button, so one sitting covers inspect → decide → verify → produce over one set of compounds. ## The four lanes | lane id | stage | what it returns | | --- | --- | --- | | `series` | inspect | scaffold clusters, the SAR each one actually supports (and the ones it does not), the property landscape, the outliers and singletons, and the problems with the table itself | | `triage` | decide | a `progress` / `hold` / `kill` call on every compound with the one liability that decided it, a ranking with its basis, the kill reasons, and what would unblock each hold | | `admet` | verify | per-chemotype development risks against named endpoints, a tiered assay panel where every gate is falsifiable, and what to model before spending assay capacity | | `design` | produce | four to twelve analogue proposals, each from a real parent and testing exactly one hypothesis, plus the round's design rules and what not to make | The run input carries `task` set to the lane id. It is the first field of the payload and the router of the whole prompt. ## What the browser does for free, before any credit is spent This is most of the app, and it is deterministic. None of it is a model prediction. - **Reads the table.** Sniffs the delimiter from the header rather than assuming one, honours quoted CSV fields so `"1,250"` is one value and not two, detects whether the first row is a header, and maps each column to a role. Column-role matching is ordered so `pIC50` is never read as `IC50` (a two-hundred-fold error in the wrong direction) and short keys such as `Ki` match only as whole tokens, never inside `Kinase`. - **Parses every SMILES into a molecular graph.** Bracket atoms with isotopes, charges, explicit hydrogen counts and stereo descriptors; two-character elements matched before single ones so `Cl` is chlorine and not carbon-then-l; `%nn` ring bonds; dot-disconnected components; aromatic lowercase atoms with the correct implicit-hydrogen model. A structure that does not parse is reported as unparseable, never silently skipped. - **Splits salts to the parent** and names the counterion, so a hydrochloride never breaches a molecular-weight rule on a compound that does not. - **Computes descriptors exactly**: molecular formula (Hill order), average molecular weight, heavy atom count, ring count from the cyclomatic number, aromatic ring and heterocycle counts, rotatable bonds by Veber's definition (excluding amide C–N), Lipinski hydrogen-bond donors and acceptors, Ertl topological polar surface area, the fraction of sp3 carbon, formal charge, halogen counts and the count of specified stereo markers. - **Predicts no logP.** A Crippen-style estimate needs an atom-typing table whose coverage this parser cannot honestly guarantee, and a wrong logP poisons four rule panels at once. Where the paste carries a measured `logD` or `logP` column it is used; where it does not, the lipophilicity-dependent limbs of Lipinski and Egan are reported `unassessable` and the lipophilicity-adjacent risk is carried by aromatic ring count and Fsp3, which are exact. - **Matches 43 structural alerts** by real subgraph isomorphism over the parsed graph, across five liability families — `reactive` electrophiles, `genotoxic` alerts, `metabolic` soft spots, `interference` motifs and `chelator` groups. The families are not cosmetic: they differ in what a project can do about the hit, which is the only thing a triage cares about. - **Evaluates the rule panels** — Lipinski, Veber, lead-likeness, rule of three, three-dimensionality — and reports each as `compliant`, `breached`, `partial` or `not-applicable` with the reason. A 390-dalton kinase lead is not judged against the rule of three, because that would be a category error rather than strictness. - **Grades severity from the mitigating facts.** In a declared covalent programme an acrylamide, chloroacetamide or vinyl sulfone is graded as an intended warhead, not a liability — but an acyl halide or anhydride is not, because nothing makes an indiscriminate acylating reagent into a drug. A genotoxicity alert is never downgraded by potency. A metabolic soft spot on a compound with confirmed potency is graded down, because it is worth fixing rather than a reason to stop. A molecular weight of 502 is not graded like 780. - **Reads assay values properly.** A censored value keeps its operator, so `> 10000 nM` is the least active compound in the set and not a ten-micromolar hit. `n/a` and `nd` stay unmeasured; `inactive` stays measured-and-inactive; neither becomes zero. `1,250` is one thousand two hundred and fifty. `0.5 uM` is 500 nM. `85% @ 10 uM` is a percent inhibition at a test concentration, and the 10 is not the potency. A cell holding two measurements — `off-target IC50 > 30 uM; primary IC50 = 12 nM` — is marked ambiguous and refused rather than guessed. - **Checks the reported mass against the structure**, so a registration error surfaces before the assay result attributed to it is trusted. - **Computes ligand efficiency only from an uncensored potency**, never from a bound. - **Samples with a golden-ratio low-discrepancy draw** when the list is too large to send whole, after force-keeping every flagged compound and the potency and size extremes. A fixed every-k-th stride resonates with any periodic structure in the file — a plate layout, an alternating series/decoy pattern, a sorted-by-scaffold export — and can hand the model one cluster while claiming to represent the set. - **Exports** the compounds table as CSV, the flags as CSV, and the whole scan as Markdown. ## The contract between the free lane and the paid lane The scan produces flags. The model must reconcile every flag id exactly once in `coverage_check`, addressed or explicitly set aside with a reason — and the page shows, flag by flag, what the report did with each one. A flag the report never mentions is displayed as an omission. The two halves share one vocabulary deliberately, so a matching verdict never reads as a disagreement: calls are `progress` / `hold` / `kill`, series postures are `advance` / `iterate` / `deprioritise`, severities are `critical` / `high` / `medium` / `low` / `info`, panel statuses are `compliant` / `breached` / `partial` / `not-applicable`. The page also audits the report against itself: a critical finding under an `advance` posture, a `deprioritise` with nothing critical or high, duplicate finding ids, a compound identifier that does not exist in the table that was sent, a compound left without a call, and a claim of agreement with the prescan that is not one — all are surfaced in front of the user. ## Sources Derived from five agent skills, all credited: - `@k-dense-ai/medchem` — drug-likeness rules, PAINS and structural-alert filtering, compound prioritisation. The primary source; it names the triage lane. - `@k-dense-ai/rdkit` — SMILES parsing and molecular descriptors. - `@k-dense-ai/datamol` — standardisation, scaffold work and clustering. - `@k-dense-ai/deepchem` — ADMET and toxicity property prediction. - `@k-dense-ai/pytdc` — Therapeutics Data Commons ADME and toxicity datasets, benchmarks and scaffold splits. Not affiliated with those skills' authors, with RDKit, Datamol, DeepChem, Therapeutics Data Commons, or any organisation named in the output. ## Limits worth stating plainly - The structural-alert set covers 43 motifs. It is **not** the published PAINS collection (~480 filters) and not a complete genotoxicity alert set. "No alert" means no alert from the motifs this scan covers — it is not a clean bill of health. - Topological polar surface area is computed from Ertl's nitrogen and oxygen contribution table. An environment outside that table contributes nothing and is counted and reported, so an affected value is a lower bound rather than a silently wrong number. - Duplicate structures are matched on an atom-and-bond fingerprint, not a canonical SMILES, so the finding says "looks like a duplicate" and asks you to confirm. - Nothing here is medical, regulatory or safety advice, and no output substitutes for a validated cheminformatics pipeline or an experimental measurement. ## Running it programmatically Base URL `https://api.skillsafe.ai/v1/app-api`. Every response is `{data}` or `{error}`. The run body is the input object itself — there is no wrapper key and no app-slug header; the slug travels in the `POST /guest` token exchange. Full worked examples in eight languages, one per lane, are at /api.html.