mcpField
P
protocolsio-integration

protocolsio-integration

github.com/jaechang-hits生产力38469 次安装1 周前更新
代码仓库

关于 protocolsio-integration

这是面向生命科学研究者和实验室自动化开发者的 protocols.io REST API 使用技能,用于搜索并读取实验方案。它还介绍方案版本、试剂与材料提取,以及需要 OAuth2 的私有内容和编辑操作。

主要功能

  1. 01按关键词、技术或 DOI 搜索方案
  2. 02读取完整步骤与实验材料
  3. 03检索方案版本历史
  4. 04浏览方案分类
  5. 05通过 OAuth2 访问私有方案及编辑

适用场景

  1. 01为实验方法查找可引用的方案
  2. 02提取步骤和试剂以构建自动化流程
  3. 03核对方案版本及对应 DOI
  4. 04查找使用特定试剂或仪器的方案

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jaechang-hits/sciagent-skills --skill protocolsio-integration

安装后在 Claude Code 中输入/protocolsio-integration确认已加载。

详细介绍

protocols.io 集成

概述

protocols.io 是生命科学领域领先的实验方案库,收录了 90,000 多个开放获取实验方案,涵盖分子生物学、细胞生物学、生物信息学、临床研究和实验室自动化。REST API 提供对方案搜索、完整方案获取(步骤、试剂、材料、设备)、方案版本管理、工作区管理以及方案发布的程序化访问。公开方案可免费访问;访问私有方案或创建/编辑方案需要身份验证(OAuth2 令牌)。

适用场景

  • 按关键词、技术或期刊文章 DOI 搜索经过验证的湿实验方案
  • 获取方案的完整分步内容(试剂、时间、用量、备注),用于自动化或分析
  • 查找与特定试剂、试剂盒或仪器相关的方案
  • 通过程序化提取方案步骤和试剂列表,构建实验室自动化工作流
  • 验证方案版本,并为方法部分引用正确的 DOI
  • 寻找社区验证的方案,作为专有方法的替代方案
  • 搭配 opentrons-protocol-api 或 benchling-integration 使用,在自动化工作流中实施下载的方案

先决条件

  • Python 包:requests、pandas
  • 数据要求:方案关键词、DOI 或 protocols.io 方案 ID
  • 环境:需要互联网连接;公开方案:无需身份验证;私有方案:需要从 https://www.protocols.io/developers 获取 OAuth2 令牌
  • 速率限制:公开 API 每秒 10 个请求;访问公开方案时允许未认证请求
pip install requests pandas
# For private protocol access or publishing:
# Register at https://www.protocols.io/developers to obtain an API token

快速入门

import requests

BASE = "https://www.protocols.io/api/v4"
# For public protocols, no token needed (but add for higher rate limits)
HEADERS = {"Authorization": "Bearer YOUR_TOKEN_HERE"}  # Optional for public

# Search for CRISPR protocols
r = requests.get(f"{BASE}/protocols",
                 params={"q": "CRISPR guide RNA design", "order_field": "views",
                         "page_size": 5},
                 headers=HEADERS)
r.raise_for_status()
data = r.json()
print(f"Total CRISPR protocols: {data['pagination']['total_results']}")
for p in data["items"][:3]:
    print(f"\n  {p['title']}")
    print(f"  DOI: {p.get('doi')} | Views: {p.get('stats', {}).get('number_of_views')}")
    print(f"  Authors: {', '.join(a['name'] for a in p.get('creators', [])[:3])}")

核心 API

查询 1:搜索方案

按关键词、技术或全文搜索 protocols.io 公共库。

import requests, pandas as pd

BASE = "https://www.protocols.io/api/v4"

def search_protocols(query, page_size=20, order_field="relevance", category_id=None):
    params = {"q": query, "page_size": page_size, "order_field": order_field}
    if category_id:
        params["filter[categories_ids][]"] = category_id
    r = requests.get(f"{BASE}/protocols", params=params)
    r.raise_for_status()
    return r.json()

data = search_protocols("RNA extraction tissue", page_size=10, order_field="views")
total = data["pagination"]["total_results"]
print(f"RNA extraction protocols: {total}")

rows = []
for p in data["items"][:10]:
    rows.append({
        "id": p.get("id"),
        "title": p.get("title"),
        "doi": p.get("doi"),
        "views": p.get("stats", {}).get("number_of_views", 0),
        "created": p.get("created_on"),
        "category": p.get("categories", [{}])[0].get("name", "n/a"),
    })
df = pd.DataFrame(rows).sort_values("views", ascending=False)
print(df.to_string(index=False))
# Search with category filter (get category IDs from /categories endpoint)
data_pcr = search_protocols("qPCR primer design", order_field="views")
print(f"\nqPCR protocols: {data_pcr['pagination']['total_results']}")
for p in data_pcr["items"][:3]:
    print(f"  {p['title'][:70]} (DOI: {p.get('doi', 'n/a')})")
查询 2:获取完整方案内容

获取包含步骤、试剂、材料和设备的完整方案。

import requests

BASE = "https://www.protocols.io/api/v4"

def get_protocol(protocol_id):
    r = requests.get(f"{BASE}/protocols/{protocol_id}")
    r.raise_for_status()
    return r.json()

# Retrieve protocol by ID (from search results or DOI lookup)
protocol_id = 45979  # Example: a public protocol
data = get_protocol(protocol_id)
protocol = data.get("payload", data)  # Handle API response structure

print(f"Title: {protocol.get('title')}")
print(f"DOI: {protocol.get('doi')}")
print(f"Authors: {', '.join(a['name'] for a in protocol.get('creators', []))}")
print(f"Steps: {len(protocol.get('steps', []))}")
print(f"Materials: {len(protocol.get('materials', []))}")
print(f"Abstract: {protocol.get('description', '')[:200]}")
# Parse protocol steps
protocol_steps = protocol.get("steps", [])
for i, step in enumerate(protocol_steps[:5], 1):
    step_desc = step.get("description", "")
    duration = step.get("duration", {})
    print(f"\nStep {i}: {step_desc[:120]}")
    if duration:
        print(f"  Duration: {duration.get('duration')} {duration.get('unit_label', '')}")
查询 3:按 DOI 获取方案

使用 DOI 获取方案,以便根据引用信息准确检索。

import requests, json

BASE = "https://www.protocols.io/api/v4"

def get_protocol_by_doi(doi):
    """Retrieve protocol using its DOI."""
    # URL-encode the DOI for the query
    r = requests.get(f"{BASE}/protocols",
                     params={"q": doi, "page_size": 5})
    r.raise_for_status()
    items = r.json()["items"]
    for item in items:
        if item.get("doi") == doi:
            return item
    return None

doi = "10.17504/protocols.io.bvb3n2qn"  # Example protocols.io DOI
protocol = get_protocol_by_doi(doi)
if protocol:
    print(f"Found: {protocol['title']}")
    print(f"  ID: {protocol['id']}")
    print(f"  Version: {protocol.get('version_id')}")
查询 4:提取试剂和材料

从已获取的方案中解析材料清单。

import requests, pandas as pd

BASE = "https://www.protocols.io/api/v4"

def get_reagents(protocol_id):
    r = requests.get(f"{BASE}/protocols/{protocol_id}")
    r.raise_for_status()
    data = r.json()
    protocol = data.get("payload", data)
    return protocol.get("materials", [])

# Get reagents list
materials = get_reagents(45979)  # Example protocol ID
print(f"Materials ({len(materials)} items):")
rows = []
for m in materials[:10]:
    rows.append({
        "name": m.get("name"),
        "quantity": m.get("quantity"),
        "unit": m.get("unit", {}).get("name", ""),
        "supplier": m.get("supplier", {}).get("name", ""),
        "catalog": m.get("sku"),
    })
df = pd.DataFrame(rows)
print(df.to_string(index=False))
查询 5:浏览方案类别

列出可用的方案类别,以便进行有针对性的搜索。

import requests, pandas as pd

BASE = "https://www.protocols.io/api/v4"

r = requests.get(f"{BASE}/categories")
r.raise_for_status()
data = r.json()
categories = data.get("items", data.get("payload", []))

print(f"protocols.io categories: {len(categories)}")
df = pd.DataFrame(categories)[["id", "name"]].head(20)
print(df.to_string(index=False))
查询 6:列出方案版本

获取方案版本历史,以跟踪更新。

import requests

BASE = "https://www.protocols.io/api/v4"

def get_protocol_versions(protocol_id):
    r = requests.get(f"{BASE}/protocols/{protocol_id}")
    r.raise_for_status()
    protocol = r.json().get("payload", r.json())
    return {
        "title": protocol.get("title"),
        "version": protocol.get("version_id"),
        "published": protocol.get("published_on"),
        "doi": protocol.get("doi"),
        "parent_doi": protocol.get("parent_publication", {}).get("doi"),
    }

info = get_protocol_versions(45979)
for k, v in info.items():
    print(f"  {k}: {v}")

核心概念

方案 DOI 和版本管理

每个已发布的 protocols.io 方案都有一个可引用的 DOI(格式:10.17504/protocols.io.XXXXX)。方案更新时会创建一个新版本,并分配新的 DOI,而原始 DOI 仍然有效。为确保可重复性,请始终在方法部分引用特定版本的 DOI。

API 身份验证

公开方案无需身份验证即可访问。以下操作需要 OAuth2 Bearer 令牌:访问私有方案、管理工作区、创建/编辑方案以及进行用户专属查询。请通过 https://www.protocols.io/developers 获取令牌。

常见工作流

工作流 1:方案发现与比较

目标:搜索符合某项技术的方案,对其进行比较,并选择最适合改编的方案。

import requests, pandas as pd

BASE = "https://www.protocols.io/api/v4"

def search_and_rank(query, top_n=20):
    """Search protocols and return ranked by views + forks."""
    r = requests.get(f"{BASE}/protocols",
                     params={"q": query, "page_size": top_n, "order_field": "views"})
    r.raise_for_status()
    data = r.json()

    rows = []
    for p in data["items"]:
        stats = p.get("stats", {})
        rows.append({
            "id": p.get("id"),
            "title": p.get("title"),
            "doi": p.get("doi"),
            "views": stats.get("number_of_views", 0),
            "forks": stats.get("number_of_forks", 0),
            "steps": p.get("number_of_steps"),
            "created": p.get("created_on")[:10] if p.get("created_on") else "n/a",
            "category": p.get("categories", [{}])[0].get("name", "n/a"),
        })

    df = pd.DataFrame(rows)
    df["popularity_score"] = df["views"] * 0.7 + df["forks"] * 0.3 * 100
    return df.sort_values("popularity_score", ascending=False)

# Compare western blotting protocols
df = search_and_rank("western blot protein detection", top_n=15)
df.to_csv("western_blot_protocols.csv", index=False)
print("Top western blot protocols:")
print(df[["title", "views", "forks", "steps"]].head(8).to_string(index=False))
工作流 2:为自动化提取方案步骤

目标:提取方案步骤、时间和试剂用量,以用于后续自动化脚本编写。

import requests, pandas as pd

BASE = "https://www.protocols.io/api/v4"

def extract_protocol_steps(protocol_id):
    """Extract structured step data from a protocol."""
    r = requests.get(f"{BASE}/protocols/{protocol_id}")
    r.raise_for_status()
    protocol = r.json().get("payload", r.json())

    steps = []
    for i, step in enumerate(protocol.get("steps", []), 1):
        duration = step.get("duration", {})
        steps.append({
            "step_number": i,
            "description": step.get("description", ""),
            "duration_value": duration.get("duration"),
            "duration_unit": duration.get("unit_label", ""),
            "temperature": step.get("temperature", {}).get("value"),
            "temp_unit": step.get("temperature", {}).get("unit_label", ""),
        })

    materials = [{
        "name": m.get("name"),
        "quantity": m.get("quantity"),
        "unit": m.get("unit", {}).get("name", ""),
    } for m in protocol.get("materials", [])]

    return {
        "title": protocol.get("title"),
        "doi": protocol.get("doi"),
        "steps": pd.DataFrame(steps),
        "materials": pd.DataFrame(materials),
    }

result = extract_protocol_steps(45979)
print(f"Protocol: {result['title']}")
print(f"\nSteps ({len(result['steps'])}):")
print(result["steps"][["step_number", "description", "duration_value", "duration_unit"]].head(5).to_string(index=False))
print(f"\nMaterials ({len(result['materials'])}):")
print(result["materials"].head(5).to_string(index=False))

# Export for automation
result["steps"].to_csv("protocol_steps.csv", index=False)
result["materials"].to_csv("protocol_materials.csv", index=False)

关键参数

参数模块默认值范围/选项作用
q搜索—关键词字符串全文搜索查询
order_field搜索"relevance""relevance"、"views"、"date"、"activity"结果排序方式
page_size搜索101–50每页结果数
page_id搜索1整数分页页码
filter[categories_ids][]搜索—类别整数 ID按方案类别筛选
Protocol ID获取必填整数要获取的特定方案

最佳实践

  1. 按浏览量排序以筛选优质内容:搜索经过充分验证的方案时,使用 order_field=views,因为浏览量高的方案已由许多团队测试过。

  2. 务必引用具体的 DOI:protocols.io 的 DOI 带有版本信息;在方法部分引用确切版本的 DOI(而不只是方案标题),以便读者复现你的确切方案。

  3. 使用前检查许可:所有公开的 protocols.io 方案默认采用 CC-BY 4.0。商业用途需要检查各方案的具体许可。

  4. 提取材料清单以订购试剂:材料 API 会返回目录编号和供应商名称,可据此直接生成采购清单。

  5. 保存方案 ID 和 DOI 以确保可复现性:为项目选择方案时,同时记录整数 ID(用于 API 访问)和 DOI(用于稳定引用)。

常见用法

用法:按试剂名称搜索

适用场景:查找使用特定商业试剂盒或试剂的方案。

import requests

r = requests.get("https://www.protocols.io/api/v4/protocols",
                 params={"q": "RNeasy Mini Kit RNA extraction", "page_size": 5,
                         "order_field": "views"})
data = r.json()
print(f"Protocols using RNeasy: {data['pagination']['total_results']}")
for p in data["items"][:3]:
    print(f"  {p['title'][:70]} ({p.get('doi', 'n/a')})")
用法:获取方法部分所需的方案引文

适用场景:为方法部分生成引文。

import requests

protocol_id = 45979
r = requests.get(f"https://www.protocols.io/api/v4/protocols/{protocol_id}")
p = r.json().get("payload", r.json())
authors = "; ".join(a["name"] for a in p.get("creators", [])[:3])
print(f"Citation: {authors} ({p.get('created_on', '')[:4]}). ")
print(f"{p.get('title')}. protocols.io. https://doi.org/{p.get('doi')}")
用法:查找分叉数最多的方案

适用场景:找出广泛改编的方案(分叉数越高,表示改编的实验室越多)。

import requests, pandas as pd

r = requests.get("https://www.protocols.io/api/v4/protocols",
                 params={"q": "ChIP-seq chromatin", "page_size": 20})
data = r.json()
df = pd.DataFrame([{
    "title": p["title"][:60],
    "forks": p.get("stats", {}).get("number_of_forks", 0),
    "views": p.get("stats", {}).get("number_of_views", 0),
} for p in data["items"]])
print(df.sort_values("forks", ascending=False).head(5).to_string(index=False))

故障排除

问题原因解决方案
搜索结果中的 items 为空查询过于具体或没有匹配项扩大查询范围;移除特殊字符
访问方案时出现 HTTP 401未经身份验证的私有方案获取 OAuth2 令牌;访问公开方案不需要身份验证
方案步骤的描述为空方案使用了富文本格式使用 re.sub(r'<[^>]+>', '', text) 从 description 中移除 HTML 标签
materials 列表为空方案没有结构化材料信息材料可能以纯文本形式写在步骤描述中
DOI 查询返回了错误的方案匹配了相似标题,而不是 DOI精确比较 DOI 字段;使用字符串相等检查 if item.get("doi") == doi
触发速率限制错误每秒请求超过 10 次在请求之间添加 time.sleep(0.15)

相关技能

  • opentrons-protocol-api — 使用通过此技能提取的步骤,在 Opentrons 液体处理机器人上执行方案
  • benchling-integration — 在 Benchling ELN 中存储检索到的方案并追踪试剂
  • scientific-manuscript-writing — 在方法部分使用 protocols.io DOI 正确引用方案

参考资料

评论

支持 Markdown