mcpField
M
metabolomics-workbench-database

metabolomics-workbench-database

github.com/jaechang-hitsデータ・データベース38470 回インストール1 週間前に更新
リポジトリ

metabolomics-workbench-database について

代謝物、研究、関連注釈を検索する Metabolomics Workbench REST API の利用を案内するスキルです。RefMet による名称標準化や m/z 検索に加え、エンドポイントと応答形式の注意点も扱います。

主な機能

  1. 01化合物識別子によるレコード検索
  2. 02条件を指定した研究の検索
  3. 03RefMet 名への標準化
  4. 04m/z とイオン種による候補検索
  5. 05研究の代謝物表と注釈の取得

活用シーン

  1. 01自由記述名を標準代謝物レコードに対応付け
  2. 02質量分析ピークから化合物候補を検索
  3. 03特定代謝物を検出した研究を特定
  4. 04研究データを取得してローカル分析

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jaechang-hits/sciagent-skills --skill metabolomics-workbench-database

インストール後、Claude Code で/metabolomics-workbench-databaseを入力して読み込まれたか確認します。

詳細

Metabolomics Workbench Database — REST API アクセス

概要

https://www.metabolomicsworkbench.org/rest/ にある Metabolomics Workbench(MW)REST API は、NIH Common Fund の支援を受けて UCSD がホストする 4,200 件以上のメタボロミクス研究を公開しています。URL のパターンは /{context}/{input_item}/{input_value}/{output_item}/{format} です。コンテキストには compound、refmet、moverz、study、analysis、metabolite、gene、protein があります。実際に確認された特記事項は次のとおりです。

  • compound/name/{x} は拒否されます。name は許可された input_item ではありません。pubchem_cid、kegg_id、inchi_key、hmdb_id、regno、lm_id、formula、smiles、abbrev を使用してください。自由形式のテキストを入力する場合は、まず refmet/match/{x} を使用してください。
  • refmet/name/{x}/all には RefMet の完全一致の名前が必要です(例:D-glucose ではなく Glucose)。曖昧検索による正規化には、まず refmet/match/{x} を使用してください。
  • moverz/{REFMET|LIPIDS|MB}/{mz}/{ion}/{tol}/txt はTSV テキストを返します(JSON 形式はありません)。
  • 古いサンプルに記載されている metstat/filter/... エンドポイントは [] を返します。代わりに study/{context}/{value}/summary(または /metabolites)とクライアント側でのフィルタリングを使用してください。

認証は不要です。

使用する場面

  • PubChem CID、KEGG ID、InChIKey、HMDB ID、化学式、SMILES から代謝物レコードを検索する
  • 生物種、疾患、last_name、研究機関、analysis_type、極性から研究を見つける
  • 研究間統合のために、代謝物名を RefMet の命名法に標準化する
  • 付加イオンを考慮したマッチング(moverz)で、MS の m/z 値から未知の化合物を特定する
  • 公開済み研究の実験代謝物テーブル(分析、存在量)を取得する
  • メタボロミクス経路に関連する遺伝子/タンパク質のアノテーションを照会する
  • ローカル分析用に生の mwTab ファイルをダウンロードする
  • NMR/MS スペクトルを含む 220K 件の代謝物 XML をローカルで解析する場合は、代わりに hmdb-database を使用してください
  • 110M 件の化合物のプロパティをリアルタイムで検索する場合は、代わりに pubchem-compound-search を使用してください

前提条件

  • Python パッケージ:requests、pandas
  • API キー不要:一般公開されています
  • レート制限:MW は厳格な制限を設けていません。一括リクエストの間には time.sleep(0.3) を追加してください
  • ベース URL:https://www.metabolomicsworkbench.org/rest
pip install requests pandas

クイックスタート

import requests

BASE = "https://www.metabolomicsworkbench.org/rest"

# Two-step free-text → compound (the API rejects compound/name/...)
def lookup_by_name(name):
    # 1) Normalise to RefMet name
    r = requests.get(f"{BASE}/refmet/match/{name}", timeout=30)
    r.raise_for_status()
    refmet = r.json()
    if not refmet.get("refmet_name"):
        return None
    # 2) Pull full compound record by RefMet name (or by pubchem_cid)
    r2 = requests.get(f"{BASE}/refmet/name/{refmet['refmet_name']}/all", timeout=30)
    rec = r2.json() if r2.json() else {}
    return rec if isinstance(rec, dict) else None

c = lookup_by_name("glucose")
print(f"{c['name']}: formula={c['formula']}, PubChem CID={c['pubchem_cid']}, "
      f"InChIKey={c['inchi_key']}")
# Glucose: formula=C6H12O6, PubChem CID=5793, InChIKey=WQZGKKKJIJFFOK-GASJEMHNSA-N

コア API

モジュール 1:化合物クエリ

compound/{input_item}/{input_value}/all/json — input_item は regno、formula、inchi_key、lm_id、pubchem_cid、hmdb_id、kegg_id、smiles、abbrev のいずれかである必要があります。従来の name 入力はサーバーに拒否されます。

import requests

BASE = "https://www.metabolomicsworkbench.org/rest"

# By PubChem CID
r = requests.get(f"{BASE}/compound/pubchem_cid/5793/all/json", timeout=30)
glucose = r.json()
print(f"PubChem 5793 -> {glucose['name']}, formula={glucose['formula']}, "
      f"HMDB={glucose.get('hmdb_id')}, KEGG={glucose.get('kegg_id')}")

# By KEGG ID
r = requests.get(f"{BASE}/compound/kegg_id/C00031/all/json", timeout=30)
print("KEGG C00031 ->", r.json()["name"])

# By InChIKey
r = requests.get(f"{BASE}/compound/inchi_key/WQZGKKKJIJFFOK-GASJEMHNSA-N/all/json", timeout=30)
print("InChIKey -> regno:", r.json()["regno"])
# Compound by formula returns a paged dict (multiple matches)
import requests
BASE = "https://www.metabolomicsworkbench.org/rest"
r = requests.get(f"{BASE}/compound/formula/C6H12O6/all/json", timeout=30)
matches = r.json()
print(f"Compounds with formula C6H12O6: {len(matches)}")
for k in list(matches)[:3]:
    print(f"  regno={matches[k]['regno']}  name={matches[k]['name']}")
モジュール 2:研究の検索

study/{input_item}/{input_value}/{output} — input_item には study_id、study_title、last_name、institute、analysis_id、metabolite_id、kegg_id、refmet_name が含まれます。output には summary、metabolites、factors、data、available_studies、species、disease が含まれます。study_id に対する summary は辞書を返し(複数の場合はアクセッションをキーとします)、last_name/institute に対してはリストを返します。

import requests, pandas as pd

BASE = "https://www.metabolomicsworkbench.org/rest"

# Single-study summary — `study/study_id/{id}/summary` returns a flat dict
# (keys: study_id, study_title, species, institute, analysis_type, ...)
r = requests.get(f"{BASE}/study/study_id/ST000001/summary", timeout=30)
s = r.json()
print(f"{s['study_id']}: {s['study_title'][:60]}")
print(f"  Species : {s.get('species')}  Institute: {s.get('institute')}")
print(f"  Submit  : {s.get('submission_date')}")
# Studies that detected a metabolite — `study/refmet_name/{x}/summary` returns
# a thin index of (refmet_name, kegg_id, study_id) rows. Chain study_id → full summary
# to get title and species.
import requests, pandas as pd
BASE = "https://www.metabolomicsworkbench.org/rest"
r = requests.get(f"{BASE}/study/refmet_name/Glucose/summary", timeout=60)
d = r.json()
rows = list(d.values()) if isinstance(d, dict) else d
print(f"Studies referencing 'Glucose': {len(rows)}")
print(pd.DataFrame(rows).head(5).to_string(index=False))
# refmet_name kegg_id  study_id
#     Glucose  C00031  ST000001
#     Glucose  C00031  ST000002
#     ...
モジュール 3:RefMet の標準化

refmet/match/{user_text} は曖昧検索による正規化を行い、標準的な RefMet レコードを返します(ただし、pubchem_cid/kegg_id は含まれません)。refmet/name/{exact_refmet_name}/all は ID を含む完全なレコードを返します。これらを 2 段階のパイプラインとして使用してください。

import requests

BASE = "https://www.metabolomicsworkbench.org/rest"

def normalise_to_refmet(user_text):
    r = requests.get(f"{BASE}/refmet/match/{user_text}", timeout=30)
    r.raise_for_status()
    m = r.json()
    if not m or not m.get("refmet_name"):
        return None
    return m["refmet_name"]

def refmet_full(refmet_name):
    r = requests.get(f"{BASE}/refmet/name/{refmet_name}/all", timeout=30)
    r.raise_for_status()
    rec = r.json()
    return rec if isinstance(rec, dict) and rec else None

name = normalise_to_refmet("alpha-D-glucose")  # -> 'Glucose'
print(f"Normalised: {name}")
rec = refmet_full(name)
print(f"  PubChem CID : {rec['pubchem_cid']}")
print(f"  InChIKey    : {rec['inchi_key']}")
print(f"  Super class : {rec['super_class']} / {rec['main_class']} / {rec['sub_class']}")
モジュール 4:研究のフィルタリング(機能しない metstat の代替)

古い metstat/filter/... エンドポイントは [] を返します。代わりに study コンテキストのエンドポイントを使用し、クライアント側でフィルタリングしてください。

import requests, pandas as pd

BASE = "https://www.metabolomicsworkbench.org/rest"

def study_ids_for_metabolite(refmet_name):
    """Return the study_id list that report a given RefMet name."""
    r = requests.get(f"{BASE}/study/refmet_name/{refmet_name}/summary", timeout=60)
    r.raise_for_status()
    d = r.json()
    rows = list(d.values()) if isinstance(d, dict) else d
    return sorted({row["study_id"] for row in rows if row.get("study_id")})

def study_summary(study_id):
    """Pull full summary (title, species, institute, dates) for one study_id.
    Response is a flat dict with keys study_id/study_title/species/institute/..."""
    return requests.get(f"{BASE}/study/study_id/{study_id}/summary", timeout=30).json()

# Find glucose studies, then enrich the first few
ids = study_ids_for_metabolite("Glucose")
print(f"Studies referencing 'Glucose': {len(ids)}")
rows = [study_summary(sid) for sid in ids[:5]]
df = pd.DataFrame(rows)
print(df[["study_id", "study_title", "species"]].head().to_string(index=False))
モジュール 5:m/z 前駆体検索(moverz)

moverz/{REFMET|LIPIDS|MB}/{mz}/{ion}/{tolerance}/txt はタブ区切りテキストを返します(JSON ではありません)。最初の DB セレクター(REFMET、LIPIDS、または MB)は必須です。最初のセグメントに mz を指定すると拒否されます。

import requests, io, pandas as pd

BASE = "https://www.metabolomicsworkbench.org/rest"

def moverz_search(db, mz, ion, tolerance=0.005):
    """Search precursor m/z in REFMET / LIPIDS / MB and return a DataFrame.
    Response is TSV text — no JSON variant."""
    assert db in {"REFMET", "LIPIDS", "MB"}
    r = requests.get(f"{BASE}/moverz/{db}/{mz}/{ion}/{tolerance}/txt", timeout=30)
    r.raise_for_status()
    return pd.read_csv(io.StringIO(r.text), sep="\t")

df = moverz_search("REFMET", 180.063, "M+H", 0.005)
print(f"Candidates at m/z 180.063 [M+H]+ (REFMET): {len(df)}")
print(df.head(5).to_string(index=False))
# Same query against the LIPIDS database
import requests, io, pandas as pd
BASE = "https://www.metabolomicsworkbench.org/rest"
r = requests.get(f"{BASE}/moverz/LIPIDS/760.585/M+H/0.01/txt", timeout=30)
df_lipids = pd.read_csv(io.StringIO(r.text), sep="\t")
print(f"Lipid candidates at m/z 760.585: {len(df_lipids)}")
print(df_lipids.head(3).to_string(index=False))
モジュール 6:遺伝子とタンパク質
import requests

BASE = "https://www.metabolomicsworkbench.org/rest"

r = requests.get(f"{BASE}/gene/gene_symbol/HMGCR/all", timeout=30)
g = r.json()
print(f"{g['gene_symbol']} -> MGP: {g.get('mgp_id')}  ({g.get('gene_name', '')[:60]})")

# Protein by UniProt accession
r2 = requests.get(f"{BASE}/protein/uniprot_id/P04035/all", timeout=30)
p = r2.json()
print(f"UniProt P04035: {p.get('protein_name', '')[:60]}  organism={p.get('organism')}")

主な概念

コンテキストごとの許可される input_item の値
コンテキスト有効な input_item備考
compoundregno、formula、inchi_key、lm_id、pubchem_cid、hmdb_id、kegg_id、smiles、abbrevname は拒否されます。先に refmet/match を使用してください
refmetmatch、name、formula、exactmass、inchi_key、pubchem_cid、regnomatch はあいまい検索です。name には正式な RefMet 名が必要です
studystudy_id、study_title、last_name、institute、analysis_id、metabolite_id、kegg_id、refmet_namestudy_id の summary は accession をキーとする辞書です。last_name/institute の場合はリストです
moverz(パス)REFMET/LIPIDS/MB最初のセグメントは DB であり、mz ではありません
genegene_id、gene_symbol、gene_name、mgp_id辞書を返します
proteinmgp_id、gene_id、uniprot_id、gene_symbol辞書を返します
出力型の規則
  • output=summary は、入力識別子が一意の場合(例:study_id)は辞書、一意でない場合(例:last_name)はリストを返します。
  • study/.../summary に /json を追加すると、レスポンスが TSV に切り替わります。フォーマットのサフィックスは省略してください。JSON がデフォルトです。
  • moverz が出力するのは /txt(TSV)のみです。JSON 形式はありません。
refmet/match と refmet/name の違い
  • refmet/match/{user_text} — あいまい検索です。refmet_name、formula、exactmass、分類を含む単一の辞書を必ず返します。pubchem_cid/inchi_key は含まれません。
  • refmet/name/{exact_refmet_name}/all — 正式な RefMet 名が必要です。ID を含む完全なレコードを返します。名前が正式なものでない場合は空のリストを返します。

一般的なワークフロー

ワークフロー 1:フリーテキストから完全な化合物レコードを取得
import requests, pandas as pd

BASE = "https://www.metabolomicsworkbench.org/rest"

def resolve_to_compound(user_text):
    # 1) Normalise via refmet/match
    rm = requests.get(f"{BASE}/refmet/match/{user_text}", timeout=30).json()
    if not rm.get("refmet_name"):
        return None
    name = rm["refmet_name"]
    # 2) Fetch full RefMet record (includes pubchem_cid / inchi_key)
    full = requests.get(f"{BASE}/refmet/name/{name}/all", timeout=30).json()
    if not isinstance(full, dict) or not full:
        return None
    # 3) Optionally pull the matching compound record via PubChem CID
    cid = full.get("pubchem_cid")
    compound = None
    if cid:
        compound = requests.get(f"{BASE}/compound/pubchem_cid/{cid}/all/json",
                                timeout=30).json()
    return {
        "input": user_text,
        "refmet_name": name,
        "formula": full.get("formula"),
        "pubchem_cid": cid,
        "hmdb_id": compound.get("hmdb_id") if compound else None,
        "kegg_id": compound.get("kegg_id") if compound else None,
        "inchi_key": full.get("inchi_key"),
    }

queries = ["glucose", "alpha-D-glucose", "L-tyrosine", "cholesterol"]
df = pd.DataFrame([resolve_to_compound(q) for q in queries])
print(df.to_string(index=False))
df.to_csv("name_resolution.csv", index=False)
ワークフロー 2:MS ヒットリストにアノテーションを付与

目標:測定した m/z 値のリストを受け取り、RefMet の候補化合物を割り当てます。

import requests, io, pandas as pd, time

BASE = "https://www.metabolomicsworkbench.org/rest"

def annotate_peaks(mz_values, ion="M+H", tolerance=0.005):
    out = []
    for mz in mz_values:
        r = requests.get(f"{BASE}/moverz/REFMET/{mz}/{ion}/{tolerance}/txt", timeout=30)
        if r.status_code != 200 or not r.text.strip():
            time.sleep(0.3); continue
        df = pd.read_csv(io.StringIO(r.text), sep="\t")
        for _, row in df.iterrows():
            out.append({
                "query_mz": mz,
                "matched_mz": row["Matched m/z"],
                "delta": row["Delta"],
                "name": row["Name"],
                "formula": row["Formula"],
                "ion": row["Ion"],
                "main_class": row.get("Main class"),
            })
        time.sleep(0.3)
    return pd.DataFrame(out)

peaks = [180.063, 166.086, 90.055]   # glucose, phenylalanine, alanine
df_ann = annotate_peaks(peaks)
print(df_ann.head(10).to_string(index=False))
df_ann.to_csv("ms_annotations.csv", index=False)
ワークフロー 3:代謝物を検出した研究を検索
import requests, pandas as pd

BASE = "https://www.metabolomicsworkbench.org/rest"

def studies_with(refmet_name, enrich_n=20):
    """Return a DataFrame: study_id rows that report the metabolite, enriched with
    title + species for the first `enrich_n` IDs (via study/study_id/.../summary)."""
    r = requests.get(f"{BASE}/study/refmet_name/{refmet_name}/summary", timeout=60)
    r.raise_for_status()
    d = r.json()
    rows = list(d.values()) if isinstance(d, dict) else d
    ids = sorted({row["study_id"] for row in rows if row.get("study_id")})
    enriched = []
    for sid in ids[:enrich_n]:
        enriched.append(requests.get(
            f"{BASE}/study/study_id/{sid}/summary", timeout=30).json())
    return pd.DataFrame(enriched)

df = studies_with("Glucose", enrich_n=20)
print(f"Glucose-detecting studies (first 20 enriched): {len(df)}")
print(df.groupby("species").size().sort_values(ascending=False).head(8))

主なパラメーター

パラメーターエンドポイントデフォルト範囲/オプション効果
contextパス必須compound、refmet、moverz、study、analysis、metabolite、gene、proteinAPI コンテキストセレクター
input_itemパス必須コンテキストによって異なります(「コンテキストごとの許可される input_item の値」を参照)識別子の種類
input_valueパス必須文字列実際の識別子または値
output_itemパスallall、summary、metabolites、factors、data など返す情報の種類
formatパス(変動)json、txtmoverz が出力するのは txt のみです。study/.../summary に /json を追加しないでください
mz/ion/tolerancemoverz パス必須float/M+H、M-H、M+Na、M+K など/floatDa 単位の質量許容誤差

ベストプラクティス

  1. フリーテキスト入力には、まず refmet/match を使用してください。 compound/name/... はサーバーに拒否されます(name は許可される input_item ではありません)。
  2. moverz は TSV のみです。 pd.read_csv(io.StringIO(r.text), sep="\t") で解析してください。レスポンスに .json() を呼び出してはいけません。
  3. study/.../summary に /json を追加しないでください。 デフォルトは JSON で、サフィックスを付けるとレスポンスが TSV に切り替わります。
  4. refmet/name/{x}/all には正式な RefMet 名が必要です。 ユーザーテキストがある場合は、まず refmet/match に渡してください。
  5. formula でページ分割された化合物の結果は、'1'、'2' などをキーとする形式で返されます。dict.values() で反復処理するか、pd.DataFrame.from_dict(orient="index") に渡してください。
  6. バッチ処理のループでは、必ず time.sleep(0.3) を使用してください。レート制限は公開されていませんが、サーバーは共有されています。

よく使うレシピ

レシピ:データベース間の ID 対応付け(PubChem ↔ KEGG ↔ HMDB)
import requests, pandas as pd

BASE = "https://www.metabolomicsworkbench.org/rest"

def cross_refs(refmet_name):
    rm = requests.get(f"{BASE}/refmet/name/{refmet_name}/all", timeout=30).json()
    if not isinstance(rm, dict) or not rm:
        return None
    cid = rm.get("pubchem_cid")
    if not cid:
        return {"refmet": refmet_name, "pubchem_cid": None}
    c = requests.get(f"{BASE}/compound/pubchem_cid/{cid}/all/json", timeout=30).json()
    return {"refmet": refmet_name,
            "pubchem_cid": cid,
            "kegg_id": c.get("kegg_id"),
            "hmdb_id": c.get("hmdb_id"),
            "inchi_key": rm.get("inchi_key")}

df = pd.DataFrame([cross_refs(n) for n in ["Glucose", "L-Tyrosine", "Cholesterol"]])
print(df.to_string(index=False))
レシピ:研究の代謝物テーブルを取得
import requests, pandas as pd

BASE = "https://www.metabolomicsworkbench.org/rest"
r = requests.get(f"{BASE}/study/study_id/ST000001/metabolites", timeout=60)
r.raise_for_status()
rows = list(r.json().values())
df = pd.DataFrame(rows)
print(f"ST000001 metabolites: {len(df)}")
print(df[["analysis_id", "analysis_summary", "metabolite_name", "refmet_name"]].head(5).to_string(index=False))
レシピ:遺伝子からメタボロミクス経路を検索
import requests

BASE = "https://www.metabolomicsworkbench.org/rest"
g = requests.get(f"{BASE}/gene/gene_symbol/HMGCR/all", timeout=30).json()
print({k: g.get(k) for k in ["gene_symbol", "gene_id", "mgp_id",
                              "gene_name", "gene_synonyms"]})

トラブルシューティング

問題原因解決策
"This input item (name) is not allowed..."compound/name/... が拒否される許可された input_item の値(pubchem_cid、kegg_id、inchi_key、hmdb_id など)を使用してください。自由形式のテキストの場合は、まず refmet/match/{x} を使用してください
moverz のレスポンスで JSONDecodeError が発生するmoverz は JSON ではなく TSV テキストを返すpd.read_csv(io.StringIO(r.text), sep="\t") で解析してください
refmet/name/{x}/all が空のリストを返す正規の RefMet 名が必要です(大文字と小文字を区別します)まず refmet/match/{user_text} で正規化し、refmet_name を refmet/name/.../all に指定してください
metstat/filter/... が [] を返すエンドポイントの構文が機能しない`study/{refmet_name
study/.../summary が JSON ではなく TSV を返す/json サフィックスを付けると TSV に切り替わる末尾の /json を削除してください。デフォルトは JSON です
化学式による化合物のクエリがリストではなく辞書を返すサーバーは複数の一致を {'1': {...}, '2': {...}} の形式でページ分けするdict.values() を反復処理してください(または pd.DataFrame.from_dict(d, orient='index') を使用してください)
refmet/match のレスポンスに pubchem_cid がないmatch は軽量なレコードを返す完全なレコードを取得するには refmet/name/{refmet_name}/all を使用してください

関連スキル

  • hmdb-database — オフラインクエリ用のローカル HMDB XML(220K の代謝物、NMR/MS スペクトル、疾患との関連)
  • pubchem-compound-search — PubChemPy による一般的な化合物特性の検索(1億1,000万件以上の化合物)
  • kegg-database — MW の研究/代謝物ヒットを補完する経路およびオーソロジーデータ
  • chembl-database-bioactivity — 同じ化合物の生物活性データ

参考資料

コメント